:root {
  /* Thème bleu nuit + accent cyan lisible sur fond sombre */
  --bg: #0a1020; --card: #111a30; --card2: #18223c; --line: #263352;
  --fg: #e8edf7; --muted: #8b9bc0; --accent: #38bdf8; --accent-ink: #06283d;
  --long: #34d17a; --short: #f26d6d;
  --radius: 10px; --shadow: 0 2px 12px rgba(0,0,0,.35);
  --t: .18s ease;
  /* ÉCHELLE DE TEXTES (ergonomie, phase 1 — 25/09/2026). Avant : 19 tailles
     de police différentes dans cette seule feuille (.58, .6, .62, .64, .66,
     .68, .7, .72, .74, .75, .76, .78, .8, .82, .84, .85, .86, .88, .9…) —
     aucune n'était une décision, chacune était un ajustement local. Cinq
     crans, et RIEN d'autre : xs (étiquettes, aides), sm (le second plan :
     muted, boutons, tableaux), md (le texte courant = 15 px), lg (h2, titres
     de tiroir), xl (le h1). Les CARTES gardent leurs tailles en `cqw` : elles
     se mettent à l'échelle de leur boîte, pas de la page. */
  --t-xs: 12px; --t-sm: 13px; --t-md: 15px; --t-lg: 18px; --t-xl: 22px;
  /* Cible tactile minimale : 40 px (Apple dit 44, Material 48 ; à 40 on tient
     encore quatre boutons sur une ligne de téléphone). Appliquée en dessous de
     720 px seulement — sur ordinateur, 36 px suffisent et les tableaux
     respirent. */
  --tap: 36px;
}
* { box-sizing: border-box; }
/* `hidden` GAGNE TOUJOURS (phase 3, 25/09/2026). L'attribut ne vaut qu'un
   `display: none` de feuille navigateur, que n'importe quel `display` d'auteur
   écrase : le bouton « Voir » des sélecteurs (que app.js cache dès que le
   changement soumet tout seul) restait visible — depuis toujours. */
[hidden] { display: none !important; }
html { scroll-behavior: smooth; }
body { margin: 0; font: 15px/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  background: radial-gradient(1200px 620px at 72% -12%, #16264f 0%, var(--bg) 62%) fixed;
  color: var(--fg); -webkit-font-smoothing: antialiased; }
a { color: var(--accent); text-decoration: none; transition: color var(--t), opacity var(--t); }
a:hover { text-decoration: underline; }

/* z-index 40 : au-dessus d'une carte du Journal zoomée (30) — avant, la carte
   agrandie glissait PAR-DESSUS la barre pendant le défilement (recalibrage 20/08). */
.topbar { position: sticky; top: 0; z-index: 40; display: flex; gap: 1.2rem; align-items: center;
  padding: .7rem 1.2rem; background: rgba(15,23,44,.82); backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line); }
.topbar .brand { font-weight: 800; font-size: var(--t-lg); color: var(--accent); letter-spacing: .3px; }
.topbar nav { display: flex; gap: .3rem; flex-wrap: wrap; }
.topbar nav a { color: var(--muted); padding: .35rem .7rem; border-radius: 8px;
  transition: color var(--t), background var(--t); }
.topbar nav a:hover { color: var(--fg); background: var(--card2); text-decoration: none; }
.topbar nav a.active { color: var(--fg); background: var(--card2);
  box-shadow: inset 0 -2px 0 var(--accent); }
.topbar .inline { margin-left: auto; display: flex; align-items: center; gap: .55rem; }
/* Sélecteur FR / EN (chantier i18n, 24/09) : poussé à droite ; la
   déconnexion le suit sans reprendre la marge automatique. */
.topbar .langues { gap: .1rem; }
.topbar .langues + .inline { margin-left: .5rem; }
.topbar .langues .link { font-size: var(--t-sm); font-weight: 600; padding: .15rem .3rem;
  color: var(--muted); }
.topbar .langues .link.actif { color: var(--fg); text-decoration: underline;
  text-underline-offset: 3px; }
/* LE COMPTE PILOTÉ (23/09/2026) — discret mais toujours là : depuis qu'il y a
   plusieurs comptes, « sur lequel suis-je ? » se pose avant chaque action. */
.topbar .qui { color: var(--muted); font-size: var(--t-sm); font-weight: 600;
  padding: .2rem .5rem; border: 1px solid var(--line); border-radius: 999px;
  max-width: 11ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.link { background: none; border: none; color: var(--muted); cursor: pointer; transition: color var(--t); }
.link:hover { color: var(--fg); }
.live-badge { display: inline-flex; align-items: center; gap: .35rem; margin-left: auto;
  color: var(--muted); font-size: var(--t-sm); }
.live-badge + .inline { margin-left: 1rem; }
#live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--long); display: inline-block; }
#live-dot.pulse { animation: pulse .9s ease; }
@keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(46,160,67,.6); } 100% { box-shadow: 0 0 0 8px rgba(46,160,67,0); } }

main { max-width: 1040px; margin: 1.5rem auto; padding: 0 1rem; animation: fadein .25s ease; }
@keyframes fadein { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
h1 { font-size: var(--t-xl); margin: .2rem 0 .9rem; line-height: 1.2; }
h2 { font-size: var(--t-lg); }
.muted { color: var(--muted); } .error { color: var(--short); }
.warn { color: var(--accent); }   /* .banner : voir plus bas, règle unique */

/* EN-TÊTE DE PAGE UNIQUE (ergonomie, phase 1). Chaque page avait le sien :
   h1 nu ici, h1 + lien « vue tableau » dedans là, un bouton collé au titre au
   Journal, un sous-titre entre parenthèses aux Positions. Désormais UN gabarit :
   le titre court à gauche (+ un compteur), les actions à DROITE dans
   `.page-actions`, et le sous-titre — s'il en faut un — en dessous, en
   `.page-sub`. Sur téléphone, les actions passent sous le titre, alignées à
   gauche, sans jamais le couper. */
.head-row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: .4rem 1rem; margin: 0 0 .9rem; }
.head-row h1 { margin: 0; display: flex; align-items: center; flex-wrap: wrap; gap: .3rem .55rem; }
.head-row h1 .muted { font-size: var(--t-md); font-weight: 500; }
.page-actions { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; margin-left: auto; }
.page-sub { margin: -.5rem 0 1rem; color: var(--muted); font-size: var(--t-sm); }
/* Utilitaires de taille (remplacent les `style="font-size:…"` des gabarits). */
.sm { font-size: var(--t-sm); } .xs { font-size: var(--t-xs); }
.filters { display: flex; flex-wrap: wrap; align-items: flex-end; gap: .8rem;
  background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  padding: .8rem 1rem; margin: 1rem 0; }
.filters label { margin: 0; display: flex; flex-direction: column; gap: .2rem;
  font-size: var(--t-sm); color: var(--muted); }
.filters select { width: auto; min-width: 120px; }
.filters .btn { padding-left: .9rem; padding-right: .9rem; }

table.grid { width: 100%; border-collapse: collapse; background: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
  box-shadow: var(--shadow); }
table.grid th, table.grid td { padding: .55rem .7rem; border-bottom: 1px solid var(--line);
  text-align: left; }
table.grid tbody tr:last-child td { border-bottom: none; }
/* En-tête NON sticky : le sticky (top:52px, sous un topbar de 57px) recouvrait la
   1re ligne des tables autonomes (Stats, Historique, Positions) au défilement. */
table.grid thead th { background: var(--card2); }
table.grid th { color: var(--muted); font-weight: 600; font-size: var(--t-sm);
  text-transform: uppercase; letter-spacing: .3px; }
table.grid tbody tr { transition: background var(--t); }
table.grid tbody tr:hover { background: rgba(255,255,255,.03); }
/* 700 et non 600 (08/09) : posée sur un <b> (déjà 700), la classe AMINCISSAIT
   le gras — les DMG de l'historique et les cumuls se lisaient plus maigres
   qu'un texte ordinaire en gras. */
.dir-long { color: var(--long); font-weight: 700; }
.dir-short { color: var(--short); font-weight: 700; }
td.live-price { font-weight: 700; font-variant-numeric: tabular-nums; color: var(--fg); }
td.inzone { color: var(--accent); font-weight: 700;
  box-shadow: inset 3px 0 0 var(--accent); }

/* Jauge de progression entrée → TP3 */
td.gauge-cell { white-space: nowrap; }
.gauge { position: relative; display: inline-block; width: 120px; height: 9px;
  background: #0a1122; border: 1px solid var(--line); border-radius: 5px;
  overflow: hidden; vertical-align: middle; }
.gauge-fill { position: absolute; left: 0; top: 0; height: 100%; border-radius: 5px;
  transition: width .4s ease; min-width: 2px; }
.dir-bg-long { background: linear-gradient(90deg, #1f7a35, var(--long)); }
.dir-bg-short { background: linear-gradient(90deg, #a83a34, var(--short)); }
.gauge-tick { position: absolute; top: 0; width: 2px; height: 100%;
  background: rgba(255,255,255,.35); }
.gauge-pct { margin-left: .5rem; font-size: var(--t-sm); color: var(--muted);
  font-variant-numeric: tabular-nums; }
.status-error { color: var(--short); } .status-open { color: var(--long); }

/* TP franchis + badge d'état actionnable */
.tp-list { white-space: nowrap; font-variant-numeric: tabular-nums; }
.tp-mark { color: var(--muted); }
.tp-mark.hit { color: var(--long); font-weight: 700; }
.state { display: inline-block; padding: .12rem .5rem; border-radius: 10px;
  font-size: var(--t-xs); font-weight: 600; white-space: nowrap; border: 1px solid var(--line); }
.state-waiting { color: var(--muted); }
.state-fresh { color: #7ee08a; border-color: #2f6b39; background: rgba(46,120,60,.15); }
.state-progressing { color: var(--accent); border-color: #24506b; background: rgba(30,80,120,.15); }
.state-near_tp { color: #6fd07f; border-color: #2f6b39; background: rgba(46,120,60,.22); }
.state-exhausted { color: #e0b24a; border-color: #7a5a1f; background: rgba(140,100,30,.18); }
.state-invalidated { color: var(--short); border-color: #7a2f2f; background: rgba(140,50,50,.18); }
.state-new { color: #ffd466; border-color: #7a6320; background: rgba(160,130,30,.22); }
.row-new > td { box-shadow: inset 0 1px 0 rgba(255,212,102,.25); }
.gain-max { margin-left: .35rem; font-size: var(--t-sm); color: var(--long); font-variant-numeric: tabular-nums; }
.risk-tag { margin-left: .4rem; font-size: var(--t-xs); color: #e0b24a; font-variant-numeric: tabular-nums; }
.mkt-badge { display: inline-block; padding: .05rem .4rem; border-radius: 6px; font-size: var(--t-xs);
  font-weight: 600; border: 1px solid var(--line); vertical-align: middle; margin: 0 .2rem; }
.mkt-futures { color: #7fb3ff; border-color: #2a4a6b; background: rgba(40,80,130,.18); }
.mkt-spot { color: #9be08a; border-color: #3a6b2f; background: rgba(60,120,46,.18); }

/* Groupes dépliables par paire (Historique) — natif <details>, sans JS */
.pair-group { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  margin: .6rem 0; box-shadow: var(--shadow); overflow: hidden; }
.pair-group > summary { cursor: pointer; padding: .7rem 1rem; list-style: none; display: flex;
  align-items: center; gap: .6rem; flex-wrap: wrap; user-select: none; transition: background var(--t); }
.pair-group > summary:hover { background: var(--card2); }
.pair-group > summary::-webkit-details-marker { display: none; }
.pair-group > summary::before { content: "\25B8"; color: var(--muted); transition: transform var(--t); }
.pair-group[open] > summary::before { transform: rotate(90deg); }
.pair-group > summary strong { font-size: var(--t-lg); }
.grp-tags { display: inline-flex; gap: .3rem; flex-wrap: wrap; margin-left: auto; }
.grp-tags .state { font-size: var(--t-xs); }
.pair-group table.grid { border: none; border-radius: 0; box-shadow: none; margin: 0; }

.pager { display: flex; align-items: center; gap: .8rem; margin: .8rem 0; }

/* Positions live (obj 1) : badge « live » + actions inline TP/SL/Close par ligne */
.live-tag { display: inline-block; font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .4px;
  color: #6ee7a8; border: 1px solid #2f6b46; background: rgba(46,120,70,.18);
  border-radius: 4px; padding: 0 .3rem; vertical-align: middle; margin-left: .25rem; }
.pos-actions { white-space: nowrap; }
.pos-actions .act-form { display: inline-flex; align-items: center; gap: .25rem; margin: .12rem .25rem .12rem 0; }
.pos-actions .act-form input[type=text] { width: 5.5rem; padding: .25rem .35rem; font-size: var(--t-sm); }
.pos-actions .act-form .btn { padding: .25rem .5rem; font-size: var(--t-sm); }

/* Formulaire d'un bot : les champs courts se rangent CÔTE À CÔTE plutôt que
   empilés. Le formulaire faisait six blocs et près de deux écrans de défilement —
   or on ne relit pas ce qu'on ne voit pas d'un seul regard, et c'est en ne
   relisant pas qu'on laisse une case mal cochée. `flex-wrap` fait retomber les
   champs en colonne sur petit écran, sans media query. */
.f-row { display: flex; flex-wrap: wrap; gap: .5rem 1rem; align-items: flex-end; }
.f-row > label { flex: 1 1 12rem; margin: 0; }
.f-row > label.narrow { flex: 0 1 8rem; }
/* Une phrase par section, pas un paragraphe : le détail vit dans le mode d'emploi. */
fieldset > .hint { margin: -.2rem 0 .7rem; font-size: var(--t-sm); }

/* Réglage du destinataire Telegram (onglet Agents). */
.tg-ligne { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem;
  margin-top: .5rem; }
.tg-reglage { margin-top: .6rem; padding-top: .6rem; border-top: 1px solid var(--line); }
.tg-reglage > summary { cursor: pointer; font-size: var(--t-sm); color: var(--muted); }
.tg-reglage p { margin: 0 0 .5rem; font-size: var(--t-sm); }
/* Les deux étapes, dans l'ordre où on doit les faire — la première n'est pas
   contournable : Telegram refuse qu'un bot écrive à qui ne lui a jamais parlé. */
.tg-etapes { margin: .6rem 0 .4rem; padding-left: 1.2rem; font-size: var(--t-sm); }
.tg-etapes li { margin: .35rem 0; }
.tg-etapes .muted { display: block; font-size: var(--t-sm); margin-top: .15rem; }
.tg-reglage .f-row { align-items: flex-end; }
.tg-reglage .f-row label { flex: 0 1 16rem; }

/* De quoi une position est faite : une ligne par carte, plus la part que la base
   n'explique pas. L'exchange n'expose qu'UNE position par paire — ce bloc rend le
   détail à qui le connaît, et surtout AVOUE ce qu'il ne connaît pas. */
.pos-parts { margin: .35rem 0 .1rem; display: flex; flex-direction: column; gap: .12rem; }
.pos-part { display: flex; align-items: baseline; gap: .4rem; font-size: var(--t-xs);
  color: var(--muted); }
.pos-part .k { flex: 1 1 auto; }
.pos-part b { font-weight: 600; color: var(--fg, inherit); }
/* La part inexpliquée se voit : c'est de l'argent engagé que rien ne pilote. */
.pos-part-inconnu { color: #f0a35e; }
.pos-part-inconnu b, .pos-part-inconnu span { color: #f0a35e; }

/* TP partiels : % de clôture par TP (formulaire de prise de position, et
   répartition E3 d'un bot — même présentation, même unité). */
.tp-splits { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem;
  margin: .2rem 0 .4rem 1.6rem; }
.tp-splits .split-in { display: inline-flex; align-items: center; gap: .35rem; font-size: var(--t-sm); }
.tp-splits .split-in input { width: 4rem; }
.tp-splits .split-hint { width: 100%; font-size: var(--t-sm); }

/* Menu d'actions par ligne (⋯) — natif <details>, sans JS. Expansion en flux
   (pas d'absolu) pour ne pas être rognée par l'overflow:hidden de la table. */
.row-menu { display: inline-block; }
.row-menu > summary { cursor: pointer; list-style: none; user-select: none;
  padding: .1rem .5rem; border: 1px solid var(--line); border-radius: 6px;
  background: var(--card2); font-weight: 700; letter-spacing: 1px; line-height: 1.2; }
.row-menu > summary::-webkit-details-marker { display: none; }
.row-menu > summary::marker { content: ""; }
.row-menu > summary:hover { background: var(--line); }
.row-menu-items { display: flex; flex-direction: column; gap: .12rem; margin-top: .3rem;
  min-width: 108px; background: var(--card2); border: 1px solid var(--line);
  border-radius: 8px; padding: .25rem; box-shadow: var(--shadow); }
.row-menu-items a, .row-menu-items button { display: block; width: 100%; text-align: left;
  padding: .35rem .55rem; font-size: var(--t-sm); border-radius: 6px; background: none;
  border: none; color: var(--fg); cursor: pointer; text-decoration: none; }
.row-menu-items a:hover, .row-menu-items button:hover { background: var(--line); }
.row-menu-items .menu-danger { color: var(--short); }

/* Watchlist : barre de seuil + case à cocher par paire */
.wl-bar { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1rem;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: .6rem .9rem; margin: .6rem 0; box-shadow: var(--shadow); }
.wl-threshold { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin: 0; }
.wl-threshold label { display: inline-flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.wl-threshold input[type=number] { width: 4.5rem; }
.wl-note { font-size: var(--t-sm); }
.wl-form { display: inline; margin: 0; }
.wl-toggle { background: none; border: none; cursor: pointer; font-size: 1.15rem;
  line-height: 1; padding: 0 .1rem; color: var(--muted); vertical-align: middle; }
.wl-toggle:hover { color: var(--fg); }
.wl-toggle.on { color: var(--accent, #5aa0ff); }

/* Action « + Journal » (enregistrer un signal) — mise en avant */
.btn-save { background: var(--accent-bg, rgba(90,160,255,.16)); color: var(--accent);
  border: 1px solid var(--accent, #5aa0ff); font-weight: 700; }
.btn-save:hover { background: var(--accent, #5aa0ff); color: #06101f; }

/* Journal : barre d'onglets (SSR, liens — fonctionne sans JS) */
.tabs { display: flex; flex-wrap: wrap; gap: .3rem; margin: .6rem 0 1rem;
  border-bottom: 1px solid var(--line); }
.tabs .tab { padding: .5rem .9rem; text-decoration: none; color: var(--muted);
  border: 1px solid transparent; border-bottom: none; border-radius: 8px 8px 0 0;
  font-weight: 600; transition: background var(--t), color var(--t); margin-bottom: -1px; }
.tabs .tab:hover { color: var(--fg); background: var(--card2); }
.tabs .tab.active { color: var(--fg); background: var(--card); border-color: var(--line);
  border-bottom: 1px solid var(--card); }
.tabs .tab-count { display: inline-block; min-width: 1.2rem; text-align: center;
  font-size: var(--t-xs); padding: 0 .35rem; margin-left: .2rem; border-radius: 10px;
  background: var(--card2); color: var(--muted); border: 1px solid var(--line); }
.tabs .tab.active .tab-count { background: var(--accent-bg, rgba(90,160,255,.15)); color: var(--accent); }

/* « Mes signaux » : 3 sections de cycle de vie (attente / activés / terminés) */
.lifecycle-section { margin: 1.4rem 0; }
.lifecycle-section .sec-head { margin: 0 0 .1rem; font-size: var(--t-lg); }
.lifecycle-section .sec-hint { margin: 0 0 .5rem; font-size: var(--t-sm); }
.lifecycle-section .sec-empty { margin: .3rem 0 0; font-size: var(--t-sm); opacity: .8; }

/* Cartes récap PnL (/trades) */
.pnl-cards { display: flex; flex-wrap: wrap; gap: .8rem; margin: 1rem 0; }
.pnl-card { background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  padding: .7rem 1rem; min-width: 140px; display: flex; flex-direction: column; gap: .25rem; }
.pnl-card strong { font-size: var(--t-lg); font-variant-numeric: tabular-nums; }

/* Bandeau de commandement (page Signaux) : compte + soldes + résumé positions */
.acct-bar { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: .8rem 1rem; margin: 1rem 0; }
.acct-form { display: flex; align-items: flex-end; gap: .8rem; flex-wrap: wrap; margin-bottom: .8rem;
  padding-bottom: .8rem; border-bottom: 1px solid var(--line); }
.acct-form label { margin: 0; display: flex; flex-direction: column; gap: .2rem; font-size: var(--t-sm); color: var(--muted); }
.acct-form select { width: auto; min-width: 220px; }
.acct-metrics { display: flex; flex-wrap: wrap; gap: 1.6rem; }
.acct-metrics .metric { display: flex; flex-direction: column; gap: .15rem; }
.acct-metrics .metric strong { font-size: var(--t-lg); font-variant-numeric: tabular-nums; }
.acct-note { font-size: var(--t-sm); margin: .6rem 0 0; }

/* Compteur de complétude des signaux (bug 1) : reçus → retenus → base → affichés */
.signal-count { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .55rem;
  margin: .5rem 0 .2rem; font-size: var(--t-sm); color: var(--muted);
  font-variant-numeric: tabular-nums; }
.signal-count .sc-item strong { color: var(--fg); }
.signal-count .sc-sep { color: var(--line); font-weight: 700; }
.signal-count .sc-tf { font-size: var(--t-sm); }
.signal-count .sc-drop { color: var(--short); border: 1px solid #7a2f2f;
  background: rgba(140,50,50,.15); border-radius: 8px; padding: 0 .45rem; font-weight: 600; }

/* Barre d'actualisation (bouton manuel + dernière maj) */
.refresh-row { display: flex; align-items: center; gap: .8rem; flex-wrap: wrap; margin: .2rem 0 1rem; }
.refresh-row form { margin: 0; }
.refresh-row .btn { padding-left: .8rem; padding-right: .8rem; }

/* Sortie IA (markdown rendu en texte préformaté, lisible) */
.ai-out { white-space: pre-wrap; line-height: 1.5; font-size: var(--t-md); }
button.btn:disabled { opacity: .5; cursor: not-allowed; }

.card { background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  padding: 1.2rem; margin: 1rem 0; }
.card.narrow { max-width: 380px; margin: 3rem auto; }
label { display: block; margin: .7rem 0; }
label.check { display: flex; gap: .4rem; align-items: center; }
/* Champs : même police que la page (le navigateur impose sinon 13.33 px et
   sa propre famille), même hauteur que les boutons. */
input, select, textarea { font: inherit; font-size: var(--t-md); }
input:not([type=checkbox]):not([type=radio]):not([type=hidden]), select { min-height: var(--tap); }
input, select { width: 100%; padding: .5rem; margin-top: .2rem; background: var(--bg);
  border: 1px solid var(--line); border-radius: 6px; color: var(--fg); }
label.check input { width: auto; }

/* BOUTONS (ergonomie, phase 1) : TROIS types, et rien d'autre — principal
   (cyan : l'action attendue de la page), secondaire (bordé : navigation,
   alternatives), danger (rouge : fermer, annuler, supprimer). Une SEULE
   hauteur (`--tap`), une seule taille de texte : les dix variantes locales
   (.refresh-row, .row-actions, .jrn-actions, .filters…) ne changent plus que
   la marge horizontale. Un bouton de 26 px de haut dans un tableau était
   injouable au pouce ; à 40 px sur téléphone, il l'est. */
button, .btn { display: inline-flex; align-items: center; justify-content: center; gap: .35rem;
  min-height: var(--tap); padding: .35rem .95rem; border-radius: 8px; border: 1px solid transparent;
  font: inherit; font-size: var(--t-sm); font-weight: 700; line-height: 1.2; text-align: center;
  background: var(--accent); color: var(--accent-ink); cursor: pointer; vertical-align: middle;
  transition: transform var(--t), box-shadow var(--t), background var(--t), opacity var(--t),
    border-color var(--t); }
button:hover, .btn:hover { box-shadow: var(--shadow); text-decoration: none; opacity: .95; }
button:active, .btn:active { transform: translateY(1px); }
.btn.secondary { background: var(--card2); color: var(--fg); border-color: var(--line); }
.btn.secondary:hover { border-color: var(--accent); }
.btn.danger { background: var(--short); color: #fff; }
/* Les boutons « texte » de la barre (FR/EN, Déconnexion) ne sont pas des
   boutons de page : pas de hauteur imposée, pas de fond. */
button.link { display: inline; min-height: 0; padding: .15rem .3rem; font-weight: 600;
  background: none; border: 0; }
button.link:hover, button.link:active { box-shadow: none; transform: none; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
input:focus, select:focus { outline: none; border-color: var(--accent); }
.actions { display: flex; gap: .8rem; margin-top: 1rem; }
.row-actions { display: flex; gap: .4rem; flex-wrap: wrap; }
.row-actions .btn { padding-left: .7rem; padding-right: .7rem; }
dl.recap { display: grid; grid-template-columns: auto 1fr; gap: .3rem 1rem; }
dl.recap dt { color: var(--muted); }
fieldset.accounts { border: 1px solid var(--line); border-radius: 8px; padding: .6rem 1rem 1rem; margin: 1rem 0; }
fieldset.accounts legend { color: var(--muted); font-size: var(--t-sm); padding: 0 .4rem; }

/* ==== Formulaire d'ordre : segments/toggles (mapping 1:1 place-order), sans JS ==== */
fieldset.of { border: 1px solid var(--line); border-radius: 8px; padding: .7rem 1rem 1rem; margin: 1rem 0; }
fieldset.of legend { color: var(--muted); font-size: var(--t-sm); padding: 0 .4rem;
  text-transform: uppercase; letter-spacing: .3px; }
.of-map { margin: .5rem 0 0; font-size: var(--t-sm); color: var(--muted); }
.of-map code { color: var(--accent); background: rgba(56,189,248,.1); padding: 0 .3rem; border-radius: 4px; }
.of-grid { display: flex; flex-wrap: wrap; gap: .8rem; }
.of-grid label { flex: 1 1 200px; margin: 0; font-size: var(--t-sm); color: var(--muted); }

/* Segment : radios rendus en boutons collés (natif, sans JS) */
.seg { position: relative; display: inline-flex; flex-wrap: wrap; border: 1px solid var(--line);
  border-radius: 8px; overflow: hidden; background: var(--bg); }
.seg input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.seg label { display: inline-flex; flex-direction: column; align-items: flex-start; gap: .1rem;
  padding: .45rem .9rem; margin: 0; cursor: pointer; color: var(--muted); font-weight: 600;
  font-size: var(--t-md); user-select: none; border-right: 1px solid var(--line);
  transition: background var(--t), color var(--t); }
.seg label:last-of-type { border-right: none; }
.seg label:hover { color: var(--fg); background: var(--card2); }
.seg input:checked + label { background: var(--accent); color: var(--accent-ink); }
.seg input:focus-visible + label { outline: 2px solid var(--accent); outline-offset: -2px; }
.seg-sub { font-size: var(--t-xs); font-weight: 500; opacity: .8; }

/* Sous-bloc TP (partiel + custom) — TOUJOURS visible (pas de contenu masqué sans JS) */
.of-sub { margin-top: .7rem; padding: .6rem .7rem; background: var(--bg);
  border: 1px dashed var(--line); border-radius: 8px; }
.of-sub-h { display: block; font-size: var(--t-sm); color: var(--muted); margin-bottom: .3rem; }
.of-custom { display: block; margin: .5rem 0 0; font-size: var(--t-sm); color: var(--muted); }

/* Chips comptes (checkbox rendus en pastilles) */
.chips { position: relative; display: flex; flex-wrap: wrap; gap: .5rem; }
.chips input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.chip { display: inline-flex; align-items: center; gap: .35rem; padding: .4rem .8rem; margin: 0;
  border: 1px solid var(--line); border-radius: 20px; cursor: pointer; font-size: var(--t-sm);
  font-weight: 600; color: var(--muted); background: var(--bg);
  transition: background var(--t), color var(--t), border-color var(--t); }
.chip::before { content: "\25CB"; font-size: var(--t-md); }
.chip:hover { color: var(--fg); border-color: #33415a; }
.chips input:checked + .chip { color: var(--accent); border-color: var(--accent);
  background: rgba(56,189,248,.12); }
.chips input:checked + .chip::before { content: "\2713"; }
.chips input:focus-visible + .chip { outline: 2px solid var(--accent); outline-offset: 2px; }

/* LE POINT DE LA CARTE DIT « PRISE OU PAS » (Djamel, 09/09/2026).
   « Faut mettre vert quand c'est pris et rouge quand c'est pas pris quand même
   c'est mieux nan ? […] comme ça on sait quelle carte est prise », et « juste
   un peu plus gros parce que maintenant y a la jauge de partout » : 7 → 10 px.

   IL REMPLACE `.live-dot-i`, qui disait tout autre chose — la FRAÎCHEUR du prix
   (vert = relevé sur l'exchange, gris = estimation). Personne ne le savait :
   Djamel a demandé « il sert à quelque chose ? » et a conclu « ah, c'est vert
   quand c'est activé » — faux. Un témoin que personne ne sait lire ne protège
   personne. Mais ce qu'il DISAIT compte : un prix périmé qui se lit comme un
   prix frais est exactement le genre de mensonge muet qu'on traque. Il n'est
   donc pas supprimé, il a déménagé sur le prix lui-même (`.px-estim` ci-dessous),
   à la bande de la paire — là où le prix est écrit depuis le 01/09.

   Même facture que les repères de la jauge refaite le 08/09 : une pierre
   taillée, pas un rond plat, sinon il jure à côté d'elle.
   LE ROUGE N'EST PAS UNE ALERTE — « pas prise » est l'état NORMAL de presque
   toutes les cartes. D'où une pierre éteinte, mate, sans halo, quand le vert
   rayonne : c'est le vert qui attire l'œil, pas le rouge. Et la couleur n'est
   jamais seule à parler, `title` et `aria-label` disent l'état en toutes
   lettres — un daltonien lit la même chose. */
.tgc-prise { display: inline-block; width: 10px; height: 10px; border-radius: 50%;
  vertical-align: middle; flex: none; }
.tgc-prise.prise-on { background: var(--long);
  background: radial-gradient(circle at 34% 28%,
      color-mix(in srgb, var(--long) 25%, white) 0%,
      var(--long) 52%,
      color-mix(in srgb, var(--long) 60%, black) 100%);
  box-shadow: 0 0 7px rgba(52,209,122,.75);
  box-shadow: 0 0 7px rgba(52,209,122,.75), inset 0 0 0 1px rgba(255,255,255,.3); }
.tgc-prise.prise-off { background: var(--short); opacity: .85;
  background: radial-gradient(circle at 34% 28%,
      color-mix(in srgb, var(--short) 28%, white) 0%,
      var(--short) 55%,
      color-mix(in srgb, var(--short) 58%, black) 100%);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.4); }

/* « ≈ » DEVANT UN PRIX ESTIMÉ — la vérité que portait le point vert, posée sur
   ce qu'elle qualifie. Rien du tout quand le prix est live : un marqueur
   toujours affiché ne signale plus rien. Le prix estimé perd aussi un cran
   d'éclat, pour qu'on le distingue sans avoir à lire le signe. */
.px-estim { opacity: .82; }
.px-estim-mark { margin-right: .1em; opacity: .75; font-weight: 600; }

/* ---- Polish : ombres, cartes, overlay de chargement, responsive ---- */
.filters, .card { box-shadow: var(--shadow); }
.card { transition: border-color var(--t); }
.pnl-card { box-shadow: var(--shadow); transition: transform var(--t), border-color var(--t); }
.pnl-card:hover { transform: translateY(-2px); border-color: #33415a; }
/* Le bandeau de confirmation portait la même teinte que le fond et n'avait aucun
   cadre : le message existait, personne ne le voyait. Constaté le 12/08 en lançant
   un run — « la page ne change pas ». Elle changeait ; ça ne se voyait pas. */
.banner { animation: fadein .25s ease; text-align: center;
  padding: .65rem 1rem; margin: .6rem auto 0; max-width: 62rem;
  border: 1px solid var(--line); border-left: 4px solid var(--accent);
  border-radius: 6px; background: #16264f; color: var(--fg); font-size: var(--t-md); }
/* Trois tons, parce que trois nouvelles différentes ne se lisent pas pareil.
   Le ton est DIT par la route (`&lvl=`), jamais déduit du texte du message. */
.banner.ok    { border-left-color: #16a34a; background: #10281a; color: #d6f5e0; }
.banner.refus { border-left-color: #dc2626; background: #2b1416; color: #ffdcdc; }

/* Barre de progression de navigation fluide (en haut) */
#nav-progress { position: fixed; top: 0; left: 0; height: 3px; width: 0; opacity: 0;
  background: var(--accent); box-shadow: 0 0 8px var(--accent); z-index: 60;
  transition: width .4s ease, opacity .3s ease; }

/* Overlay de chargement (analyse IA + attente des formulaires data-loading).
   z-index AU-DESSUS de la fenêtre de détail (90) — revue du 07/09 : à 50, le
   voile d'attente s'affichait DERRIÈRE la modale et son bouclier anti
   double-clic ne couvrait plus les boutons TP/SL/Fermer qu'elle contient. Un
   double-clic sur « Fermer » pendant le vol du premier POST, c'est DEUX ordres
   market réels. */
#loading-overlay { position: fixed; inset: 0; z-index: 95; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 1rem; color: var(--fg);
  background: rgba(8,10,14,.72); backdrop-filter: blur(3px); }
#loading-overlay[hidden] { display: none; }
.spinner { width: 42px; height: 42px; border-radius: 50%;
  border: 4px solid rgba(56,189,248,.25); border-top-color: var(--accent);
  animation: spin .8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* Réduction des animations si l'utilisateur le demande (accessibilité) */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
  html { scroll-behavior: auto; }
}

/* Responsive : mobile / petits écrans — LE breakpoint mobile du site est 720,
   partout (harmonisation du 14/09 : la popup AUTOMATION vivait à 640, seule). */
@media (max-width: 720px) {
  /* Topbar COMPACTE (14/09, « harmonie sur téléphone ») : elle occupait trois
     rangs pleins — marque, puis une nav qui défilait de côté SANS aucun indice
     (Comptes/Risque semblaient coupés, la barre de défilement est masquée sur
     tactile). La nav REVIENT À LA LIGNE au lieu de défiler : tout est visible,
     rien à deviner — et paddings/tailles resserrés pour que la barre collante
     mange le moins d'écran possible. */
  :root { --tap: 40px; }
  /* PHASE 2 (ergonomie, 25/09/2026) : la barre du haut ne fait plus qu'UNE
     ligne — la marque et la cloche. Les sept liens qui prenaient deux rangs
     (≈ 110 px d'écran, collants) vivent désormais dans la BARRE D'ONGLETS du
     bas (`.tabbar`, en fin de feuille) ; FR/EN, le compte et la déconnexion
     dans son menu « Plus ». Ce qui suit ne vaut que si le navigateur sait
     ouvrir ce menu (`popover`) — sinon, l'ancienne nav revient (voir
     `@supports not`, plus bas). */
  .topbar { gap: .3rem .8rem; padding: .45rem .8rem; }
  .topbar .brand { font-size: var(--t-md); }
  .topbar nav { margin-left: auto; gap: 0; }
  .topbar nav a:not(.nav-notif) { display: none; }
  .topbar nav a.nav-notif { display: inline-flex; align-items: center; justify-content: center;
    min-width: var(--tap); min-height: var(--tap); padding: 0 .4rem; font-size: 1.15rem; }
  .topbar .inline { display: none; }
  /* Le contenu ne finit jamais SOUS la barre d'onglets. */
  main { padding-bottom: calc(80px + env(safe-area-inset-bottom)); }
  .phrase-x { width: 2.2rem; height: 2.2rem; }
  /* Les actions passent SOUS le titre, alignées à gauche, pleine largeur. */
  .page-actions { margin-left: 0; flex-basis: 100%; }
  /* Les boutons d'en-tête se partagent la ligne (Signaux : « vue tableau »
     et « Cartes » tenaient sur deux rangs entre les rails du classeur). */
  .page-actions .btn { flex: 1 1 auto; padding-left: .5rem; padding-right: .5rem; }
  /* iOS zoome sur tout champ dont le texte fait moins de 16 px : on les met
     à 16, et la page ne saute plus quand on tape. */
  input, select, textarea { font-size: 16px; }
  /* Le chrono de fabrication du snapshot est un diagnostic d'ordinateur. */
  .fresh-chrono { display: none; }
  /* POSITIONS SUR TÉLÉPHONE (phase 3) : le titre et l'interrupteur du bot sur
     la première ligne ; puis UNE ligne de boutons (Actualiser · Cartes · vue) ;
     puis l'état du snapshot en texte. La barre de fraîcheur perd sa boîte
     (`display: contents`) : ses organes deviennent des items de la ligne
     d'actions et se rangent par `order` ; ses attributs, que live-prices.js
     lit, ne bougent pas. Le `::before` à 100 % force le retour à la ligne. */
  .pos-head .bot-global { order: 1; margin-left: auto; }
  .pos-head .page-actions { order: 2; gap: .4rem; }
  .pos-head .fresh-bar { display: contents; }
  .pos-head .fresh-refresh { margin: 0; order: 1; }
  .pos-head [data-density] { order: 2; }
  .pos-head .view-toggle { order: 3; }
  .pos-head .page-actions::before { content: ""; flex-basis: 100%; height: 0; order: 4; }
  .pos-head .fresh-badge { order: 5; }
  .pos-head .fresh-age { order: 6; }
  /* Trois boutons sur UNE ligne de 358 px : marges resserrées, et chacun
     prend sa part de la largeur. */
  .pos-head .page-actions .btn { flex: 1 1 auto; padding: .3rem .45rem; }
  /* « ↻ Actualiser » se réduit à sa flèche (40 px, aria-label conservé) :
     c'est ce qui fait tenir les trois boutons sur la ligne. */
  .tel-cache { display: none; }
  .pos-head .fresh-refresh .btn { flex: 0 0 auto; min-width: var(--tap); font-size: 1.05rem; }
  /* Le survol n'existe pas au tactile : la phrase « s'agrandit au survol »
     coûtait un rang d'écran pour une information non actionnable au doigt. */
  .hint-souris { display: none; }
  main { margin: 1rem auto; }
  .head-row { flex-wrap: wrap; align-items: center; gap: .3rem;
    justify-content: flex-start; }
  .head-row > h1 { width: 100%; }
  /* FILTRES COMPACTS (25/09, Djamel : « le tableau prend trop de place ») :
     empilés, les trois champs du Journal et leur bouton mangeaient 400 px —
     un demi-écran avant la première carte. Une grille de deux colonnes :
     Paire | Timeframe, puis Recherche | Filtrer. Trois fois moins haut. */
  .filters { display: grid; grid-template-columns: 1fr 1fr; align-items: end;
    gap: .4rem .5rem; padding: .55rem .6rem; margin: .6rem 0 .7rem; }
  .filters label { min-width: 0; font-size: var(--t-xs); }
  .filters select, .filters input { min-width: 0; width: 100%; margin-top: 0; }
  .filters .btn { width: 100%; }
  .filters .classeur-hint { grid-column: 1 / -1; }
  .tabs { margin: .4rem 0 .6rem; }
  /* tables défilables horizontalement sans casser la page */
  table.grid { display: block; overflow-x: auto; white-space: nowrap; }
  .pnl-cards { gap: .5rem; }
  .pnl-card { flex: 1 1 45%; min-width: 0; }
}

/* Fiche détail d'ordre : échanges HTTP bruts (preuve — sous-compte invisible sur MEXC) */
.diag-pre { background: #0d1420; border: 1px solid #223047; border-radius: 8px;
  padding: .7rem .9rem; font-size: var(--t-sm); overflow-x: auto; white-space: pre-wrap;
  word-break: break-word; }

/* Chips comptes enrichies (sizing + assets) : retour à la ligne autorisé */
.chip { flex-wrap: wrap; }
.chip .muted { font-size: var(--t-xs); }

/* Assets par compte (page Comptes) : solde futures + tokens spot */
.assets-line { margin: .3rem 0 .1rem; font-size: var(--t-sm); }
.assets-tokens { font-size: var(--t-sm); color: var(--muted); margin: .15rem 0 0; }

/* ============================================================================
   Fond : la grenouille samouraï de Joker sur TOUT l'écran (avant : une vignette
   coincée en bas à droite). Trois couches fixes, sous le contenu, inertes au
   pointeur — les cartes et tableaux restent opaques, donc lisibles.

   1. NAPPE (html::before, z -3) : l'illustration plein cadre, très floutée et
      assombrie. Elle ne se lit plus comme une image mais comme une nappe de
      couleur — rouge du masque, or de l'armure, bleu de la pluie — qui donne sa
      profondeur à toute la page. `inset` négatif : le flou ne doit jamais
      laisser voir ses propres bords.
   2. DÉTAIL (body::before, z -2) : la MÊME illustration, nette, plein cadre, à
      basse opacité. C'est elle qui fait exister la pluie, l'armure et le fusil
      d'un bord à l'autre de l'écran.
   3. VOILE (body::after, z -1) : dégradés de lecture. Voile plein partout, puis
      renfort sur la colonne centrale (max 1040 px) où vit le texte — les marges,
      elles, laissent l'illustration respirer. Vignette + halo cyan de la charte.
   ========================================================================== */
html::before {
  content: ""; position: fixed; inset: -8vmax; z-index: -3; pointer-events: none;
  background: url("/static/bg-frog.jpg") center / cover no-repeat;
  filter: blur(64px) saturate(1.1) brightness(.55);
  opacity: .9;
}
body::before {
  content: ""; position: fixed; inset: 0; z-index: -2; pointer-events: none;
  /* Cadrage 20 % : sur un écran large le carré est rogné en hauteur — on remonte
     pour garder le buste et le fusil plutôt que le bas de la cape. */
  background: url("/static/bg-frog.jpg") center 20% / cover no-repeat;
  filter: saturate(.9) contrast(.92);
  opacity: .3;
}
body::after {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    linear-gradient(rgba(10,16,32,.58), rgba(10,16,32,.58)),
    linear-gradient(90deg, rgba(10,16,32,0) 0%, rgba(10,16,32,.46) 24%,
                    rgba(10,16,32,.46) 76%, rgba(10,16,32,0) 100%),
    radial-gradient(120% 85% at 50% 42%, transparent 32%, rgba(6,10,22,.55) 100%),
    radial-gradient(1200px 620px at 72% -12%, rgba(56,189,248,.10), transparent 62%);
}
@media (max-width: 900px) {
  /* Écran étroit : le contenu occupe toute la largeur, il n'y a plus de marge à
     faire respirer → voile plein et présence réduite. Le flou coûte cher sur
     mobile : on le rabote. */
  html::before { filter: blur(36px) saturate(1.1) brightness(.55); opacity: .8; }
  body::before { opacity: .16; }
  body::after { background:
    linear-gradient(rgba(10,16,32,.74), rgba(10,16,32,.74)),
    radial-gradient(120% 85% at 50% 42%, transparent 30%, rgba(6,10,22,.55) 100%); }
}
@media (prefers-contrast: more) {
  body::before { opacity: .1; }
  html::before { opacity: .5; }
}

/* Journal : action principale « Placer ordre » sortie du menu ⋯ (visible d'un coup
   d'œil, le menu ne garde que le secondaire). Bouton compact pour tenir en ligne. */
.jrn-actions { display: flex; align-items: center; gap: .4rem; white-space: nowrap; }
.jrn-actions .btn { padding-left: .65rem; padding-right: .65rem; }

/* Journal : bouton « Placer ordre » (ordre libre) accolé au titre */
.jrn-new { padding-left: .8rem; padding-right: .8rem; }

/* Positions : barre des marges futures par compte (dispo / utilisée / gelée / PnL) */
.margins-bar { display: flex; flex-wrap: wrap; gap: .5rem; margin: .4rem 0 1rem; }
.margin-chip { display: inline-block; padding: .45rem .8rem; font-size: var(--t-sm);
  background: var(--card); border: 1px solid var(--line); border-radius: 10px; }

/* ============================================================================
   Cartes TGC (Trading Game Cards) — cadre thématique par timeframe, réf. visuelle
   fournie par Joker : 15m vert/nature « Flow of Nature » · 1h bleu/marée « Flow of
   the Tide » · 4h violet/gothique « Gothic Eclipse » · 1d or/aube « Golden Dawn ».
   Contenu visible SANS JS ; glow/hover purement additifs.
   ========================================================================== */
/* Colonne 290 → 218 px (−25 %, demande Joker 15/08 : les cartes mangeaient trop
   d'écran). Tout ce qui recouvre l'illustration est en cqw et suit donc tout
   seul ; ce qui est en rem (l'extension, la jauge) a été compacté à la main plus
   bas, sinon le bandeau du bas devenait plus haut que l'illustration. */
.tgc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(218px, 1fr));
  gap: .7rem; margin: .8rem 0 1.4rem; }
.tgc-pair { font-weight: 800; font-size: 1.05rem; letter-spacing: .3px; }
.tgc-actions { display: flex; gap: .4rem; margin-top: .5rem; flex-wrap: wrap; align-items: center; }
.tgc-actions .btn { padding: .32rem .6rem; font-size: .8rem; }
.tgc-actions-pos input[type="text"] { width: 5.2rem; }

/* ============================================================================
   LA JAUGE (Hannyah + Position) : échelle INV → TP3, repères posés dessus.
   Refonte du 08/09 (Djamel : « la jauge faut juste l'améliorer et la mettre sur
   toutes les cartes », « une belle jauge »). Elle n'est plus un simple filet
   gris : monture métallique, remplissage en pierre taillée, et REPÈRES EN
   GEMMES — un losange facetté par borne, comme les pierres serties de la
   référence qu'il a envoyée.
   ELLE PREND SA COULEUR DE LA CARTE, sans une ligne de gabarit : les classes
   `tf-*` et `combo-*` sont déjà sur l'<article>, il suffit d'y lire une
   variable. Vert 15m · bleu 1h · violet 4h · or 1d, et par-dessus les accords :
   15m+1h rouge · 1h+4h noir · 4h+1d diamant. La cascade fait le reste — un
   combo écrit la variable APRÈS le timeframe, donc il gagne, ce qui est
   exactement la règle « la confluence PROMEUT la carte ».
   ========================================================================== */
/* Palette de la pierre, par timeframe. `--gem` = la facette claire (le haut de
   la taille), `--gem-d` = la profondeur (le bas). */
.tf-15m { --gem: #7ee08a; --gem-d: #1e4a2a; }
.tf-1h  { --gem: #6fb9ff; --gem-d: #143a5e; }
.tf-4h  { --gem: #b48bff; --gem-d: #3a2560; }
.tf-1d  { --gem: #ffd466; --gem-d: #6b4f12; }
.tf-1w  { --gem: #ff9dc2; --gem-d: #5c2038; }
/* SANS TIMEFRAME (ordre manuel hors carte, position hors dashboard — 25/09) :
   un gris d'acier, qui ne ressemble à aucune des quatre couleurs de TF. */
.tf-x   { --gem: #aab6cc; --gem-d: #262d3a; }
/* Les accords priment sur le timeframe (même spécificité, déclarés après). */
.combo-rouge   { --gem: #ff5a52; --gem-d: #6e0f14; }
.combo-noir    { --gem: #c8d6ec; --gem-d: #0b0e14; }
.combo-diamant { --gem: #eafaff; --gem-d: #2b6d8c; }

.tgc-gauge-wrap { margin: .5rem 0 .1rem; }
/* Monture : creux sombre + liseré métallique teinté par la pierre. */
.tgc-gauge { position: relative; height: 10px; border-radius: 7px;
  background: linear-gradient(180deg, rgba(0,0,0,.55), rgba(0,0,0,.28));
  border: 1px solid var(--line); overflow: visible;
  box-shadow: inset 0 1px 3px rgba(0,0,0,.7);
  box-shadow: inset 0 1px 3px rgba(0,0,0,.7),
              0 0 0 1px color-mix(in srgb, var(--gem, #8fa) 22%, transparent); }
/* La pierre : profondeur en bas, facette claire en haut, éclat au sommet.
   Chaque déclaration à `color-mix` est PRÉCÉDÉE de son équivalent simple —
   même discipline que les `font-size` en rem puis cqw ailleurs dans ce
   fichier. Un navigateur sans color-mix (< Chrome 111 / Safari 16.2) jette la
   seconde et garde une jauge pleine et lisible, au lieu d'une barre vide. */
.tgc-gauge-fill { height: 100%; border-radius: 7px; opacity: 1;
  background: var(--gem, #7ee08a);
  background: linear-gradient(180deg,
      color-mix(in srgb, var(--gem, #7ee08a) 92%, white) 0%,
      var(--gem, #7ee08a) 42%,
      var(--gem-d, #1e4a2a) 100%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.55);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.55),
              0 0 7px color-mix(in srgb, var(--gem, #7ee08a) 55%, transparent);
  transition: width .4s ease; }
/* CURSEUR DE PRIX — carte NON ACTIVÉE seulement (08/09). Volontairement PAUVRE
   à côté de la pierre du remplissage : un filet clair, sans éclat, sans
   couleur de thème. Il ne doit surtout pas se lire comme un avancement — il dit
   « le prix est ici », pas « on en est là ». Sa hauteur dépasse la monture pour
   rester repérable entre deux gemmes. */
.tgc-curseur { position: absolute; top: -3px; bottom: -3px; width: 2px;
  transform: translateX(-1px); border-radius: 1px;
  background: rgba(255,255,255,.9);
  box-shadow: 0 0 4px rgba(0,0,0,.8), 0 0 3px rgba(255,255,255,.5); }
/* Prix au-delà de l'échelle : le curseur est AU BORD, sa valeur est ailleurs —
   même convention que les repères hors échelle, en pointillés et éteint, pour
   qu'il n'affirme pas « le prix est pile sur TP3 ». */
.tgc-curseur.c-hors { background: none; opacity: .75;
  border-left: 2px dashed rgba(255,255,255,.85); box-shadow: none; }

/* REPÈRE = une gemme : un carré tourné à 45°, serti d'un liseré clair. Le
   losange remplace le trait de 2 px — c'est ce qui donne l'allure « sertie »
   sans image et sans un octet de plus. */
.tgc-mark { position: absolute; top: 50%; width: 7px; height: 7px;
  transform: translate(-50%, -50%) rotate(45deg); border-radius: 1.5px;
  background: linear-gradient(135deg, #fff, var(--muted) 60%);
  box-shadow: 0 0 0 1px rgba(0,0,0,.55), 0 1px 3px rgba(0,0,0,.6); }
.tgc-mark.m-entry { background: linear-gradient(135deg, #fff, var(--accent) 65%); }
.tgc-mark.m-tp    { background: linear-gradient(135deg, #fff, var(--long) 65%); }
.tgc-mark.m-inv   { background: linear-gradient(135deg, #fff, var(--short) 65%); }
.tgc-marklabels { position: relative; height: .85rem; margin-top: .12rem; }
/* Le décalage suit la position du repère : INV (0 %) s'aligne à gauche, TP3
   (100 %) à droite, les repères du milieu restent centrés. Avec un -50 % fixe,
   « INV » et « TP3 » dépassaient du cadre de la carte. */
.tgc-marklabel { position: absolute; transform: translateX(calc(var(--p, 50) * -1%));
  font-size: .58rem; color: var(--muted); white-space: nowrap; }
.tgc-marklabel.m-entry { color: var(--accent); }
.tgc-marklabel.m-inv { color: var(--short); }

/* WAITING — remplace la jauge tant que le signal n'est pas activé. Même hauteur
   que la jauge (9px + repères) pour que les cartes de la grille restent alignées.
   La piste en pointillés dit « rien n'a encore commencé » sans imiter une barre
   de progression, qui suggérerait un mouvement inexistant. */
.tgc-waiting { display: flex; align-items: center; gap: .45rem;
  margin: .45rem 0 .1rem; min-height: calc(8px + .12rem + .85rem); }
.tgc-waiting::after { content: ""; flex: 1; height: 8px; border-radius: 6px;
  border: 1px dashed var(--line); background: var(--card2); }
.tgc-waiting-k { font-size: .6rem; font-weight: 700; letter-spacing: .12em;
  color: var(--muted); }
.tgc-waiting-d { font-size: .58rem; color: var(--muted); white-space: nowrap; }
/* ACTIVÉ sans position : la zone est atteinte, mais rien n'est engagé — vert
   franc pour le dire, piste pleine (plus de pointillés d'attente). */
.tgc-waiting.act .tgc-waiting-k { color: var(--long); }
.tgc-waiting.act::after { border-style: solid; border-color: var(--long);
  opacity: .45; }
/* Terminé sans position : même bande, ton éteint — il n'y a plus rien à suivre. */
.tgc-waiting.over { opacity: .55; }

/* Thèmes par timeframe (cartes Signal & Hannyah) */
.tf-15m { --tgc-a: #2f6b39; --tgc-b: #10240f; --tgc-glow: rgba(90,200,120,.16); --tgc-tag: #7ee08a; }
.tf-1h  { --tgc-a: #1e5a8a; --tgc-b: #0b1b33; --tgc-glow: rgba(80,160,255,.16); --tgc-tag: #6fb9ff; }
.tf-4h  { --tgc-a: #5a3d8a; --tgc-b: #180f2e; --tgc-glow: rgba(160,110,255,.16); --tgc-tag: #b48bff; }
.tf-1d  { --tgc-a: #8a6b1e; --tgc-b: #2b1f07; --tgc-glow: rgba(240,190,80,.18); --tgc-tag: #ffd466; }
.tf-1w  { --tgc-a: #7a2f4f; --tgc-b: #260a18; --tgc-glow: rgba(240,110,160,.16); --tgc-tag: #ff9dc2; }
.tf-x   { --tgc-a: #33415f; --tgc-b: #131c30; --tgc-glow: transparent; --tgc-tag: #aab6cc; }

/* Lien de bascule cartes/tableau */
.view-toggle { font-size: var(--t-sm); white-space: nowrap; }

/* Journal : pagination SSR (liste plate, 50/page — refonte Djamel 01/08) */
.pager { display: flex; align-items: center; justify-content: center; gap: .9rem;
  margin: 1.1rem 0 .4rem; }

/* ============================================================================
   Cartes TGC — HABILLAGE NÉON (23/09/2026, maquette de Djamel).
   La carte était une illustration recouverte de calques en % (bandeau, pièce,
   parchemin) calés sur des gravures. Elle devient une PILE sur verre sombre :
     entête   — vignette (logo) · paire · pastilles timeframe + sens
     scène    — l'illustration du JETON (static/tgc/tokens) dans un cadre
                biseauté, repli sur celle du timeframe
     tuiles   — les chiffres, chacun dans sa case
     bande    — prise/pas prise, série, DMG, jauge (inchangés)
   Plus rien ne recouvre l'image : le débordement du cartouche sur la gravure,
   combattu au pixel le 08/09 à 158 px, n'a plus de gravure à recouvrir.

   LA COULEUR DU TIMEFRAME TIENT TOUT : `--gem` (vert 15m · bleu 1h · violet 4h
   · or 1d, puis les accords 15m+1h rouge · 1h+4h noir · 4h+1d diamant, déclarés
   APRÈS et donc gagnants) colore le liseré, le cadre de la scène, la pastille
   et la jauge. D'où UNE scène par jeton, pas une par jeton et par timeframe.

   Tout est en `cqw` : l'<article> est le conteneur, la typo suit la colonne —
   218 px (normal) comme 158 px (Compact). Chaque taille en cqw est bornée par
   un plancher en px (`max()`) : à 158 px, 3,5cqw seul tomberait sous 6 px.
   Chaque `color-mix` est PRÉCÉDÉ d'un équivalent simple (même discipline que
   la jauge) : un navigateur qui ne le connaît pas garde une carte lisible.
   ========================================================================== */
/* LE VERRE PREND LA TEINTE DE LA PIERRE (Djamel, 23/09 : « on a l'impression
   que les cartes ont toutes le même fond sombre alors que chaque timeframe
   devrait avoir une couleur »). Un halo de `--gem` en haut, puis un verre
   teinté qui s'assombrit vers le bas, pour que les chiffres restent sur du
   sombre. Les combos suivent d'eux-mêmes : ils réécrivent `--gem`.
   Repli sans color-mix : le dégradé du thème (`--tgc-a` → `--tgc-b`). */
.tgc-art { position: relative; container-type: inline-size;
  border-radius: 16px; overflow: hidden; display: flex; flex-direction: column;
  background: linear-gradient(165deg, var(--tgc-a, #121b2d) 0%, var(--tgc-b, #070b15) 70%);
  /* LA CARTE DE SA COULEUR (Djamel, 25/09 : « je les trouve toutes noires
     alors qu'elles ont toutes une couleur », image de référence à l'appui).
     Le verre du 23/09 ne portait que 20 % → 6 % de la pierre : à l'œil, une
     carte noire au liseré vert. Désormais le fond EST la pierre profonde
     (`--gem-d`) avec un halo clair en haut, un cadre double — le liseré de
     la pierre, puis un filet d'or — et une lueur de la couleur. */
  background:
    radial-gradient(120% 50% at 50% 0%, color-mix(in srgb, var(--gem, #33415f) 42%, transparent), transparent 75%),
    linear-gradient(175deg,
      color-mix(in srgb, var(--gem-d, #131c30) 95%, #000) 0%,
      color-mix(in srgb, var(--gem-d, #131c30) 70%, #03060b) 55%,
      color-mix(in srgb, var(--gem-d, #131c30) 55%, #02040a) 100%);
  border: 1.5px solid var(--gem, #33415f);
  border: 1.5px solid color-mix(in srgb, var(--gem, #33415f) 85%, white);
  box-shadow: 0 0 0 1.5px rgba(214,178,90,.75), 0 0 0 3px rgba(0,0,0,.6),
    0 6px 26px rgba(0,0,0,.55), 0 0 22px var(--tgc-glow, transparent);
  box-shadow: 0 0 0 1.5px rgba(214,178,90,.75), 0 0 0 3px rgba(0,0,0,.6),
    0 0 18px color-mix(in srgb, var(--gem, #33415f) 55%, transparent),
    inset 0 0 26px color-mix(in srgb, var(--gem, #33415f) 30%, transparent),
    0 6px 26px rgba(0,0,0,.55);
  transition: transform var(--t), box-shadow var(--t); }
/* Survol = SOURIS uniquement. Sur tactile il n'y a pas de survol, seulement un
   « :hover collant » posé par le tap qui faisait bouger les cartes pendant le
   défilement (recalibrage 20/08). */
@media (hover: hover) and (pointer: fine) {
  .tgc-art:hover { transform: translateY(-3px);
    box-shadow: 0 10px 30px rgba(0,0,0,.6), 0 0 34px var(--tgc-glow, transparent); }
}

.tgc-abox { padding: 3.6cqw 3.6cqw 0; }

/* ENTÊTE : vignette · paire · pastilles. La vignette a une taille FIXE en cqw
   (15cqw) : la scène démarre donc toujours à la même hauteur, et les badges
   d'état posés dessus (ACTIVÉ, combo) tombent au même endroit sur toutes les
   cartes — voir `.tgc-badge-act`. */
.tgc-ahead { display: flex; align-items: center; gap: 3cqw; height: 15cqw;
  margin-bottom: 3cqw; min-width: 0; }
.tgc-aid { display: flex; flex-direction: column; justify-content: center;
  gap: 1.3cqw; min-width: 0; flex: 1; }
.tgc-ahead .tgc-pair { font-weight: 800; font-size: 1rem; font-size: max(13px, 7cqw);
  line-height: 1.05; letter-spacing: .15px; color: #f2f6ff;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tgc-ahead .tgc-quote { font-style: normal; font-weight: 600; font-size: .72em;
  color: #aeb9cf; }
.tgc-chips { display: flex; gap: 1.6cqw; min-width: 0; }
/* Les pastilles : timeframe à la couleur de la pierre, sens en vert/rouge.
   Le sens garde SA couleur quel que soit le thème — c'est lui qu'on lit en
   premier, et une carte short sur un 15m vert ne doit pas avoir l'air long. */
.tgc-abadge, .tgc-adir { font-size: .6rem; font-size: max(9px, 4.2cqw); font-weight: 800;
  line-height: 1.25; padding: .3cqw 2.2cqw; border-radius: 99px; white-space: nowrap;
  border: 1px solid rgba(255,255,255,.2); }
/* LA PASTILLE DU TIMEFRAME GARDE SA COULEUR (Djamel, 25/09) : `--tgc-tag` est
   posé par la classe tf-* et AUCUN combo ne le réécrit — un « 1h » reste bleu
   sur une carte combo rouge ; la couleur du combo, elle, tient la carte. */
.tgc-abadge { color: var(--tgc-tag, var(--gem, #dfe7f7)); background: rgba(2,6,12,.55);
  border-color: var(--gem, rgba(255,255,255,.2));
  border-color: rgba(214,178,90,.85); }
.tgc-adir { color: var(--long); border-color: var(--long);
  background: rgba(2,12,6,.6); }
.tgc-adir.short { color: var(--short); border-color: var(--short);
  background: rgba(14,2,2,.6); }

/* VIGNETTE : le vrai logo du jeton sur une tuile sombre (chantier 20/08/2026 —
   récolte scripts/fetch_logos.py). Sans logo, la pièce dorée au ticker revient,
   police par palier de longueur (BTC → 1000FLOKI) pour rester dans la tuile. */
.tgc-coin { flex: none; width: 15cqw; height: 15cqw; border-radius: 4cqw;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  font-weight: 900; font-size: .62rem; font-size: max(8px, 3.9cqw); letter-spacing: 0;
  color: #6b4a10; text-shadow: 0 1px 0 rgba(255,244,200,.9);
  background: radial-gradient(circle at 32% 28%, #fff3c0 0%, #f8d675 30%, #e3ab35 62%, #b57b16 88%, #93600e 100%);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.25), 0 2px 8px rgba(0,0,0,.45); }
.tgc-coin.tgc-coin-long { font-size: .5rem; font-size: max(7px, 3.1cqw); }
.tgc-coin.tgc-coin-xl { font-size: .42rem; font-size: max(6px, 2.4cqw); }
.tgc-coin.tgc-coin-img { background: #0d1526; border: 1px solid rgba(255,255,255,.1);
  box-shadow: 0 2px 8px rgba(0,0,0,.45); }
.tgc-coin.tgc-coin-img img { width: 74%; height: 74%; border-radius: 50%;
  object-fit: cover; display: block; }

/* LA SCÈNE, dans un cadre biseauté au liseré de la pierre. 16/9 et non 4/3 :
   la carte garde ainsi à peu près la hauteur qu'elle avait avant l'habillage
   (mesuré au navigateur à 221 px : 379 px de haut en 4/3). L'orbe des scènes
   par jeton est centré à ~35 % de la hauteur de l'image carrée — `center 16%`
   le remet au milieu du cadre. */
.tgc-scene { position: relative; aspect-ratio: 16 / 9; border-radius: 3cqw;
  background-color: #0a1020; background-size: cover; background-position: center 16%;
  clip-path: polygon(7% 0, 93% 0, 100% 9%, 100% 91%, 93% 100%, 7% 100%, 0 91%, 0 9%);
  box-shadow: inset 0 0 0 1.5px var(--gem, #33415f), inset 0 0 22px rgba(0,0,0,.35);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--gem, #33415f) 80%, transparent),
              inset 0 0 22px color-mix(in srgb, var(--gem, #33415f) 30%, transparent); }
/* `?v=2` — LES QUATRE .jpg ONT ÉTÉ RETOUCHÉS le 08/09 (mention « TRADING SIGNAL
   TGC » effacée) SANS changer de nom. Sans ce jeton de version, navigateurs et
   Cloudflare continuent de servir l'image d'avant. Incrémenter à chaque retouche. */
.tf-15m .tgc-scene { background-image: url("/static/tgc/tf-15m.jpg?v=2"); }
.tf-1h  .tgc-scene { background-image: url("/static/tgc/tf-1h.jpg?v=2"); }
.tf-4h  .tgc-scene { background-image: url("/static/tgc/tf-4h.jpg?v=2"); }
.tf-1d  .tgc-scene { background-image: url("/static/tgc/tf-1d.jpg?v=2"); }
/* Pas d'illustration propre : celle du 1d, que le voile gris (`--gem` en
   fusion `color`, plus bas) désature — la pièce centrale reste dorée. */
.tf-x   .tgc-scene { background-image: url("/static/tgc/tf-1d.jpg?v=2"); }
/* Cadrage du repli, calculé sur les 4 illustrations (464x458, pièce gravée
   Ø 34,5 % de la largeur, centre à 38,3 % de la hauteur — mesure du 02/08) :
   à 125 % et 28,6 % en vertical, la pièce tombe au centre de la scène et le
   bandeau gravé du haut (< 14 %) reste hors champ. Le médaillon la couvre
   alors à Ø 43 % de la largeur de scène. */
.tgc-scene:not(.tgc-scene-tok) { background-size: 125%; background-position: 50% 28.6%; }
.tgc-medal { position: absolute; left: 50%; top: 50%; width: 43.5%; aspect-ratio: 1;
  transform: translate(-50%, -50%); border-radius: 50%;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  font-weight: 900; font-size: 1.2rem; font-size: max(13px, 7.4cqw);
  color: #6b4a10; text-shadow: 0 1px 0 rgba(255,244,200,.9), 0 -1px 2px rgba(90,60,10,.55);
  background: radial-gradient(circle at 32% 28%, #fff3c0 0%, #f8d675 30%, #e3ab35 62%, #b57b16 88%, #93600e 100%);
  box-shadow: inset 0 0 0 1.2cqw rgba(151,103,18,.9), inset 0 0 0 2.2cqw rgba(255,236,170,.65),
    inset 0 0 8cqw rgba(120,70,10,.55), 0 1.8cqw 5.5cqw rgba(0,0,0,.5),
    0 0 8cqw rgba(248,214,117,.4); }
.tgc-medal.tgc-coin-long { font-size: max(10px, 5cqw); }
.tgc-medal.tgc-coin-xl { font-size: max(8px, 3.8cqw); }
.tgc-medal img { width: 84%; height: 84%; border-radius: 50%; object-fit: cover; display: block; }
/* Déclarée APRÈS les thèmes, même spécificité : la scène du jeton gagne. */
.tgc-scene.tgc-scene-tok { background-image: var(--scene); }
/* LE PAYSAGE PREND LA COULEUR DU TIMEFRAME (25/09). Une scène par JETON, bleu
   nuit : sans ceci, un BTC 15m et un BTC 1d avaient le même décor. Un voile
   `--gem` en mode de fusion `color` recolore le paysage en gardant sa lumière
   — et un masque radial laisse le CENTRE intact : la pièce reste dorée, comme
   sur l'image de référence. Rien n'est repeint : sans `mix-blend-mode`, le
   voile ne s'applique pas et la scène reste celle d'avant. */
.tgc-scene { box-shadow: inset 0 0 0 1.5px rgba(214,178,90,.85),
    inset 0 0 26px color-mix(in srgb, var(--gem, #33415f) 45%, transparent); }
.tgc-scene::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: var(--gem, transparent); mix-blend-mode: color; opacity: .62;
  -webkit-mask: radial-gradient(circle at 50% 50%, transparent 0 20%, #000 40%);
  mask: radial-gradient(circle at 50% 50%, transparent 0 20%, #000 40%); }

/* LES TUILES. Deux colonnes ; HP (signal) et DMG (position) en pleine largeur.
   PLANCHER DE LISIBILITÉ : `max()` garde la proportion sur les cartes larges et
   empêche la typo de fondre sur les étroites. `nowrap` + ellipse : une tuile ne
   grandit JAMAIS en hauteur — au pire la valeur s'ellipse, et l'infobulle
   (title des libellés) reste là. */
.tgc-apanel { margin-top: 2.6cqw; }
.tgc-agrid, .tgc-fgrid { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr);
  gap: 1.8cqw; }
/* Libellé et valeur sur UNE ligne (valeur calée à droite, comme l'ancien
   parchemin) : c'est ce qui rend à la carte sa hauteur d'avant. */
.tgc-tile { display: flex; align-items: baseline; justify-content: space-between;
  gap: 1.6cqw; min-width: 0; padding: 1.3cqw 2.6cqw;
  border-radius: 2.6cqw; background: rgba(2,6,10,.55);
  border: 1px solid rgba(255,255,255,.08);
  border: 1px solid color-mix(in srgb, var(--gem, #fff) 38%, transparent); }
.tgc-tile .k { flex: none; font-size: .56rem; font-size: max(8px, 3.7cqw);
  font-weight: 700; letter-spacing: .3px; color: var(--muted); line-height: 1.2; }
.tgc-tile b { min-width: 0; font-size: .74rem; font-size: max(10px, 4.9cqw);
  font-weight: 800; line-height: 1.25; color: #eef3ff; font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tgc-tile-wide { grid-column: 1 / -1; }
/* Colonne étroite (Compact, 158 → ~200 px) : une demi-tuile n'a plus la place
   de loger libellé ET valeur côte à côte — un prix à 8 décimales y serait
   coupé. Le libellé passe AU-DESSUS de la valeur ; les pleines largeurs
   restent sur une ligne. */
@container (max-width: 200px) {
  .tgc-tile:not(.tgc-tile-wide) { flex-direction: column; align-items: stretch; gap: 0; }
}
/* DEF en vert d'eau (des sorties en gain), ✓ vert franc quand atteint.
   INV RESTE NEUTRE : c'est la fin de mouvement THOR, PAS un stop-loss — la
   peindre en rouge la ferait lire comme un SL (règle du 09/09). */
.tgc-tile-def b { color: #8fe8b8; }
.tgc-tile-hit { border-color: rgba(52,209,122,.35); }
.tgc-tile i.ok { font-style: normal; color: var(--long); margin-left: 1cqw; }
/* Prix à beaucoup de décimales (SHIB, BONK, PEPE) : un cran plus bas pour que
   la valeur tienne dans sa demi-tuile. */
.num-long .tgc-tile b { font-size: max(9.2px, 4.1cqw); letter-spacing: -.01em; }
/* Le DMG de la face : pleine largeur, centré, graisse maximale (Joker, 08/09 :
   « la police est trop fine »). Le mot « DMG » est DANS la valeur — c'est le
   texte exact que le flux réécrit à chaque tick. */
.tgc-tile-dmg { justify-content: center; }
.tgc-tile-dmg b { font-size: max(11px, 5.6cqw); font-weight: 900; }
.tgc-tile-dmg b.dir-long { color: var(--long); }
.tgc-tile-dmg b.dir-short { color: var(--short); }
.tgc-tile-dmg b.muted { color: var(--muted); }
.tgc-tile-dmg:has(b.dir-long) { border-color: rgba(52,209,122,.3); background: rgba(52,209,122,.06); }
.tgc-tile-dmg:has(b.dir-short) { border-color: rgba(242,80,80,.3); background: rgba(242,80,80,.06); }

/* NUMÉRO DE SÉRIE — « TGC · #1138 · 08/09/2026 », dans la BANDE DU BAS, à la
   place qu'occupaient « activé » et « il y a 22 j » (Djamel, 08/09 : « dans
   l'espace où c'était écrit activé »). C'est une pastille `.st` comme ses
   voisines : elle hérite du fond translucide et ne demande que sa typographie
   de série — chiffres à chasse fixe pour que deux cartes s'alignent, et
   interlettrage pour l'allure « gravé ». */
.tgc-aserial { font-variant-numeric: tabular-nums; letter-spacing: .06em;
  font-weight: 700; color: rgba(232, 238, 252, .78); white-space: nowrap; }

/* LA BANDE DU BAS (le VIVANT : prise, série, DMG, jauge, actions). Plus de
   dégradé propre : elle continue le verre de la carte, séparée par un filet. */
.tgc-ext { padding: 2.4cqw 3.6cqw 3cqw; display: flex; flex-direction: column;
  gap: .34rem; flex: 1; }
.tgc-alive { display: flex; align-items: center; gap: .38rem; color: var(--fg); flex-wrap: wrap; }
.tgc-alive .px { font-weight: 800; font-size: .92rem; }
.tgc-alive .st { font-size: .64rem; padding: .1rem .42rem; border-radius: 9px;
  background: rgba(255,255,255,.08); }
.tgc-ext .tgc-actions { gap: .3rem; }
.tgc-ext .tgc-actions .btn { padding: .26rem .45rem; font-size: .72rem; }
.tgc-alive .st.ok { background: rgba(46,160,67,.4); }
.tgc-alive .st.star { background: rgba(240,190,80,.3); color: #ffd466; }
.tgc-alive .st.warn { background: rgba(240,190,80,.28); color: #ffe4a8; font-weight: 700; }
.tgc-ext .tgc-actions { margin-top: auto; }
.tgc-ext .tgc-actions .grow { flex: 1; display: flex; }
.tgc-ext .tgc-actions .grow .btn { flex: 1; }
.tgc-ext .tgc-gauge { background: linear-gradient(180deg, rgba(0,0,0,.62), rgba(0,0,0,.38)); }
.tgc-ext .tgc-waiting::after { background: rgba(0,0,0,.4); }
.tgc-ext .tgc-waiting-k { color: #e8eefc; opacity: .85; }

/* ============================================================================
   Journal — cartes Hannyah COMPACTES (refonte 16/08/2026, RECALIBRAGE 20/08/2026).
   État par défaut : AUCUNE action visible ; seulement, si le signal est ACTIVÉ,
   une icône ACTIVÉ + une bordure colorée par le SENS (vert long / rouge short).
   Rien n'est affiché pour un signal non activé. Au survol (souris) OU au tap
   (app.js pose `.is-expanded`), la carte s'agrandit et déplie Confier au bot ·
   Prendre position · Retirer.

   RECALIBRAGE 20/08 — le principe (zoom sur place) est conservé, trois maux
   corrigés :
   1. le scale FIXE (1.07) débordait de l'écran — sur mobile en colonne pleine
      largeur, prix coupé à gauche, TP3/INV coupés à droite (photo Joker) →
      app.js mesure la place libre autour de la carte et pose `--zoom`
      (amplitude possible) + `transform-origin` (la carte grandit VERS l'espace
      libre) ; sans JS, repli neutre scale(1.04) centré ;
   2. sur tactile, le « :hover collant » du tap agitait les cartes pendant le
      défilement → tout le survol est enfermé dans @media (hover: hover) ;
   3. les actions surgissaient en display:none→flex (non animable) et la carte
      repassait SOUS ses voisines à l'instant où le survol cessait → dépliage
      animé (max-height/opacity/visibility) et z-index retenu le temps que la
      carte finisse de rétrécir.
   Tout est isolé sur `.tgc-hannyah` : les cartes Signal (page Signaux) et
   Position (page Positions) partagent `.tgc-art` mais ne sont PAS touchées.
   ========================================================================== */
.tgc-hannyah { position: relative; cursor: pointer; z-index: 1;
  /* z-index en dernier : il ne bouge qu'APRÈS le rétrécissement (délai .26s) —
     sinon la carte plonge sous ses voisines pendant qu'elle est encore grosse. */
  transition: transform .26s cubic-bezier(.2, .75, .25, 1), box-shadow .26s ease,
              z-index 0s linear .26s; }

/* Icône ACTIVÉ : posée dans le coin haut droit de la SCÈNE. Seul marqueur
   d'activation à l'état compact. La scène démarre à 21,6cqw sur toutes les
   cartes (marge 3,6 + vignette 15 + écart 3, cf. `.tgc-ahead`) : 24,5cqw la
   place juste sous le biseau, quelle que soit la largeur de colonne. */
.tgc-badge-act { position: absolute; top: 24.5cqw; right: 8cqw; z-index: 4;
  display: inline-flex; align-items: center; gap: .2rem;
  padding: .1rem .42rem; border-radius: 999px; pointer-events: none;
  font-size: .6rem; font-weight: 800; letter-spacing: .05em; color: #04120a;
  background: linear-gradient(90deg, #7ee08a, #34c759);
  box-shadow: 0 2px 8px rgba(0,0,0,.45), 0 0 10px rgba(52,199,89,.55);
  text-shadow: 0 1px 0 rgba(255,255,255,.3); }
.tgc-act-short .tgc-badge-act { color: #180404;
  background: linear-gradient(90deg, #ff9a9a, #e23c3c);
  box-shadow: 0 2px 8px rgba(0,0,0,.45), 0 0 10px rgba(226,60,60,.55); }
/* TERMINÉ (04/09) : cycle complet (TP3 atteint) — l'or du validé remplace le
   vert/rouge « activé », quel que soit le sens (le short ne reste pas rouge). */
.tgc-badge-fin, .tgc-act-short .tgc-badge-fin { color: #1a1303;
  background: linear-gradient(90deg, #ffe27a, #f0b90b);
  box-shadow: 0 2px 8px rgba(0,0,0,.45), 0 0 10px rgba(240,185,11,.55); }

/* POSTÉ (25/09) : un ordre limit attend au carnet — le bleu du « en cours »,
   distinct du vert/rouge d'ACTIVÉ et de l'or de TERMINÉ, quel que soit le sens. */
.tgc-badge-poste, .tgc-act-short .tgc-badge-poste { color: #04121f;
  background: linear-gradient(90deg, #9fdcff, #38bdf8);
  box-shadow: 0 2px 8px rgba(0,0,0,.45), 0 0 10px rgba(56,189,248,.55); }

/* ============================================================================
   LE COMBO — la couleur d'un accord entre timeframes (21/08/2026).

   Quand plusieurs timeframes portent le MÊME signal (même entrée, mêmes TP),
   la carte du Journal prend une couleur :
     15m + 1h → ROUGE · 1h + 4h → NOIR · 4h + 1d → DIAMANT
   C'est le SEUL endroit où la couleur intervient. Une carte seule reste
   exactement celle d'avant, et les pages Signaux et Positions ne sont pas
   concernées du tout — elles rendent le thème du timeframe, comme toujours.

   POURQUOI UN ANNEAU EN PSEUDO-ÉLÉMENT et pas un `box-shadow` sur la carte :
   `.tgc-activated` pose déjà un box-shadow (le halo vert/rouge du sens du
   trade), avec une spécificité plus forte. Les deux se seraient écrasés l'un
   l'autre selon l'état de la carte. L'anneau vit donc sur `::after` — il ne
   peut entrer en conflit avec rien — et la LUEUR passe par `--tgc-glow`, une
   variable que `.tgc-art` lit déjà dans son ombre : aucune règle à surcharger.

   Le NOIR est un onyx, pas un trou : anneau sombre à rebord clair. Un anneau
   noir uni sur un fond sombre (#0b0f18) ne se verrait tout simplement pas. */
.tgc-hannyah[class*="combo-"]::after {
  content: ""; position: absolute; inset: 0; z-index: 5; pointer-events: none;
  border-radius: inherit;
  box-shadow: inset 0 0 0 3px var(--combo-c),
              inset 0 0 0 4.5px var(--combo-rim, rgba(255,255,255,.25)),
              inset 0 0 14px var(--combo-in, transparent); }
.combo-rouge   { --combo-c: #d81f26; --combo-rim: rgba(255,150,140,.5);
  --combo-in: rgba(216,31,38,.35); --tgc-glow: rgba(226,60,45,.42); }
.combo-noir    { --combo-c: #10141b; --combo-rim: rgba(190,205,228,.6);
  --combo-in: rgba(0,0,0,.5); --tgc-glow: rgba(150,170,200,.3); }
.combo-diamant { --combo-c: #bfe9ff; --combo-rim: rgba(255,255,255,.85);
  --combo-in: rgba(150,220,255,.3); --tgc-glow: rgba(150,220,255,.5); }

/* Le badge qui NOMME l'accord. Les deux cartes d'un combo sont identiques à
   l'écran — même entrée, mêmes TP : sans lui, elles se lisent comme un doublon
   et la couleur reste une décoration inexpliquée. Posé en bas à gauche de
   l'illustration, à l'opposé du badge ACTIVÉ. */
/* Posé sur l'ILLUSTRATION, en miroir du badge ACTIVÉ (même hauteur, côté
   opposé). Il était d'abord en bas à gauche de l'article : il tombait sur la
   bande du bas et recouvrait le prix live — un badge décoratif ne masque pas
   un chiffre. */
.tgc-badge-combo { position: absolute; left: 8cqw; top: 24.5cqw; z-index: 6;
  padding: .12rem .5rem; border-radius: 999px; pointer-events: none;
  font-size: .58rem; font-weight: 800; letter-spacing: .06em;
  color: #fff; background: var(--combo-c);
  box-shadow: 0 2px 8px rgba(0,0,0,.5),
              inset 0 0 0 1px var(--combo-rim, rgba(255,255,255,.3)); }
.combo-diamant .tgc-badge-combo { color: #0d2b3c; }

/* Bordure colorée (glow) de la carte ACTIVÉE. `outline` n'est pas rognée par
   `overflow:hidden` et suit le radius sur les navigateurs récents ; le glow est un
   box-shadow additif, coloré selon le sens. */
.tgc-hannyah.tgc-activated { outline: 2px solid transparent; outline-offset: -2px; }
.tgc-hannyah.tgc-activated.tgc-act-long { outline-color: rgba(52,199,89,.85);
  box-shadow: 0 6px 26px rgba(0,0,0,.55), 0 0 22px rgba(52,199,89,.45); }
.tgc-hannyah.tgc-activated.tgc-act-short { outline-color: rgba(226,60,60,.85);
  box-shadow: 0 6px 26px rgba(0,0,0,.55), 0 0 22px rgba(226,60,60,.5); }

/* Actions repliées : hauteur nulle ANIMABLE (pas display:none, qui claquait sec),
   invisibles ET hors tabulation (visibility) tant que la carte est compacte. La
   visibility ne bascule qu'à la FIN du repli (délai .28s), immédiatement à l'ouverture. */
.tgc-hannyah .tgc-actions { display: flex; overflow: hidden;
  max-height: 0; opacity: 0; visibility: hidden; margin-top: 0;
  transform: translateY(4px);
  transition: max-height .28s cubic-bezier(.2, .75, .25, 1), opacity .2s ease,
              margin-top .28s cubic-bezier(.2, .75, .25, 1), transform .28s ease,
              visibility 0s linear .28s; }
.tgc-hannyah.is-expanded .tgc-actions { max-height: 8rem; opacity: 1;
  visibility: visible; margin-top: .4rem; transform: none;
  transition: max-height .28s cubic-bezier(.2, .75, .25, 1), opacity .22s ease .06s,
              margin-top .28s cubic-bezier(.2, .75, .25, 1), transform .28s ease,
              visibility 0s; }

/* Agrandissement au tap : amplitude `--zoom` et origine posées par app.js selon
   la place RÉELLE autour de la carte (au bord de l'écran : zoom réduit, dirigé
   vers l'espace libre — la carte ne sort JAMAIS du cadre). Sans JS : 1.04 centré.
   Le z-index part immédiatement (transition: z-index 0s ici, délai au repli). */
.tgc-hannyah.is-expanded { transform: scale(var(--zoom, 1.04)); z-index: 30;
  will-change: transform;
  transition: transform .26s cubic-bezier(.2, .75, .25, 1), box-shadow .26s ease,
              z-index 0s; }
.tgc-hannyah.tgc-activated.tgc-act-long.is-expanded {
  box-shadow: 0 14px 34px rgba(0,0,0,.6), 0 0 30px rgba(52,199,89,.55); }
.tgc-hannyah.tgc-activated.tgc-act-short.is-expanded {
  box-shadow: 0 14px 34px rgba(0,0,0,.6), 0 0 30px rgba(226,60,60,.6); }

/* Le survol fait la même chose que le tap — SOURIS uniquement (cf. RECALIBRAGE
   point 2 : sur tactile ces règles transformaient le défilement en gigote). */
@media (hover: hover) and (pointer: fine) {
  .tgc-hannyah:hover .tgc-actions { max-height: 8rem; opacity: 1;
    visibility: visible; margin-top: .4rem; transform: none;
    transition: max-height .28s cubic-bezier(.2, .75, .25, 1), opacity .22s ease .06s,
                margin-top .28s cubic-bezier(.2, .75, .25, 1), transform .28s ease,
                visibility 0s; }
  .tgc-hannyah:hover { transform: scale(var(--zoom, 1.04)); z-index: 30;
    will-change: transform;
    transition: transform .26s cubic-bezier(.2, .75, .25, 1), box-shadow .26s ease,
                z-index 0s; }
  .tgc-hannyah.tgc-activated.tgc-act-long:hover {
    box-shadow: 0 14px 34px rgba(0,0,0,.6), 0 0 30px rgba(52,199,89,.55); }
  .tgc-hannyah.tgc-activated.tgc-act-short:hover {
    box-shadow: 0 14px 34px rgba(0,0,0,.6), 0 0 30px rgba(226,60,60,.6); }
}

/* ============================================================================
   Notifications — « qu'est-ce qui a bougé depuis ma dernière visite ? »
   ========================================================================== */
.nav-notif { position: relative; }
.notif-count { display: inline-block; min-width: 1.15rem; padding: 0 .32rem;
  margin-left: .3rem; border-radius: 999px; background: var(--short); color: #fff;
  font-size: var(--t-xs); font-weight: 800; text-align: center; vertical-align: middle; }
h1 .notif-count { font-size: var(--t-xs); vertical-align: super; }

.notif-list { list-style: none; margin: 1rem 0; padding: 0;
  display: flex; flex-direction: column; gap: .5rem; }
.notif { display: flex; align-items: flex-start; gap: .7rem;
  padding: .7rem .9rem; background: var(--card); border: 1px solid var(--line);
  border-left: 3px solid var(--line); border-radius: 10px; }
/* Non lue = pleine opacité et liseré coloré ; lue = en retrait, mais lisible. */
.notif:not(.notif-unread) { opacity: .62; }
.notif-good { border-left-color: var(--long); }
.notif-info { border-left-color: var(--accent); }
.notif-warn { border-left-color: #f0be50; }
.notif-danger { border-left-color: var(--short); background: rgba(210,60,60,.1); }
.notif-icon { font-size: var(--t-md); line-height: 1.5; width: 1.2rem; text-align: center; }
.notif-main { flex: 1; min-width: 0; }
.notif-title { font-weight: 700; }
.notif-danger .notif-title { color: #ffd9d9; }
.notif-body { font-size: .86rem; color: var(--muted); margin-top: .15rem; }
.notif-when { font-size: var(--t-xs); white-space: nowrap; }

/* Alarme de COHÉRENCE (incident BNB 03/08) : une contradiction entre ce que
   l'appli affiche et ce que l'exchange a répondu ne doit jamais être discrète. */
.alarm { display: flex; gap: .55rem; align-items: flex-start; margin: .6rem 0;
  padding: .6rem .8rem; border-radius: 10px; font-size: var(--t-md); line-height: 1.45;
  border: 1px solid transparent; }
.alarm-danger { background: rgba(210,60,60,.16); border-color: rgba(242,80,80,.5);
  color: #ffd9d9; font-weight: 600; }
.alarm-warn { background: rgba(240,190,80,.12); border-color: rgba(240,190,80,.4);
  color: #ffe4a8; }

/* Interrupteur global du bot (page Positions) */
.bot-switch.on { background: #1f7a37; border-color: #2ea043; color: #eaffea; }
.bot-switch.off { background: var(--card2); color: var(--muted); }

/* Carte INVALIDÉE (le setup est mort — invalidation ≠ SL) : signal visuel fort */
.tgc-alive .st.bad { background: rgba(210,60,60,.5); color: #ffd9d9; font-weight: 700; }
.tgc-art.tgc-invalidated { box-shadow: 0 6px 26px rgba(0,0,0,.55), 0 0 26px rgba(242,80,80,.4);
  outline: 2px solid rgba(242,80,80,.55); outline-offset: -2px; }

/* (La BRÛLURE — cérémonie du 06/08 — a été retirée le 17/08 : le moteur supprime
   les cartes mortes à la détection, la notification fait témoin.) */

/* ============================================================================
   Fiabilité & temps réel de la page Positions (chantier du 07/08/2026).
   Le principe visuel : une donnée dont on ne garantit plus la fraîcheur DOIT
   se voir. Gris + opacité réduite + mention de l'heure — jamais un chiffre net
   qui se présente comme actuel alors qu'il ne l'est plus.
   ========================================================================== */
.fresh-bar { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  margin: .2rem 0 .8rem; padding: .45rem .7rem; background: var(--card);
  border: 1px solid var(--line); border-radius: 10px; font-size: var(--t-sm); }
.fresh-bar.is-stale { border-color: #7a2f2f; background: rgba(140,50,50,.12); }
.fresh-age { color: var(--muted); font-variant-numeric: tabular-nums; }
.fresh-refresh { margin-left: auto; }
#btn-refresh.is-loading { opacity: .6; cursor: progress; }
#btn-refresh.is-loading .fleche { display: inline-block; animation: spin .8s linear infinite; }
#btn-refresh.is-error { border-color: #7a2f2f; color: var(--short); }

/* Badge d'état : Live / Retardé / Déconnecté / Données périmées. */
.fresh-badge { display: inline-flex; align-items: center; gap: .35rem;
  padding: .14rem .6rem; border-radius: 10px; font-size: var(--t-xs); font-weight: 700;
  border: 1px solid var(--line); color: var(--muted); }
.fresh-badge::before { content: ""; width: .5rem; height: .5rem; border-radius: 50%;
  background: currentColor; }
.fresh-badge[data-state="live"] { color: #7ee08a; border-color: #2f6b39;
  background: rgba(46,120,60,.16); }
.fresh-badge[data-state="live"]::before { animation: fresh-pulse 1.8s ease-in-out infinite; }
.fresh-badge[data-state="late"] { color: #e0b24a; border-color: #7a5a1f;
  background: rgba(140,100,30,.18); }
.fresh-badge[data-state="down"],
.fresh-badge[data-state="stale"] { color: var(--short); border-color: #7a2f2f;
  background: rgba(140,50,50,.18); }
@keyframes fresh-pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) {
  .fresh-badge[data-state="live"]::before { animation: none; }
}

/* Flux coupé ou snapshot en échec : plus rien ne bouge, et ça se voit.
   Règle §C3 — « pas de chiffre qui bouge alors que rien n'arrive ». */
body.feed-down .pos-card [data-cell="pnl"],
body.feed-down .pos-card [data-cell="mark"],
body.feed-down .pos-card [data-cell="liqdist"],
body.feed-down .pos-row [data-cell="pnl"],
body.feed-down .pos-row [data-cell="mark"],
body.feed-down .pos-row [data-cell="liq"],
/* La FENÊTRE DE DÉTAIL aussi (07/09, revue) : le bloc déplacé dans #pos-modal
   n'a plus d'ancêtre .pos-card — flux coupé, son PRIX gardait son style vivant
   au moment précis où l'utilisateur le lit pour décider. */
body.feed-down .pos-modal [data-cell="pnl"],
body.feed-down .pos-modal [data-cell="mark"],
body.feed-down .pos-modal [data-cell="liqdist"],
/* LES TOTAUX AUSSI (24/09) : ils s'animent désormais au même tick que les
   cartes, ils doivent donc se figer avec elles — et le montrer. */
body.feed-down #pnl-total,
body.feed-down .cj-cumuls {
  color: var(--muted) !important; opacity: .65; }
.tgc-grid.is-stale, .pos-row.is-stale, .margin-chip.is-stale { opacity: .55; }
.pos-row.is-stale td { color: var(--muted); }

/* Total de page = somme des cartes (§3.2), et son badge de divergence. */
.pnl-bar { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap;
  margin: .2rem 0 1rem; font-size: var(--t-md); }
.pnl-total strong { font-size: var(--t-lg); font-variant-numeric: tabular-nums; }
.state-warn { color: #e0b24a; border-color: #7a5a1f; background: rgba(140,100,30,.18); }
.state-bad { color: var(--short); border-color: #7a2f2f; background: rgba(140,50,50,.18); }

/* ============================================================================
   FENÊTRE DE DÉTAIL D'UNE CARTE (Djamel, 07/09 — références Tensor).
   « y aura a gauche la carte et a droite le texte ». Sur mobile la largeur
   n'existe pas : les deux colonnes s'empilent, la carte au-dessus.
   ========================================================================= */
.pos-modal { position: fixed; inset: 0; z-index: 90;
  display: flex; align-items: center; justify-content: center; padding: 2vh 2vw; }
.pos-modal[hidden] { display: none; }
.pos-modal-back { position: absolute; inset: 0; background: rgba(4,8,18,.78);
  backdrop-filter: blur(3px); }
.pos-modal-box { position: relative; z-index: 1;
  display: grid; grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  grid-template-areas: "titre titre" "carte infos";
  gap: .9rem 1.2rem;
  width: min(920px, 96vw); max-height: 92vh; overflow: auto;
  background: var(--card, #141b2e); border: 1px solid var(--line, #243049);
  border-radius: 14px; padding: 1rem 1.1rem 1.2rem;
  box-shadow: 0 18px 60px rgba(0,0,0,.6); }
.pos-modal-titre { grid-area: titre; margin: 0; font-size: var(--t-lg); padding-right: 2rem; }
.pos-modal-left { grid-area: carte; }
/* La vignette est un clone de `.tgc-abox` (entête · scène · tuiles) dans une
   enveloppe qui reprend les classes de l'<article> — `app.js` la recrée. Elle
   en tient le conteneur (cqw), la pierre (`--gem`) et l'illustration du
   timeframe, peinte par `.tf-15m .tgc-scene` : sans cet ANCÊTRE, la carte
   s'afficherait nue. */
.pos-modal-left .tgc-art { cursor: default; }
.pos-modal-left .tgc-art:hover { transform: none; }
.pos-modal-right { grid-area: infos; display: flex; flex-direction: column; gap: .7rem; }
.pos-modal-x { position: absolute; top: .5rem; right: .6rem; z-index: 2;
  background: transparent; border: 0; color: var(--dim, #94a3c4);
  font-size: 1.5rem; line-height: 1; cursor: pointer; padding: .2rem .4rem; }
.pos-modal-x:hover { color: var(--fg, #e6ebf5); }
body.modal-open { overflow: hidden; }

/* PHRASES GAMIFIÉES (lot E) : « Prêt à attaquer… » et « +X DMG sur… » — le
   rendu principal des ordres limites et de l'historique ; le tableau complet
   vit dans le volet .table-second, replié mais entier. */
.phrase-list { list-style: none; margin: .3rem 0 .6rem; padding: 0;
  display: flex; flex-direction: column; gap: .3rem; }
.phrase { font-size: var(--t-md); }
.phrase b { font-variant-numeric: tabular-nums; }
.table-second { margin: .2rem 0 1rem; }
.table-second > summary { cursor: pointer; font-size: var(--t-sm); width: fit-content; }

/* « CARTES EN JEU (4/5/2/4) » — chaque compteur dans la couleur de sa rareté,
   c'est-à-dire de son timeframe : les teintes reprennent celles des artworks
   (Nature vert · Tide bleu · Eclipse violet · Dawn or). */
.cartes-en-jeu .cj { font-weight: 900; font-variant-numeric: tabular-nums; }
.cartes-en-jeu .cj-15m { color: #4ade80; }
.cartes-en-jeu .cj-1h  { color: #60a5fa; }
.cartes-en-jeu .cj-4h  { color: #a78bfa; }
.cartes-en-jeu .cj-1d  { color: #e8b64c; }
.cartes-en-jeu .cj-x   { color: #aab6cc; }
.cartes-en-jeu .cj-cumuls { font-size: .82em; margin-left: .35rem;
  font-variant-numeric: tabular-nums; }
/* Combien de LONG, combien de SHORT (24/09) — même famille que les cumuls,
   juste avant eux : le sens se lit avant l'argent. */
.cartes-en-jeu .cj-sens { font-size: .82em; margin-left: .35rem;
  font-variant-numeric: tabular-nums; }

/* Le détail lui-même : masqué dans la carte, révélé dans la fenêtre. */
.pos-detail[hidden] { display: none; }
.pos-detail { display: flex; flex-direction: column; gap: .8rem; }
.pos-detail-facts { display: grid; grid-template-columns: auto 1fr auto 1fr;
  gap: .4rem .8rem; align-items: baseline; font-size: var(--t-sm); }
.pos-detail-facts .k { color: var(--dim2, #6b7899); font-weight: 700;
  font-size: var(--t-xs); letter-spacing: .04em; }
.pos-detail-facts b { font-variant-numeric: tabular-nums; }
/* « +3 » sur la face : le NOMBRE de marches restantes, pas une décimale du prix.
   Plus petit, plus clair, décollé — sans quoi « 6.161  +3 » se lit comme un seul
   nombre (constaté au navigateur sur la prod, 11/09/2026). */
.tgc-tile .def-plus { font-size: .82em; font-weight: 600; opacity: .6;
  margin-left: .35em; }
/* L'échelle de sortie réelle (11/09/2026) — les marches dans l'ordre où le prix
   les rencontrera. La première est mise en avant : c'est celle que la face
   annonce, et le lien entre les deux doit se voir sans le chercher. */
.pos-exits { display: flex; flex-wrap: wrap; align-items: baseline; gap: .3rem .45rem; }
.pos-exits .k { color: var(--dim2, #6b7899); font-weight: 700;
  font-size: var(--t-xs); letter-spacing: .04em; width: 100%; }
.pos-exit { font-size: var(--t-sm); font-variant-numeric: tabular-nums;
  padding: .1rem .4rem; border-radius: .35rem;
  border: 1px solid color-mix(in srgb, var(--gem, #7aa2ff) 35%, transparent);
  background: color-mix(in srgb, var(--gem, #7aa2ff) 10%, transparent); }
.pos-exit.is-next { border-color: color-mix(in srgb, var(--gem, #7aa2ff) 75%, transparent);
  font-weight: 700; }
.pos-detail .tgc-actions { flex-wrap: wrap; gap: .4rem; }
/* L'invite au clic, sur la face. */
.pos-more { margin-left: auto; opacity: .75; }
.pos-card { cursor: pointer; }

/* 08/09 (« visiblement c'est moche », capture Joker) — deux excès de carte.
   1) Les badges d'état gardaient le nowrap pensé pour les tableaux et
      débordaient du parchemin (« TROP DE DEF — 200 posés pour 60 en
      position » coupé au bord) : SUR LA CARTE ils se replient. Les tableaux,
      eux, gardent leur nowrap.
   2) Une paire chargée empilait N lignes « en attente + Annuler » sur CHAQUE
      prise : au-delà de 2, le volet .pending-fold (patron « vue tableau »). */
.tgc-ext .state { white-space: normal; }
.pending-fold summary { cursor: pointer; font-size: var(--t-xs); color: var(--muted);
  list-style: none; }
.pending-fold summary::-webkit-details-marker { display: none; }
.pending-fold[open] summary { margin-bottom: .3rem; }

/* La POPUP DE PRISE du Journal (Djamel, 08/09) : l'essentiel en grille de
   deux colonnes — marché, entrée, prix, volume, DEF — puis les boutons. */
.jrn-card { cursor: pointer; }
.take-mini { display: flex; flex-direction: column; gap: .8rem; }
.take-mini-grid { display: grid; grid-template-columns: 1fr 1fr;
  gap: .6rem .8rem; }
.take-mini-grid label { display: flex; flex-direction: column; gap: .25rem;
  margin: 0; font-size: var(--t-xs); color: var(--muted); font-weight: 600; }
.take-mini-grid input, .take-mini-grid select { width: 100%; }
@media (max-width: 480px) {
  .take-mini-grid { grid-template-columns: 1fr; }
}

@media (max-width: 720px) {
  .pos-modal-box { grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "titre" "carte" "infos"; }
  /* `max-width` seul laissait la vignette s'effondrer à 2 px (phase 3) : la
     carte est un conteneur (`container-type: inline-size`), donc SANS largeur
     intrinsèque, et un item de grille en colonne unique prend la largeur de
     son contenu. Une largeur explicite, bornée à la colonne. */
  .pos-modal-left { width: min(260px, 100%); max-width: none; margin: 0 auto; }
  .pos-detail-facts { grid-template-columns: auto 1fr; }
}
.diverg { display: inline-block; }
.diverg summary { cursor: pointer; list-style: none; }
.diverg summary::-webkit-details-marker { display: none; }
.diverg-body { margin-top: .4rem; padding: .6rem .8rem; background: var(--card2);
  border: 1px solid var(--line); border-radius: 10px; font-size: var(--t-sm);
  font-variant-numeric: tabular-nums; max-width: 42rem; }
.diverg-body p { margin: .5rem 0 0; }

/* Liquidation : la distance en % est l'information actionnable ; en dessous de
   15 %, elle passe en alerte. Le prix lui-même vient de l'exchange — jamais
   recalculé, et « — » plutôt que 0 quand il est absent (cross). */
.liq-px { font-variant-numeric: tabular-nums; }
.liq-dist { margin-left: .35rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.liq-alert { color: var(--short) !important; font-weight: 700; }
.tgc-alive .st.liq-alert { background: rgba(140,50,50,.28); }

/* Position ouverte hors dashboard : réelle, mais sans ordre local à piloter. */
.pos-card.is-orphan { opacity: .94; }
.pos-card.is-orphan .tgc-scene { filter: saturate(.75); }

/* L'EXCHANGE QUI PORTE LA POSITION (15/09) : une teinte PAR exchange — depuis
   KuCoin, deux comptes coexistent et les cartes se ressemblaient. Le bleu MEXC
   et le vert KuCoin se distinguent d'un coup d'œil, même en périphérie. */
.pos-cex { font-weight: 700; letter-spacing: .04em; }
.pos-cex-mexc { background: rgba(0, 82, 255, .18); color: #7ea8ff;
  border: 1px solid rgba(0, 82, 255, .35); }
.pos-cex-kucoin { background: rgba(35, 175, 145, .16); color: #4fd6b8;
  border: 1px solid rgba(35, 175, 145, .4); }
/* Dans les faits de la popup, la même teinte SANS pastille (c'est un <b>). */
b.pos-cex-mexc, b.pos-cex-kucoin { background: none; border: none; }
b.pos-cex-mexc { color: #7ea8ff; }
b.pos-cex-kucoin { color: #4fd6b8; }

/* ------------------------------------------------------------------ Agents
   Le bandeau d'arrêt doit se voir SANS le chercher : quand l'automatisation est
   coupée, la carte entière vire au rouge sourd. Un état aussi lourd de sens que
   « plus aucun bot n'agit » ne se signale pas par une ligne de texte discrète. */
.kill-on { border-color: var(--short); background: rgba(140, 50, 50, .18); }

/* Les étapes du bot dans le formulaire de création : encadrées, pour qu'on lise
   E1/E2/E3/E4 comme quatre décisions distinctes et non comme un long formulaire. */
fieldset { border: 1px solid var(--line); border-radius: 8px;
           padding: .7rem 1rem 1rem; margin: 1rem 0; }
fieldset legend { color: var(--muted); font-size: var(--t-sm); padding: 0 .4rem; }
.check-list { display: flex; flex-wrap: wrap; gap: .8rem; margin: .5rem 0; }

/* Un run se déplie : la liste reste lisible à cent runs, le détail est à un clic. */
details.run { border: 1px solid var(--line); border-radius: 6px;
              padding: .5rem .8rem; margin: .5rem 0; }
details.run summary { cursor: pointer; font-variant-numeric: tabular-nums; }
details.run[open] summary { margin-bottom: .6rem; }
details.run h3 { font-size: var(--t-md); color: var(--muted); margin: .8rem 0 .3rem; }

/* Un bot sans aucune étape active n'est pas en panne : c'est une coquille qu'on
   arme plus tard. Mais une case vide dans la colonne « étapes » ressemble à un bot
   normal — d'où ce badge, qui dit l'état au lieu de le laisser deviner. */
.badge-inerte { border: 1px solid var(--line); border-radius: 4px;
                padding: .05rem .35rem; font-size: var(--t-xs); letter-spacing: .04em; }
.preflight { margin: .4rem 0 0; padding-left: 1.1rem; }
.preflight li { margin-bottom: .5rem; }

/* (Les styles de la colonne « Positions » des Agents — .pos-ligne,
   .badge-attente, .badge-orphelin — sont partis avec elle le 15/09 :
   retirée sur demande Djamel, le détail vit sur la page Positions.) */

/* REPÈRE HORS ÉCHELLE (12/08/2026). L'invalidation de THOR tombe au-delà du
   dernier TP sur 198 cartes sur 198 — ce n'est pas un cas limite, c'est sa
   sémantique : « l'occasion s'est évaporée », pas « stop-loss ». La plaquer à
   100 % la superposait à TP3 en une bouillie illisible, et l'écran affirmait une
   invalidation à hauteur de la cible. On la décale et on la marque d'un chevron :
   le repère est AU BORD, sa valeur est AILLEURS. */
/* Depuis la refonte du 08/09 le repère est une GEMME, plus un trait : le
   `border-left` en pointillés d'avant serait une barre en biais au travers de
   la pierre. La gemme hors échelle se lit maintenant comme une pierre NON
   SERTIE — creuse, éteinte, sans éclat — face aux pierres pleines des bornes
   réelles. Le chevron du libellé continue de dire « sa valeur est ailleurs ». */
.tgc-mark.m-hors { opacity: .5; background: transparent;
  box-shadow: inset 0 0 0 1px currentColor, 0 1px 2px rgba(0,0,0,.5); }
.tgc-marklabel.m-hors { opacity: .6; font-style: italic;
  transform: translateX(-100%) translateY(.65rem); }

/* ---- Anti-scoliose (01/09/2026) : volets repliés + cran « Compact ». Les
   cartes étant en container queries (cqw), resserrer la COLONNE suffit à tout
   rétrécir — aucun style de carte à dupliquer, la leçon carte EX est sauve. */
html.dense .tgc-grid { grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); }
/* COMPACT = DEUX COLONNES SUR TÉLÉPHONE, TOUJOURS (Djamel, 25/09 : « sur
   téléphone on ne se rend même pas compte de la différence »). Sur Signaux,
   les deux rails du classeur mangent ~106 px : il restait 284 px, moins que
   2 × 158 — la grille retombait sur UNE colonne, exactement comme en mode
   normal. En dessous de 720 px, le compact ne négocie plus : deux colonnes,
   quelle que soit la place laissée par la page. */
@media (max-width: 720px) {
  html.dense .tgc-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .45rem; }
}
/* Le Compact n'a plus de réglage propre aux cartes depuis l'habillage néon
   (23/09) : le cartouche ne recouvre plus l'illustration, il n'y a plus de
   débordement à rattraper à 158 px — les planchers en px des tuiles suffisent. */
.fold-toolbar { display: flex; gap: .5rem; margin: .4rem 0 .6rem; }
.pilote-bar { display: flex; flex-wrap: wrap; gap: .45rem; align-items: center;
  padding: .35rem .5rem; margin: .2rem 0 .4rem; }

/* ---- La barre de pilotage TIENT SUR LA BANDE (01/09/2026). Hors du details
   (sinon invisible volet ferme), hors du summary (sinon chaque clic toggle) :
   elle survole la ligne de bande, a droite. Sur ecran etroit elle redescend
   sous la bande — mieux vaut deux lignes que des champs inatteignables. */
.pair-wrap { position: relative; }
/* v2 (01/09, apres la version cassee livree sans verification visuelle —
   lecon retenue) : barre MINIMALE centree verticalement sur la ligne de
   bande, summary qui RESERVE la place. En etroit : sous la bande. */
/* Ancre EN HAUT (pas top:50% : volet ouvert, la barre se centrait sur tout
   le groupe et flottait a cote des cartes — vu a la verification visuelle). */
.pair-wrap > .pilote-onband { position: absolute; top: .3rem; right: .4rem;
  margin: 0; padding: 0 .3rem; z-index: 2;
  flex-wrap: nowrap; white-space: nowrap; }
/* 14/09 : la barre ne porte plus qu'AUTOMATION + les chips — le summary ne
   réserve plus que ça (l'ancien formulaire en ligne exigeait 30rem : c'est
   précisément « ça commence à prendre beaucoup d'espace sur la barre »). */
.pair-wrap > .pair-group > summary { padding-right: 16rem; min-height: 1.9rem; }
@media (max-width: 1100px) {
  .pair-wrap > .pilote-onband { position: static; transform: none;
    flex-wrap: wrap; white-space: normal; padding: .2rem .4rem; }
  .pair-wrap > .pair-group > summary { padding-right: 1rem; }
}

/* ---- AUTOMATION (14/09/2026) : la popup de création de bot. Un <details>
   natif — elle marche SANS JS — dont le panneau s'ancre sous le bouton, à
   droite, au-dessus des cartes. Grille deux colonnes façon .take-mini-grid. */
.auto-pop { position: relative; display: inline-block; }
/* POPUP OUVERTE = SA barre passe DEVANT (15/09, capture Djamel : les boutons
   AUTOMATION d'AVAX et BNB flottaient PAR-DESSUS le panneau d'ADA). Chaque
   barre est un contexte d'empilement à z-index 2 : le z-index 40 du panneau ne
   vaut QUE dans sa barre, et les barres SUIVANTES du DOM repassent devant à
   égalité. 35 = au-dessus des cartes zoomées du Journal (30), sous la topbar
   collante (40). `:has()` : Chrome/Edge/Safari depuis 2022-2023, et sans lui
   on retombe sur le défaut d'avant — jamais pire. */
.pair-wrap > .pilote-onband:has(.auto-pop[open]) { z-index: 35; }
.auto-pop > summary { list-style: none; cursor: pointer; font-size: var(--t-xs);
  letter-spacing: .04em; }
.auto-pop > summary::-webkit-details-marker { display: none; }
.auto-pop[open] > summary { opacity: .75; }
.auto-pop-body { position: absolute; right: 0; top: calc(100% + .4rem);
  z-index: 40; width: min(24rem, 92vw); padding: .8rem .9rem;
  background: var(--card2); border: 1px solid var(--line);
  border-radius: 12px; box-shadow: 0 10px 30px rgba(0, 0, 0, .45);
  white-space: normal; text-align: left; }
.auto-pop-grid { display: grid; grid-template-columns: 1fr 1fr;
  gap: .5rem .7rem; margin-bottom: .6rem; }
.auto-pop-grid label { display: flex; flex-direction: column; gap: .25rem;
  margin: 0; font-size: var(--t-xs); color: var(--muted); font-weight: 600; }
.auto-pop-grid input, .auto-pop-grid select { width: 100%;
  box-sizing: border-box; }
.auto-pop-grid button { align-self: end; }
.auto-pop-tfs { display: grid; grid-template-columns: 1fr 1fr;
  gap: .45rem .7rem; margin-bottom: .6rem; }
.auto-pop-tf { display: flex !important; align-items: center; gap: .35rem;
  font-size: var(--t-sm); margin: 0; }
/* Le width:100% global des inputs de formulaire ÉTIRAIT la case à 117 px et
   écrasait le champ contrats à 18 px (vu à la vérification visuelle) : la
   case reprend sa taille naturelle, le champ prend le reste de la ligne. */
.auto-pop-tf input[type="checkbox"] { margin: 0; width: auto; flex: 0 0 auto; }
.auto-pop-tf input[type="text"] { flex: 1 1 auto; min-width: 0; width: auto;
  box-sizing: border-box; }
.auto-pop-note { font-size: var(--t-xs); margin: .2rem 0 0; }
.auto-chip { text-decoration: none; }
@media (max-width: 1100px) {
  /* La barre est en flux à cette largeur : le bouton est à GAUCHE, le panneau
     s'ancre donc à gauche — ancré à droite il sortirait de l'écran. */
  .auto-pop-body { right: auto; left: 0; }
}
/* 720 et non 640 : LE breakpoint mobile du site (harmonisation du 14/09 — à
   700 px la popup restait en deux colonnes pendant que tout le reste de la
   page passait déjà en mode téléphone). */
@media (max-width: 720px) {
  .pilote-onband > .btn, .pilote-onband > .st,
  .auto-pop > summary { font-size: var(--t-sm) !important; }
  .auto-pop-grid, .auto-pop-tfs { grid-template-columns: 1fr; }
}

/* ======================= LE CLASSEUR (Signaux, 22/09) ========================
   Djamel : « met des onglets sur les côtés avec le jeton dessus […] ALL puis
   toutes les paires ; à droite pareil avec 5 cases ALL 15m 1h 4h 1d ». Deux
   rails FIXÉS sous la barre du haut (hauteur mesurée par classeur.js), onglets
   de classeur qui dépassent du bord : arrondis côté page, collés côté écran.
   Multi-sélection : un onglet allumé (.is-on) s'avance vers le contenu. */
.classeur { position: fixed; top: calc(var(--classeur-top, 56px) + 10px); z-index: 35;
  display: flex; flex-direction: column; gap: 4px; padding: 6px 0;
  max-height: calc(100vh - var(--classeur-top, 56px) - 22px);
  overflow-y: auto; overflow-x: hidden; scrollbar-width: none; }
.classeur::-webkit-scrollbar { display: none; }
.classeur-jetons { left: 0; align-items: flex-start; }
.classeur-tf { right: 0; align-items: flex-end; }
.onglet { display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; width: 58px; min-height: 54px; padding: 5px 4px; cursor: pointer;
  border: 1px solid var(--line); background: rgba(15,23,44,.94); color: var(--muted);
  font: 700 .6rem/1 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  letter-spacing: .3px; text-transform: uppercase; box-shadow: var(--shadow);
  transition: transform var(--t), color var(--t), background var(--t), border-color var(--t); }
.classeur-jetons .onglet { border-left: 0; border-radius: 0 12px 12px 0; }
.classeur-tf .onglet { border-right: 0; border-radius: 12px 0 0 12px; }
.onglet img, .onglet-piece { width: 28px; height: 28px; border-radius: 50%; object-fit: cover;
  box-shadow: 0 0 0 2px rgba(248,214,117,.3); }
.onglet-piece { display: flex; align-items: center; justify-content: center; font-size: .48rem;
  color: #6b4a10; letter-spacing: 0;
  background: radial-gradient(circle at 32% 28%, #fff3c0 0%, #f8d675 30%, #e3ab35 62%, #b57b16 88%, #93600e 100%); }
.onglet-nom { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.onglet-all { min-height: 40px; font-size: .72rem; letter-spacing: .7px; }
.onglet:hover { color: var(--fg); background: var(--card2); }
.onglet.is-on { color: var(--fg); background: var(--card2); border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent), var(--shadow); }
.onglet.is-on img, .onglet.is-on .onglet-piece { box-shadow: 0 0 0 2px var(--accent); }
.classeur-jetons .onglet.is-on { transform: translateX(5px); }
.classeur-tf .onglet.is-on { transform: translateX(-5px); }
/* COLLÉS AU CADRE (Djamel, 25/09 : « la barre est tout à gauche ou tout à
   droite, faudrait qu'elle soit collée au cadre principal »). Sur un écran
   large, les rails quittaient le bord de l'écran pour flotter à 20 cm du
   contenu. Dès qu'il y a la place (main = 1040 px + deux rails), ils
   s'alignent sur les bords du CADRE : 50 % − 520 px (demi-cadre) + 16 px
   (son padding) − 58 px (un onglet) − 12 px d'air = 50 % − 574 px (l'onglet
   allumé avance de 5 px vers le contenu, il reste 7 px d'air). L'onglet
   se retourne : plat contre le contenu, arrondi vers l'extérieur. En
   dessous de 1200 px, rien ne change : collés à l'écran, `main` leur a
   déjà réservé sa marge. */
@media (min-width: 1201px) {
  .classeur-jetons { left: max(0px, calc(50% - 574px)); align-items: flex-end; }
  .classeur-tf { right: max(0px, calc(50% - 574px)); align-items: flex-start; }
  .classeur-jetons .onglet { border-left: 1px solid var(--line); border-right: 0;
    border-radius: 12px 0 0 12px; }
  .classeur-tf .onglet { border-right: 1px solid var(--line); border-left: 0;
    border-radius: 0 12px 12px 0; }
}
.classeur-hint { font-size: .78rem; align-self: center; }
.classeur-vide { margin: 1rem 0; }
/* Le masquage par `hidden` doit GAGNER sur les display des cartes/groupes. */
details.pair-group[hidden], section.lifecycle-section[hidden], .tgc-art[hidden],
tr[hidden] { display: none !important; }
/* main est centré à 1040 px : au-delà de ~1200 px les rails vivent dans la marge
   libre ; en dessous, la page leur fait de la place. */
@media (max-width: 1200px) { body.avec-classeur main { padding-left: 76px; padding-right: 76px; } }
@media (max-width: 720px) {
  /* LE BAS DU RAIL PASSAIT SOUS LA BARRE D'ONGLETS (Djamel, 25/09 : « XRP et
     WIF s'affichent mal, gênés par la case Signaux »). Deux causes : la
     hauteur du rail ignorait la barre du bas (phase 2), et `100vh` sur iPhone
     est la GRANDE fenêtre (barre Safari repliée) — le rail débordait de
     l'écran visible. `100dvh` suit la fenêtre réelle ; on retire la barre du
     bas et l'encoche. Les derniers jetons se lisent sans tirer. */
  .classeur { max-height: calc(100vh - var(--classeur-top, 56px) - 22px - 70px);
    max-height: calc(100dvh - var(--classeur-top, 56px) - 22px - 70px - env(safe-area-inset-bottom));
    padding-bottom: 8px; }
  .onglet { width: 46px; min-height: 46px; font-size: .52rem; padding: 4px 2px; gap: 2px; }
  .onglet img, .onglet-piece { width: 22px; height: 22px; }
  .onglet-all { min-height: 36px; font-size: .62rem; }
  body.avec-classeur main { padding-left: 52px; padding-right: 52px; }
}
/* LE DUO PHARE SOL·BTC (23/09) : deux pièces entrelacées sous ALL. Le dessin
   vit dans le SVG (couches, halos, anneaux aux couleurs des deux marques) ;
   ici, seulement sa taille — au même gabarit que les pièces simples. */
.duo-entrelac { width: 44px; height: 28px; display: block; }
@media (max-width: 720px) { .duo-entrelac { width: 35px; height: 22px; } }
.onglet-duo.is-on .duo-entrelac { filter: drop-shadow(0 0 3px var(--accent)); }
/* Les timeframes s'écrivent « 15m » partout dans l'app — pas en capitales. */
.onglet-tf { text-transform: none; font-size: .68rem; }

/* MODIFIER LA PUISSANCE D'ATTAQUE (Agents, 22/09) : volet compact dans la
   cellule des volumes — un bouton, puis les montants par timeframe. */
.vol-edit { margin-top: .35rem; }
.vol-edit > summary { list-style: none; display: inline-block; cursor: pointer;
  padding: .22rem .55rem; font-size: var(--t-xs); }
.vol-edit > summary::-webkit-details-marker { display: none; }
.vol-edit-form { display: flex; flex-direction: column; gap: .3rem; margin-top: .4rem;
  padding: .5rem; background: var(--card2); border: 1px solid var(--line);
  border-radius: 8px; min-width: 140px; }
.vol-edit-form label { display: flex; align-items: center; justify-content: space-between;
  gap: .4rem; margin: 0; font-size: var(--t-xs); color: var(--muted); }
.vol-edit-form input { width: 4.4rem; padding: .22rem .4rem; font-size: var(--t-sm); }
.vol-edit-form .btn { padding: .3rem .6rem; font-size: var(--t-sm); margin-top: .2rem; }
.vol-edit-form .vol-edit-check { justify-content: flex-start; gap: .35rem; }
/* L'unité à côté de chaque case du volet (26/09) : « c » contrats, « u » unités. */
.vol-edit-form .vol-unit-wrap { display: inline-flex; align-items: center; gap: .25rem; }
.vol-edit-form .vol-unit { color: var(--muted); font-size: var(--t-xs); }
/* Ce que le bot détient, une ligne par sens (26/09) — colonne Positions d'Agents. */
.bot-pos { white-space: nowrap; font-size: var(--t-sm); }
/* Au comptant, levier et marge ne partent pas : grisés, pas cachés (les valeurs restent). */
.spot-off { opacity: .45; }
.vol-edit-form .vol-edit-check input { width: auto; }

/* ===== LE SÉLECTEUR DE COMPTE — page Positions (24/09/2026) =====
   REMPLACE le rail d'onglets du 23/09, dont le commentaire disait : « des
   onglets en ligne, pas un menu déroulant. Un déroulant cache le nombre de
   comptes et demande deux gestes ». L'usage a tranché contre lui, et pour deux
   raisons que ce raisonnement n'avait pas vues :
     · les onglets étaient une MULTI-SÉLECTION à bascule — cliquer un deuxième
       compte l'ajoutait au lieu de remplacer, et les chiffres du haut de page
       additionnaient alors deux comptes sans prévenir ;
     · « lisible d'un coup » ne tient qu'à deux ou trois comptes. Djamel en
       annonce quinze : le rail s'enroulait déjà sur trois lignes de téléphone,
       et isoler le quinzième aurait demandé quatorze clics d'extinction.
   Le déroulant, lui, ne cache plus rien : chaque ligne porte son compte, son
   exchange, ses prises, ses ordres en attente et son état.
   Le squelette (`.acct-form`) existait déjà, stylé et câblé, inutilisé. */
.acct-form.vue-positions { margin: 0 0 .8rem; padding: 0; border-bottom: 0;
  gap: .5rem .8rem; align-items: center; }
.acct-form.vue-positions label { flex-direction: row; align-items: center; gap: .4rem; }
.acct-form.vue-positions select { min-width: 0; max-width: 100%;
  padding: .28rem .5rem; font-size: var(--t-sm); }
/* Sur téléphone : chaque champ prend la ligne entière, le libellé devant. Sans
   cette règle, le `min-width: 220px` de `.acct-form select` débordait. */
@media (max-width: 720px) {
  /* Phase 3 : Compte et Classer CÔTE À CÔTE (libellé au-dessus) — deux
     rangs de moins avant la première carte. */
  .acct-form.vue-positions { gap: .4rem .6rem; }
  .acct-form.vue-positions label { flex: 1 1 40%; min-width: 0; flex-direction: column;
    align-items: stretch; gap: .15rem; }
  .acct-form.vue-positions label select { flex: 1; }
}

/* ===== LE REGROUPEMENT PAR JETON — page Positions (24/09/2026) =====
   « On doit savoir y a combien de long combien de jup. » L'en-tête de groupe
   répond en chiffres ; il reste DISCRET (pas un titre de section) pour ne pas
   repousser les cartes vers le bas — c'est la même page qui se plaint déjà de
   n'en montrer qu'une par écran de téléphone. */
.groupe-jeton { display: flex; flex-wrap: wrap; align-items: baseline;
  gap: .2rem .6rem; margin: 1rem 0 .35rem; font-size: var(--t-sm);
  font-weight: 700; letter-spacing: .2px;
  font-variant-numeric: tabular-nums; }
.groupe-jeton .gj-nom { font-size: var(--t-md); font-weight: 900; }
.groupe-jeton .gj-n { color: var(--muted); font-weight: 600; }
.groupe-jeton .state { font-weight: 600; }

/* MON ACCÈS THOR — page Comptes (lot 1 multi-utilisateurs, 23/09/2026). */
.thor-acces { margin: 0 0 1.4rem; }
.thor-acces .thor-form { display: flex; flex-wrap: wrap; gap: .5rem .8rem; align-items: flex-end; }
.thor-acces .thor-form label { display: flex; flex-direction: column; gap: .2rem;
  font-size: var(--t-sm); color: var(--muted); }
.thor-acces .thor-form input { min-width: 15rem; max-width: 100%; }
.thor-acces strong.ok { color: #5fd08a; }
.thor-acces strong.bad { color: #ff7b7b; }
.thor-acces strong.warn { color: #e8c777; }

/* INSCRIPTION (lot 2, 23/09/2026) : chaque champ dit pourquoi on le demande. */
.inscription form label { display: flex; flex-direction: column; gap: .25rem; }
.inscription .aide { font-size: var(--t-xs); color: var(--muted); line-height: 1.35; }

/* Bandeaux multilignes (lots 3-4 i18n) : un compte rendu (changement de volume,
   alertes moteur) arrive en LIGNES séparées ; sans pre-line, le bandeau et la
   page Notifications les écrasaient en une seule phrase. */
.banner, .notif-body { white-space: pre-line; }

/* ===================================================================== TIROIRS
   Page Positions (24/09) : limites, historique, erreurs et désynchros quittent
   le bas de page pour des tiroirs `popover` (le pourquoi complet est dans
   trades.html). Une rangée de pastilles chiffrées, puis un panneau qui glisse
   depuis la DROITE sur ordinateur — la page reste visible à gauche, on compare
   sans perdre sa place — et depuis le BAS sur téléphone, où la largeur manque. */
.tiroirs { display: flex; flex-wrap: wrap; gap: .5rem; margin: .2rem 0 1.1rem; }
.tiroir-btn { display: inline-flex; align-items: center; gap: .45rem; cursor: pointer;
  padding: .42rem .55rem .42rem .8rem; border-radius: 999px; font: inherit; font-size: var(--t-sm);
  color: var(--fg); background: rgba(24,34,60,.78); border: 1px solid var(--line);
  transition: border-color var(--t), background var(--t), transform var(--t); }
.tiroir-btn:hover { border-color: var(--accent); background: var(--card2); }
.tiroir-btn:active { transform: translateY(1px); }
.tiroir-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tiroir-btn b { min-width: 1.7em; padding: .05rem .45rem; border-radius: 999px; text-align: center;
  font-size: var(--t-sm); background: rgba(56,189,248,.16); color: var(--accent); }
/* À zéro, la pastille reste à sa place, mais se fait discrète : rien n'y attend. */
.tiroir-btn.is-vide { color: var(--muted); }
.tiroir-btn.is-vide b { background: rgba(139,155,192,.12); color: var(--muted); }
/* Erreurs présentes, désynchro : l'anomalie se voit sans ouvrir le tiroir. */
.tiroir-btn.is-alerte { border-color: rgba(242,109,109,.55); }
.tiroir-btn.is-alerte b { background: rgba(242,109,109,.18); color: var(--short); }

/* Le panneau. Le style par défaut du navigateur pour [popover] (centré, bord
   noir, fond Canvas blanc) est entièrement remplacé. Tant qu'il est FERMÉ, on
   n'y touche pas : le navigateur le cache (display:none) — et s'il ignore
   `popover`, les listes restent en ligne, lisibles, à leur ancienne place. */
.tiroir:popover-open { position: fixed; inset: 0 0 0 auto; margin: 0; padding: 0;
  width: min(760px, 100vw); height: 100dvh; max-height: none;
  display: flex; flex-direction: column; overflow: hidden;
  color: var(--fg); background: #0d1529; border: 0; border-left: 1px solid var(--line);
  box-shadow: -18px 0 48px rgba(0,0,0,.55); animation: tiroir-droite .2s ease-out; }
.tiroir::backdrop { background: rgba(4,8,18,.5); backdrop-filter: blur(2px); }
@keyframes tiroir-droite { from { transform: translateX(32px); opacity: 0; } }
@keyframes tiroir-bas { from { transform: translateY(32px); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .tiroir:popover-open { animation: none; } }
.tiroir-tete { display: flex; align-items: center; gap: .8rem; flex: none;
  padding: .85rem 1rem .75rem 1.2rem; border-bottom: 1px solid var(--line);
  background: rgba(17,26,48,.96); }
.tiroir-tete h2 { margin: 0; flex: 1; font-size: var(--t-lg); }
.tiroir-fermer { flex: none; width: var(--tap); height: var(--tap); padding: 0; border-radius: 50%; cursor: pointer;
  font: inherit; font-size: var(--t-md); line-height: 1; color: var(--muted);
  background: transparent; border: 1px solid var(--line); transition: color var(--t), border-color var(--t); }
.tiroir-fermer:hover, .tiroir-fermer:focus-visible { color: var(--fg); border-color: var(--accent); outline: none; }
/* Le corps défile SEUL, l'en-tête (titre + ✕) reste en vue. Les tableaux
   larges (9 colonnes) défilent aussi en travers, sans élargir le panneau. */
.tiroir-corps { flex: 1; overflow: auto; overscroll-behavior: contain;
  padding: .7rem 1.2rem 1.6rem; }
.tiroir-corps > p.muted:first-child { margin-top: .2rem; font-size: var(--t-sm); }
.tiroir-corps .phrase-list { margin-top: .4rem; }
.tiroir-corps table.grid { min-width: 640px; }
/* Le compte rendu de l'action qui vient d'être prise DANS ce tiroir (copie du
   bandeau `?msg=`, posée par app.js) : sous le titre, hors du corps qui
   défile — il reste en vue pendant qu'on descend la liste. */
.tiroir-msg { flex: none; margin: .6rem 1.2rem 0; max-width: none; text-align: left; }
/* Navigateur trop ancien pour `popover` : il n'en cache rien, les listes
   s'affichent en ligne comme avant — les pastilles et le ✕, qui n'y
   ouvriraient ni ne fermeraient rien, s'effacent. */
@supports not selector(:popover-open) {
  .tiroir { margin-top: 2rem; }
  .tiroir-fermer, .tiroirs { display: none; }
}
@media (max-width: 720px) {
  .tiroir:popover-open { inset: auto 0 0 0; width: 100vw; height: auto; max-height: 88dvh;
    border-left: 0; border-top: 1px solid var(--line); border-radius: 16px 16px 0 0;
    box-shadow: 0 -18px 48px rgba(0,0,0,.55); animation-name: tiroir-bas; }
  .tiroir-tete { padding: .75rem .9rem .65rem 1rem; }
  .tiroir-corps { padding: .6rem .9rem 1.4rem; }
  .tiroir-btn { font-size: var(--t-sm); padding: .38rem .5rem .38rem .7rem; }
  /* Sur téléphone, `table.grid` défile déjà en travers dans SA boîte : une
     largeur minimale en plus imposait un second défilement imbriqué, et les
     boutons Retirer/Détail restaient hors de l'écran. */
  .tiroir-corps table.grid { min-width: 0; }
  .tiroir-msg { margin: .5rem .9rem 0; }
}

/* ==================================================== AGENTS — PAR COMPTE
   (24/09) Chaque compte sous sa bande : l'exchange dans sa teinte (la même que
   les cartes de Positions), le nom, puis SES chiffres. Un filet coloré à gauche
   court le long de ses lignes — deux comptes ne se lisent jamais comme une
   seule liste. */
.vue-agents { margin-top: 1rem; }
.vue-agents-note { font-size: var(--t-sm); }
table.grid tbody.bot-groupe + tbody.bot-groupe .bot-groupe-tete th { border-top: 14px solid transparent; background-clip: padding-box; }
table.grid .bot-groupe-tete th { text-align: left; font-weight: 400; font-size: var(--t-md); letter-spacing: 0;
  text-transform: none; padding: .6rem .8rem; color: var(--fg);
  background: linear-gradient(90deg, rgba(56,189,248,.10), rgba(24,34,60,.35) 60%);
  border-left: 3px solid var(--accent); }
.bot-groupe-tete .pos-cex { font-size: var(--t-xs); padding: .08rem .38rem; border-radius: 4px; margin-right: .35rem; }
.bot-groupe-chiffres { margin-left: .7rem; font-size: var(--t-sm); color: var(--muted); }
.bot-groupe-plus { float: right; font-size: var(--t-sm); padding: .1rem .55rem; border-radius: 999px;
  border: 1px solid var(--line); }
.bot-groupe-plus:hover { border-color: var(--accent); text-decoration: none; }
table.grid .bot-groupe-tete.is-eteint th { opacity: .75; border-left-color: var(--muted);
  background: rgba(24,34,60,.35); }
tbody.bot-groupe td:first-child { border-left: 3px solid rgba(56,189,248,.25); }
/* Une ligne de bot se lit de HAUT en bas : centrées, les cellules courtes
   flottaient au milieu d'une ligne que le Rythme (24 créneaux) rendait haute. */
tbody.bot-groupe td { vertical-align: top; }
/* Le Rythme (« à chaque clôture 1h UTC — 00h, 01h… ») : sans plancher, la
   colonne se réduisait à un mot par ligne et étirait toute la rangée. */
tbody.bot-groupe td:nth-child(6) { min-width: 9rem; }

/* ✕ d'annulation au bout d'une phrase « Prêt à attaquer » (25/09) : discret au
   repos, rouge au survol — c'est un ordre réel qu'on retire. */
.phrase-annuler { display: inline; margin-left: .35rem; }
.phrase-x { font: inherit; font-size: var(--t-sm); line-height: 1; cursor: pointer;
  width: 1.7rem; height: 1.7rem; min-height: 0; border-radius: 50%; padding: 0; vertical-align: middle;
  color: var(--muted); background: transparent; border: 1px solid var(--line);
  transition: color var(--t), border-color var(--t), background var(--t); }
.phrase-x:hover, .phrase-x:focus-visible { color: #fff; background: rgba(242,109,109,.25);
  border-color: var(--short); outline: none; }
/* Les ordres au carnet, dans la fenêtre de détail (déplacés de la face). */
.pos-carnet { margin-top: .6rem; display: flex; flex-direction: column; gap: .3rem; }
.pos-carnet .pending-orders form { display: flex; align-items: center; gap: .4rem; margin: .15rem 0; }

/* Paire croisée (SOL/BTC, 25/09) : la mention qui remplace les boutons. */
.tgc-lecture-seule { font-size: .68rem; font-size: max(9px, 4cqw); font-style: italic; }

/* Jetons pliables (Positions, 25/09) : l'en-tête EST le bouton. Le chevron dit
   l'état ; replié, les chiffres du jeton restent lisibles sur la ligne. */
.groupes-toolbar { margin: .4rem 0 0; }
details.groupe-pli > summary.groupe-jeton { cursor: pointer; list-style: none;
  padding: .35rem .5rem; border-radius: 8px; transition: background var(--t); }
details.groupe-pli > summary.groupe-jeton::-webkit-details-marker { display: none; }
details.groupe-pli > summary.groupe-jeton::before { content: "▸"; color: var(--muted);
  display: inline-block; transition: transform var(--t); margin-right: .1rem; }
details.groupe-pli[open] > summary.groupe-jeton::before { transform: rotate(90deg); }
details.groupe-pli > summary.groupe-jeton:hover { background: rgba(255,255,255,.04); }
details.groupe-pli:not([open]) > summary.groupe-jeton { opacity: .85; }


/* ============================================ BARRE D'ONGLETS — téléphone (phase 2)
   Sur un écran de 390 px, la barre du haut mangeait trois rangs (marque, puis
   sept liens sur deux lignes) et restait collée en défilant : plus d'un
   quart de l'écran avant la première carte. Les applis que Djamel utilise
   tous les jours (Binance, Telegram…) posent leurs quatre ou cinq
   destinations EN BAS, sous le pouce. On fait pareil : Signaux · Journal ·
   Positions · Agents · Plus. Tout le reste (Comptes, Risque, Ce qui a bougé,
   FR/EN, le compte, la déconnexion) est dans « Plus », une feuille qui monte
   du bas — un `popover` natif, donc SANS JavaScript (règle de la maison : le
   site marche sans lui) et hors de <main> (le rafraîchissement automatique ne
   la balaie pas).
   Ordinateur : rien ne change, la barre et la feuille n'existent pas. */
.tabbar, .menu-plus { display: none; }
@media (max-width: 720px) {
  .tabbar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 40; display: flex;
    padding: .2rem .2rem calc(.2rem + env(safe-area-inset-bottom));
    background: rgba(13,21,41,.94); backdrop-filter: blur(10px);
    border-top: 1px solid var(--line); }
  .tabbar a, .tabbar .tab-plus { position: relative; flex: 1 1 0; min-width: 0;
    transition: color var(--t), background var(--t), transform .12s ease;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
    min-height: 50px; padding: .2rem 0; border: 0; border-radius: 10px; background: none;
    color: var(--muted); font: inherit; font-size: var(--t-xs); font-weight: 600; line-height: 1.1;
    text-decoration: none; box-shadow: none; }
  .tabbar .tab-ico { width: 24px; height: 24px; display: block;
    transition: color var(--t), filter var(--t); }
  .tabbar a:active, .tabbar .tab-plus:active { transform: scale(.94); }
  .tabbar a:hover, .tabbar .tab-plus:hover { text-decoration: none; opacity: 1; box-shadow: none; }
  .tabbar a.active { color: var(--fg); background: var(--card2); }
  /* Onglet actif : le trait s'épaissit et prend la lueur cyan de la charte. */
  .tabbar a.active .tab-ico { color: var(--accent); stroke-width: 2.2;
    filter: drop-shadow(0 0 5px rgba(56,189,248,.65)); }
  /* Le compteur des notifications suit « Plus », puisque la cloche y est. */
  .tabbar .notif-count { position: absolute; top: 3px; left: calc(50% + 6px); }
  /* La feuille « Plus ». Même geste que les tiroirs : elle monte du bas. */
  .menu-plus:popover-open { position: fixed; inset: auto 0 0 0; margin: 0; display: block;
    width: 100vw; max-height: 85dvh; overflow: auto; overscroll-behavior: contain;
    padding: .5rem .6rem calc(.8rem + env(safe-area-inset-bottom));
    border: 0; border-top: 1px solid var(--line); border-radius: 16px 16px 0 0;
    color: var(--fg); background: #0d1529; box-shadow: 0 -18px 48px rgba(0,0,0,.55);
    animation: tiroir-bas .2s ease-out; }
  .menu-plus::backdrop { background: rgba(4,8,18,.5); backdrop-filter: blur(2px); }
  .menu-plus .menu-tete { display: flex; align-items: center; gap: .6rem;
    padding: .2rem .4rem .4rem .8rem; color: var(--muted); font-size: var(--t-sm); }
  .menu-plus .menu-tete .qui { max-width: 60vw; }
  .menu-plus .menu-tete .tiroir-fermer { margin-left: auto; }
  .menu-plus a { display: flex; align-items: center; gap: .7rem; min-height: 48px;
    padding: 0 .8rem; border-radius: 10px; color: var(--fg); font-size: var(--t-md); }
  .menu-plus a:hover, .menu-plus a.active { background: var(--card2); text-decoration: none; }
  .menu-plus .tab-ico { width: 22px; height: 22px; flex: none; color: var(--muted); }
  .menu-plus a.active .tab-ico { color: var(--accent); }
  .menu-plus .menu-pied { display: flex; align-items: center; justify-content: space-between;
    gap: .6rem; margin-top: .4rem; padding: .5rem .8rem 0; border-top: 1px solid var(--line); }
  .menu-plus .menu-pied form { display: flex; align-items: center; gap: .3rem; margin: 0; }
  .menu-plus .menu-pied .link { min-height: var(--tap); display: inline-flex; align-items: center;
    font-size: var(--t-sm); }
  .menu-plus .menu-pied .langues .link.actif { color: var(--fg); text-decoration: underline;
    text-underline-offset: 3px; }
  .menu-plus .menu-pied .link.sortie { color: var(--short); font-weight: 700; }
}
@media (prefers-reduced-motion: reduce) { .menu-plus:popover-open { animation: none; } }
/* Navigateur sans `popover` : la feuille « Plus » ne s'ouvrirait jamais —
   on efface la barre du bas et on rend à la barre du haut ses sept liens. */
@supports not selector(:popover-open) {
  @media (max-width: 720px) {
    .tabbar, .menu-plus { display: none !important; }
    .topbar { flex-wrap: wrap; }
    .topbar nav { order: 3; width: 100%; flex-wrap: wrap; gap: .15rem; margin-left: 0; }
    .topbar nav a:not(.nav-notif) { display: inline-block; padding: .28rem .5rem; }
    .topbar .inline { display: flex; }
    main { padding-bottom: 1rem; }
  }
}

/* ================================= ERREURS EN CLAIR, DÉTAIL REPLIÉ (phase 1)
   « Échec : KeyError 'positionSide' … » dans un bandeau bleu : le lecteur
   comprend qu'il y a eu un problème, pas ce qu'il doit faire. Désormais le
   bandeau dit la chose en français (« L'opération a échoué, rien n'est
   garanti côté exchange : vérifie la position ») et la sortie brute de
   l'exchange est là, un clic plus loin, pour le diagnostic — jamais perdue,
   jamais imposée. `?detail=` est posé par la route, comme `?msg=`. */
.banner-detail { margin: .35rem auto 0; text-align: left; max-width: 48rem; }
.banner-detail summary { cursor: pointer; color: inherit; opacity: .8; font-size: var(--t-xs);
  text-align: center; list-style: none; min-height: 28px; display: inline-flex; align-items: center;
  gap: .3rem; padding: 0 .5rem; border-radius: 6px; }
.banner-detail summary::-webkit-details-marker { display: none; }
.banner-detail summary::before { content: "▸"; }
.banner-detail[open] summary::before { content: "▾"; }
.banner-detail code, .detail-tech code { display: block; margin-top: .3rem; padding: .45rem .65rem;
  white-space: pre-wrap; word-break: break-word; font-size: var(--t-xs); line-height: 1.4;
  color: var(--fg); background: #0d1420; border: 1px solid #223047; border-radius: 6px; }
.banner-detail { display: flex; flex-direction: column; align-items: center; }
.banner-detail code { align-self: stretch; }
/* La même chose, en ligne, dans une fiche ou un tableau. */
.detail-tech { display: inline-block; vertical-align: top; }
.detail-tech summary { cursor: pointer; color: var(--muted); font-size: var(--t-xs); list-style: none;
  min-height: 28px; display: inline-flex; align-items: center; gap: .3rem; }
.detail-tech summary::-webkit-details-marker { display: none; }
.detail-tech summary::before { content: "▸"; }
.detail-tech[open] summary::before { content: "▾"; }

/* ============================================== AGENTS — phase 3 (25/09/2026)
   Les bots d'abord ; l'arrêt d'urgence en une ligne ; le bilan et les alertes
   repliés (même pli que les groupes de Positions) ; la création dans un
   tiroir. */
.kill-bar { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .9rem;
  padding: .6rem .9rem; margin: .2rem 0 1rem; }
.kill-bar .muted { font-size: var(--t-sm); }
.bilan-pli, .alertes-pli { margin: 1rem 0; }
.pli-corps { margin: .4rem 0 0; }
.alertes-pli .gj-nom strong { font-weight: 800; }
.bot-groupe-plus { float: right; min-height: 28px; padding: 0 .6rem; border-radius: 999px;
  font-size: var(--t-sm); font-weight: 600; color: var(--accent);
  background: transparent; border: 1px solid var(--line); }
.bot-groupe-plus:hover { border-color: var(--accent); background: var(--card2); box-shadow: none; }
/* Un bouton qui se lit comme un lien (ouvre le tiroir de création). */
button.link.lien { color: var(--accent); font: inherit; padding: 0; }
button.link.lien:hover { text-decoration: underline; }
.tiroir-corps > p.muted + form, .tiroir-corps > form { margin-top: .6rem; }
@media (max-width: 720px) { .bot-groupe-plus { min-height: 32px; } }

/* ================================================ PHASE 4 — LE RESSENTI (25/09/2026)
   Le toast : le bandeau `?msg=` posé PAR-DESSUS la page (sous la barre du
   haut ; au-dessus des onglets sur téléphone), au lieu d'un bloc qui pousse
   tout vers le bas et reste jusqu'au prochain clic. app.js ajoute la croix,
   le compte à rebours (`--toast-vie`) et nettoie l'adresse. Sans script, le
   bandeau reste en flux, en haut, comme avant : `@media (scripting: none)`. */
@media (scripting: enabled) {
  /* En BAS, centré : en haut il couvrait le titre et les boutons de la page
     pendant six secondes. Sur téléphone, juste au-dessus des onglets. */
  body > .banner { position: fixed; z-index: 85; left: 50%; transform: translateX(-50%);
    bottom: 1.2rem; width: min(48rem, calc(100vw - 2rem)); margin: 0;
    padding-right: 2.6rem; text-align: left;
    box-shadow: 0 14px 40px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.04);
    animation: toast-entre .28s cubic-bezier(.2,.8,.3,1); }
  @media (max-width: 720px) {
    body > .banner { bottom: calc(66px + env(safe-area-inset-bottom)); width: calc(100vw - 1.2rem); }
  }
}
@keyframes toast-entre { from { opacity: 0; transform: translate(-50%, 12px); } }
.banner .toast-x { position: absolute; top: .35rem; right: .35rem; width: 32px; height: 32px;
  min-height: 0; padding: 0; border-radius: 50%; background: transparent; border: 0;
  color: inherit; opacity: .7; font-size: var(--t-md); }
.banner .toast-x:hover { opacity: 1; background: rgba(255,255,255,.08); box-shadow: none; }
/* Le fil de vie : une ligne qui se consume en bas du toast, suspendue au survol. */
.banner.auto::after { content: ""; position: absolute; left: 0; bottom: 0; height: 3px;
  width: 100%; border-radius: 0 0 6px 6px; background: currentColor; opacity: .35;
  transform-origin: left; animation: toast-vie var(--toast-vie, 7s) linear forwards; }
.banner.auto:hover::after { animation-play-state: paused; }
@keyframes toast-vie { to { transform: scaleX(0); } }
.banner.is-parti { animation: toast-sort .25s ease forwards; pointer-events: none; }
@keyframes toast-sort { to { opacity: 0; transform: translate(-50%, 8px); } }
/* La copie posée dans un tiroir (app.js, rouvrirTiroir) reste un bloc en flux. */
.banner.tiroir-msg { position: static; transform: none; width: auto; animation: none;
  box-shadow: none; padding-right: 1rem; }
.banner.tiroir-msg::after, .banner.tiroir-msg .toast-x { display: none; }

/* Le changement de page se fond (View Transitions, quand le navigateur l'a). */
::view-transition-old(root), ::view-transition-new(root) { animation-duration: .18s; }

/* « ↻ Actualiser » (live-prices.js, forcerSnapshot) : pendant l'attente, les
   chiffres en cours de remplacement s'estompent et la flèche tourne. */
main.is-rafraichit .argent, main.is-rafraichit .tgc-grid, main.is-rafraichit table.grid,
main.is-rafraichit .groupe-pli { opacity: .5; filter: saturate(.5); transition: opacity .3s, filter .3s; }

/* Le squelette des positions (premier chargement) : la silhouette des cartes. */
.sk-carte { aspect-ratio: 5 / 7; border-radius: 12px; padding: 8%;
  background: linear-gradient(180deg, #172142, #0f1730); border: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 6%; }
.sk-tete, .sk-scene, .sk-ligne { border-radius: 6px; background: #223052;
  position: relative; overflow: hidden; }
.sk-tete { height: 9%; width: 55%; }
.sk-scene { flex: 1; border-radius: 10px; }
.sk-ligne { height: 8%; width: 100%; } .sk-ligne.court { width: 60%; }
.sk-tete::after, .sk-scene::after, .sk-ligne::after { content: ""; position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 20%, rgba(255,255,255,.09) 50%, transparent 80%);
  transform: translateX(-100%); animation: sk-brille 1.6s ease-in-out infinite; }
@keyframes sk-brille { to { transform: translateX(100%); } }

/* ============================================ AGENTS SUR TÉLÉPHONE — UNE CARTE PAR BOT
   (25/09/2026) Le tableau reste un tableau dans le HTML ; ici, il se replie.
   La règle générique `table.grid { display:block; overflow-x:auto }` du
   téléphone est écartée pour `.bots`. */
@media (max-width: 720px) {
  table.grid.bots { display: block; overflow: visible; white-space: normal;
    background: transparent; border: 0; box-shadow: none; border-radius: 0; }
  table.grid.bots thead { display: none; }
  table.grid.bots tbody, table.grid.bots tr, table.grid.bots th { display: block; }
  table.grid.bots .bot-groupe-tete th { border-radius: 10px; margin: .9rem 0 .45rem; padding: .55rem .7rem;
    border-bottom: 0; }
  table.grid.bots tbody.bot-groupe + tbody.bot-groupe .bot-groupe-tete th { border-top: 0; }
  table.grid.bots tr.bot-ligne { display: grid; grid-template-columns: 1fr 1fr; gap: .45rem .8rem;
    padding: .7rem .8rem .65rem; margin: 0 0 .5rem; background: var(--card);
    border: 1px solid var(--line); border-left: 3px solid rgba(56,189,248,.35); border-radius: 10px; }
  table.grid.bots tr.bot-ligne:hover { background: var(--card); }
  table.grid.bots tr.bot-ligne td { display: block; padding: 0; border: 0; min-width: 0; }
  table.grid.bots tbody.bot-groupe td:first-child { border-left: 0; }
  /* Placement EXPLICITE (25/09, Djamel : « les timeframes en face du volume ») :
     Timeframes à gauche, Volume à droite, sur la MÊME ligne — chaque contrat
     en face de son timeframe, comme dans le tableau. */
  .bots .c-bot { grid-column: 1 / 2; grid-row: 1; }
  .bots .c-bot strong { font-size: var(--t-md); }
  .bots .c-mode { grid-column: 2 / 3; grid-row: 1; text-align: right; }
  .bots .c-marche { grid-column: 1 / -1; grid-row: 2; margin-top: -.25rem; }
  .bots .c-etapes { grid-column: 1 / -1; grid-row: 3; }
  .bots .c-tf { grid-column: 1 / 2; grid-row: 4; }
  .bots .c-volume { grid-column: 2 / 3; grid-row: 4; }
  .bots .c-rythme { grid-column: 1 / -1; grid-row: 5; }
  .bots .c-run { grid-column: 1 / 2; grid-row: 6; }
  .bots .c-pnl { grid-column: 2 / 3; grid-row: 6; }
  table.grid.bots tr.bot-ligne td.row-actions { grid-row: 7; }
  .bots td[data-label]::before { content: attr(data-label); display: block; font-size: var(--t-xs);
    color: var(--muted); text-transform: uppercase; letter-spacing: .3px; opacity: .8; }
  .bots .c-etapes, .bots .c-rythme, .bots .c-run, .bots .c-pnl { color: var(--fg); }
  table.grid.bots tr.bot-ligne td.row-actions { display: flex; gap: .5rem; grid-column: 1 / -1; margin-top: .25rem;
    padding-top: .55rem; border-top: 1px solid var(--line); }
  /* La bande AUTOMATION de chaque paire du Journal : un rang, pas deux. */
  .pair-wrap > .pilote-onband { padding: 0 .3rem .15rem; gap: .3rem; }
  .auto-pop > summary { min-height: 34px; padding-top: .2rem; padding-bottom: .2rem; }
  .bots .row-actions .btn { flex: 1 1 auto; }
  .bots tr.bot-ligne > td[colspan] { grid-column: 1 / -1; }
  .bots .vol-edit-form input { width: 5.5rem; }
}

/* SIGNAUX SUR TÉLÉPHONE (25/09) : la Watchlist repliée, le filtre en une ligne. */
.wl-pli { margin: .2rem 0 .8rem; }
.wl-pli > .wl-bar { margin-top: .4rem; padding: .7rem .9rem; background: var(--card);
  border: 1px solid var(--line); border-radius: 10px; }
@media (max-width: 720px) {
  .classeur-hint { display: none; }
  .wl-pli .gj-n { font-size: var(--t-sm); }
}

/* ================================ ALERTE MOTEUR REPLIABLE EN PASTILLE (25/09)
   Ouverte : la phrase rouge, une croix en haut à droite. Fermée : une pastille
   « ⚠ alerte moteur » — l'alarme ne disparaît jamais tout à fait. */
.alerte-pli { position: relative; margin: .4rem 0 .8rem; }
.alerte-pli > summary { list-style: none; cursor: pointer; }
.alerte-pli > summary::-webkit-details-marker { display: none; }
.alerte-pli > .error { margin: 0; padding: .55rem 2.6rem .55rem .8rem; border-radius: 8px;
  background: rgba(242,109,109,.10); border: 1px solid rgba(242,109,109,.35); }
.alerte-pli[open] > summary { position: absolute; top: .3rem; right: .3rem; z-index: 1;
  width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; color: var(--short); opacity: .8; }
.alerte-pli[open] > summary:hover { opacity: 1; background: rgba(242,109,109,.18); }
.alerte-pli[open] > summary::before { content: "✕"; }
.alerte-pli[open] > summary .alerte-court { display: none; }
.alerte-pli:not([open]) > summary { display: inline-flex; align-items: center; gap: .4rem;
  min-height: 32px; padding: 0 .75rem 0 .6rem; border-radius: 999px; font-size: var(--t-sm);
  font-weight: 600; color: var(--short); background: rgba(242,109,109,.12);
  border: 1px solid rgba(242,109,109,.5); }
.alerte-pli:not([open]) > summary::before { content: "⚠"; }
.alerte-pli:not([open]) > summary:hover { background: rgba(242,109,109,.2); }

/* ================================ POSITIONS — LE BLOC D'ARGENT EN TUILES (25/09)
   Chaque chiffre sous son étiquette. Ordinateur : les tuiles sur une ligne
   (le PnL, puis chaque compte). Téléphone : le latent en grand sur toute la
   largeur, réalisé et soldé côte à côte, puis l'argent du compte en grille
   2 × 2. Les identifiants animés par live-prices.js ne changent pas. */
.argent { display: flex; flex-direction: column; gap: .5rem; margin: .3rem 0 1rem; }
.argent .tuile { display: flex; flex-direction: column; gap: .05rem; min-width: 0;
  padding: .4rem .7rem; border-radius: 10px; background: rgba(24,34,60,.55);
  border: 1px solid var(--line); font-variant-numeric: tabular-nums; }
.argent .tuile .k { font-size: var(--t-xs); color: var(--muted); text-transform: uppercase;
  letter-spacing: .3px; white-space: nowrap; }
.argent .tuile b, .argent .tuile strong { font-size: var(--t-md); font-weight: 800; line-height: 1.2; }
.argent .tuile-forte { border-color: rgba(56,189,248,.35); }
.argent .tuile-forte b { color: var(--fg); }
.argent .tuile .risk-tag { margin: 0; font-size: var(--t-md); }
.argent .v-sous { font-size: var(--t-xs); }
.argent-pnl { margin: 0; gap: .5rem; align-items: stretch; }
.argent-pnl .tuile-latent strong { font-size: var(--t-xl); }
/* Les aveux de partialité sous un chiffre (« hors SOL/USDT », « réalisé
   partiel ») : AMBRE, la couleur de la réserve — pas le bleu d'accent des
   liens, qui se lisait comme un bouton (Djamel, 25/09 : « pourquoi en bleu ? »). */
.argent-pnl .v-sous .partiel { color: #f0be50; }
.argent .margins-bar { margin: 0; gap: .5rem; }
.argent .margin-chip { padding: .45rem .6rem .5rem; display: flex; flex-direction: column; gap: .35rem; }
.argent-tete { display: flex; align-items: baseline; gap: .4rem; font-size: var(--t-sm); }
.argent-tete strong { font-size: var(--t-md); }
.argent-devise { margin-left: auto; font-size: var(--t-xs); }
.argent-tuiles { display: flex; flex-wrap: wrap; gap: .4rem; }
.argent-tuiles .tuile { flex: 1 1 auto; background: rgba(10,16,32,.45); }
@media (max-width: 720px) {
  .argent-pnl { display: grid; grid-template-columns: 1fr 1fr; }
  .argent-pnl .tuile-latent { grid-column: 1 / -1; }
  .argent-pnl .state, .argent-pnl .diverg { grid-column: 1 / -1; justify-self: start; }
  .argent .margins-bar { flex-direction: column; }
  .argent-tuiles { display: grid; grid-template-columns: 1fr 1fr; }
}

/* « CARTES EN JEU » lisible du premier coup (25/09) : le titre, puis des
   pastilles 15m · 1h · 4h · 1d, le sens, les cumuls étiquetés. */
.cartes-en-jeu { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .7rem; }
.cartes-en-jeu .cj-titre { margin-right: .2rem; }
.cartes-en-jeu .cj-tfs { display: inline-flex; gap: .3rem; }
.cartes-en-jeu .cj-tf { display: inline-flex; flex-direction: column; align-items: center; min-width: 2.6rem;
  padding: .15rem .45rem; border-radius: 8px; background: rgba(255,255,255,.05); line-height: 1.1; }
.cartes-en-jeu .cj-tf small { font-size: var(--t-xs); color: var(--muted); font-weight: 600; }
.cartes-en-jeu .cj-sens, .cartes-en-jeu .cj-cumuls { display: inline-flex; flex-wrap: wrap;
  align-items: baseline; margin-left: 0; line-height: 1.15; }
.cartes-en-jeu .cj-cumuls small { flex-basis: 100%; }
.cartes-en-jeu .cj-cumuls small { font-size: var(--t-xs); font-weight: 600; }
.cartes-en-jeu .cj-sep { color: var(--muted); margin: 0 .25rem; font-weight: 400; }
@media (max-width: 720px) {
  .cartes-en-jeu { gap: .5rem .8rem; }
  .cartes-en-jeu .cj-titre { flex-basis: 100%; }
}

/* ======================================= TÉLÉPHONE, PLUS RAFFINÉ (25/09/2026)
   Djamel, capture du Journal : « les boutons, les barres, c'est un peu trop
   épais pour le tel ». Les cibles restent tapables, mais les commandes
   SECONDAIRES (vue, cartes, plier, filtrer, AUTOMATION) s'affinent : 32-34 px,
   fond presque transparent, trait fin ; seule l'action PRINCIPALE garde son
   plein cyan et ses 40 px, et elle va à droite. Les onglets du Journal
   deviennent un sélecteur segmenté ; les filtres perdent leur boîte ; les
   bandes de paires respirent moins. */
@media (max-width: 720px) {
  .btn.secondary { min-height: 34px; padding: .25rem .7rem; font-weight: 600;
    background: rgba(24,34,60,.5); border-color: rgba(38,51,82,.9); }
  .page-actions .btn { flex: 0 1 auto; }
  .page-actions .btn:not(.secondary) { margin-left: auto; }
  .tabs { gap: 3px; padding: 3px; margin: .4rem 0 .6rem; border: 1px solid var(--line);
    border-radius: 10px; background: rgba(10,16,32,.45); }
  .tabs .tab { flex: 1 1 0; min-width: 0; text-align: center; padding: .38rem .4rem; margin: 0;
    border: 0; border-radius: 8px; font-size: var(--t-sm); white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis; }
  .tabs .tab.active { background: var(--card2); border: 0; box-shadow: inset 0 0 0 1px var(--line); }
  .filters { background: transparent; border: 0; box-shadow: none; padding: 0; margin: .2rem 0 .7rem; }
  .filters .btn { min-height: 36px; }
  .fold-toolbar { gap: .4rem; margin: .2rem 0 .5rem; }
  .fold-toolbar .btn { min-height: 32px; padding: .15rem .6rem; font-size: var(--t-xs); }
  .pair-group > summary { padding: .5rem .7rem; gap: .45rem; }
  .pair-group > summary strong { font-size: var(--t-md); }
  .pair-wrap > .pilote-onband { padding: 0 .4rem .25rem; gap: .35rem; }
  .auto-pop > summary { min-height: 28px; padding: .1rem .55rem; letter-spacing: .04em;
    font-size: var(--t-xs) !important; background: transparent; }
  .pilote-onband > .st { font-size: var(--t-xs) !important; }
}
@media (max-width: 720px) {
  /* Une paire SANS bot : la pastille AUTOMATION remonte sur la ligne de la
     paire, à droite, au lieu d'occuper un rang à elle seule. Avec des bots
     (les puces MX/KC), la bande reste sous la ligne, où elle a la place. */
  .pair-wrap:not(:has(.pilote-onband .st)) > .pilote-onband { position: absolute; top: .3rem; right: .35rem;
    padding: 0; margin: 0; }
  .pair-wrap:not(:has(.pilote-onband .st)) > .pair-group > summary { padding-right: 8.5rem; }
}

/* TABLEAUX SUR TÉLÉPHONE (25/09, « le tableau est trop grand ») : texte et
   marges resserrés, et la PREMIÈRE colonne (la paire) reste collée à gauche
   pendant qu'on fait défiler les autres — on sait toujours quelle ligne on
   lit. Le tableau des bots, lui, se replie en cartes (règles .bots). */
@media (max-width: 720px) {
  table.grid:not(.bots):not(.tel-cartes) { font-size: var(--t-xs); border-radius: 8px; }
  table.grid:not(.bots):not(.tel-cartes) th, table.grid:not(.bots):not(.tel-cartes) td { padding: .4rem .5rem; }
  table.grid:not(.bots):not(.tel-cartes) th:first-child, table.grid:not(.bots):not(.tel-cartes) td:first-child { position: sticky; left: 0;
    z-index: 1; background: var(--card); box-shadow: 4px 0 8px -4px rgba(0,0,0,.6); }
  table.grid:not(.bots):not(.tel-cartes) thead th:first-child { background: var(--card2); }
  table.grid:not(.bots):not(.tel-cartes) .btn { min-height: 30px; padding: .1rem .5rem; font-size: var(--t-xs); }
}

/* ==================== TABLEAUX EN CARTES COMPACTES SUR TÉLÉPHONE (25/09/2026)
   Positions, Journal, Signaux : même recette que les bots — le tableau reste
   un tableau dans le HTML, le CSS le replie sous 720 px. Titre et sens en
   tête, le PnL en grand (Positions), chaque chiffre sous son étiquette,
   l'état sur une ligne, les actions en bas. */
@media (max-width: 720px) {
  table.grid.tel-cartes { display: block; overflow: visible; white-space: normal;
    background: transparent; border: 0; box-shadow: none; border-radius: 0; font-size: var(--t-sm); }
  table.grid.tel-cartes thead { display: none; }
  table.grid.tel-cartes tbody { display: block; }
  table.grid.tel-cartes tbody tr { display: grid; grid-template-columns: 1fr 1fr; gap: .4rem .8rem;
    padding: .65rem .8rem .6rem; margin: 0 0 .5rem; background: var(--card);
    border: 1px solid var(--line); border-radius: 10px; }
  table.grid.tel-cartes tbody tr:hover { background: var(--card); }
  table.grid.tel-cartes tbody tr:last-child { margin-bottom: 0; }
  table.grid.tel-cartes td { display: block; padding: 0; border: 0; min-width: 0; }
  .tel-cartes .c-titre { grid-column: 1 / 2; grid-row: 1; }
  .tel-cartes .c-titre strong { font-size: var(--t-md); }
  .tel-cartes .c-sens { grid-column: 2 / 3; grid-row: 1; text-align: right; font-weight: 800; align-self: start; }
  .tel-cartes td[data-label]::before { content: attr(data-label); display: block; font-size: var(--t-xs);
    color: var(--muted); text-transform: uppercase; letter-spacing: .3px; opacity: .8; }
  .tel-cartes .c-pnl { grid-column: 1 / -1; }
  .tel-cartes .c-pnl [data-cell="pnl"] { font-size: var(--t-lg); font-weight: 800; }
  .tel-cartes .c-etat, .tel-cartes .c-prog, .tel-cartes .c-compte, .tel-cartes .c-tp { grid-column: 1 / -1; }
  .tel-cartes .c-compte { font-size: var(--t-xs); }
  table.grid.tel-cartes td.c-etat { display: flex; flex-wrap: wrap; gap: .3rem; align-items: center; }
  /* `table.grid.tel-cartes td.…` : la règle de repli en bloc l'emporterait sinon (spécificité). */
  table.grid.tel-cartes td.c-actions { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: .4rem; align-items: center;
    margin-top: .2rem; padding-top: .55rem; border-top: 1px solid var(--line); white-space: normal; }
  .tel-cartes .c-actions .btn { min-height: 34px; }
  .tel-cartes .c-actions .act-form { display: flex; flex: 1 1 46%; margin: 0; gap: .3rem; }
  /* `width: 0` + flex : sinon la largeur INTRINSÈQUE du champ (≈ 150 px, taille
     par défaut d'un <input>) entre dans le calcul de la ligne et chaque
     formulaire se retrouve seul sur la sienne. */
  .tel-cartes .c-actions .act-form input[type=text] { flex: 1 1 0; width: 0; min-width: 0; min-height: 34px; }
  .tel-cartes .c-actions form { width: auto; max-width: 100%; }
  .tel-cartes .c-actions form.inline { display: inline-flex; }
}
@media (max-width: 720px) {
  /* Positions : ordre de lecture fixé — PnL juste sous la paire, puis
     taille | entrée, prix | liquidation, progression, compte, actions. */
  .tel-cartes.positions .c-pnl { grid-row: 2; }
  .tel-cartes.positions .c-taille { grid-row: 3; grid-column: 1; }
  .tel-cartes.positions .c-entree { grid-row: 3; grid-column: 2; }
  .tel-cartes.positions .c-prix { grid-row: 4; grid-column: 1; }
  .tel-cartes.positions .c-liq { grid-row: 4; grid-column: 2; }
  .tel-cartes.positions .c-prog { grid-row: 5; }
  .tel-cartes.positions .c-compte { grid-row: 6; }
  .tel-cartes.positions .c-actions { grid-row: 7; }
  /* Les boutons seuls (Fermer, Détail) restent côte à côte ; seuls les
     formulaires à champ (DEF, INT) prennent une demi-ligne. */
  .tel-cartes .c-actions .act-form:not(:has(input[type=text])) { flex: 0 0 auto; }
}

/* ================================== TABLEAUX SUR ORDINATEUR — resserrés (25/09)
   Le tableau des Positions faisait 1 930 px de large dans une colonne de
   1 040 : la colonne Actions (659 px : deux formulaires, quatre boutons sur
   une seule ligne) et la Progression (382 px : la jauge et sa pastille côte
   à côte) débordaient de la page. Trois leviers : la colonne centrale
   s'élargit quand elle porte un tableau (`:has`), le texte passe au cran
   sm avec des marges plus serrées, et ce qui peut s'empiler s'empile —
   pastilles sous le nom, formulaires DEF/INT l'un sous l'autre. */
@media (min-width: 721px) {
  main:has(table.grid.tel-cartes) { max-width: min(1280px, 100% - 2rem); }
  table.grid.tel-cartes { font-size: var(--t-sm); }
  table.grid.tel-cartes th, table.grid.tel-cartes td { padding: .45rem .5rem; }
  table.grid.positions .c-titre .state { display: block; width: fit-content; margin-top: .15rem; }
  table.grid.positions .gauge-cell { min-width: 8.5rem; }
  table.grid.positions .gauge-cell .state { display: block; width: fit-content; margin-top: .25rem; }
  table.grid.positions .c-compte .muted { display: block; }
  table.grid.positions .c-pnl .muted { display: block; }
  .pos-actions { white-space: normal; min-width: 14rem; max-width: 17rem; }
  .pos-actions > span[style] { display: flex !important; margin-bottom: .2rem; }
  .pos-actions .act-form { margin: .1rem .3rem .1rem 0; }
  .pos-actions .act-form input[type=text] { width: 5rem; }
}

/* ================================== KILL SWITCH PAR COMPTE (25/09/2026)
   Sur la bande du compte (Agents) et sur sa tuile (Positions). Coupé : la
   bande vire au rouge sourd, la tuile porte « auto OFF » en rouge. */
table.grid .bot-groupe-tete.is-coupe th { border-left-color: var(--short);
  background: linear-gradient(90deg, rgba(242,109,109,.14), rgba(24,34,60,.35) 60%); }
.bot-groupe-tete .compte-switch { display: inline-flex; margin-left: .5rem; vertical-align: middle; }
.bot-groupe-tete .compte-switch .btn { min-height: 28px; padding: .1rem .6rem; font-size: var(--t-xs); }
.bot-groupe-tete .compte-coupe { margin-left: .5rem; font-size: var(--t-xs); vertical-align: middle; }
.argent-tete .auto-compte { display: inline-flex; margin-left: .4rem; }
.argent-tete .auto-switch { min-height: 24px; padding: 0 .5rem; font-size: var(--t-xs); border-radius: 999px;
  letter-spacing: .3px; }
.argent-tete .auto-switch.on { color: #7ee08a; border-color: rgba(52,209,122,.45); background: rgba(52,209,122,.1); }
.argent-tete .auto-switch.off { color: var(--short); border-color: rgba(242,109,109,.55); background: rgba(242,109,109,.14); }
/* Le pli en DEUX GESTES de l'interrupteur (26/09) — même recette que .suppr-pli :
   le résumé EST le bouton, le formulaire s'ouvre dessous, encadré. */
.auto-pli { display: inline-block; vertical-align: middle; }
.auto-pli > summary { list-style: none; cursor: pointer; }
.auto-pli > summary::-webkit-details-marker { display: none; }
.auto-pli[open] > summary { opacity: .6; }
.auto-form { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-top: .35rem;
  padding: .45rem .6rem; border: 1px solid rgba(242,109,109,.45); border-radius: 8px;
  background: rgba(242,109,109,.08); max-width: 34rem; }
.auto-form .xs { flex: 1 1 14rem; }
.bot-groupe-tete .compte-coupe-note { display: block; margin-top: .25rem; font-weight: 400; max-width: 60ch; }
/* Sur téléphone, le formulaire du pli OUVERT se pose PAR-DESSUS la tuile, sur
   toute sa largeur : dans la colonne de sa pastille, le texte se lisait en
   colonne de quatre mots. Fond plein (la tuile est en dessous). */
@media (max-width: 720px) {
  .argent-compte { position: relative; }
  .argent-tete .auto-pli[open] > .auto-form { position: absolute; left: .4rem; right: .4rem;
    top: 2.2rem; z-index: 5; background: var(--card2); box-shadow: 0 8px 24px rgba(0,0,0,.45); }
}
@media (max-width: 720px) {
  .bot-groupe-tete .compte-switch { margin: .3rem 0 0; }
  .bot-groupe-tete .compte-switch .btn { min-height: 32px; }
}

/* SUPPRIMER UN BOT (25/09) : replié derrière un premier clic, confirmé par une case. */
.suppr-pli { display: inline-block; vertical-align: middle; }
.suppr-pli > summary { list-style: none; }
.suppr-pli > summary::-webkit-details-marker { display: none; }
.suppr-pli[open] > summary { opacity: .6; }
.suppr-form { display: inline-flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-top: .35rem;
  padding: .45rem .6rem; border: 1px solid rgba(242,109,109,.45); border-radius: 8px;
  background: rgba(242,109,109,.08); }
.suppr-form label.check { margin: 0; font-size: var(--t-sm); }
@media (max-width: 720px) { .tel-cartes .c-actions .suppr-pli, .bots .row-actions .suppr-pli { flex-basis: 100%; } }

/* ===================== ABONNEMENT (lot 4, 25/09/2026) =====================
   La page Abonnement, la page de paiement et le poste des abonnés. Le QR est
   un <svg> inline noir sur fond blanc : un lecteur de QR veut du contraste,
   pas le thème sombre. Rien ici n'est porteur de contenu : sans CSS, tout se
   lit encore, dans l'ordre. */
.abo-etat strong.ok { color: #5fd08a; }
.abo-etat strong.bad { color: #ff7b7b; }
.abo-prix { font-size: 1.7rem; font-weight: 700; letter-spacing: .01em; }
.abo-montant { font-size: 1.45rem; font-weight: 700; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  word-break: break-all; margin: .2rem 0 .4rem; }
.qr { background: #fff; border-radius: 12px; padding: 10px; width: 240px; max-width: 100%; aspect-ratio: 1;
  display: flex; align-items: center; justify-content: center; margin: .6rem 0; }
.qr svg { width: 100%; height: 100%; display: block; }
code.adresse { display: block; word-break: break-all; padding: .45rem .6rem; margin: .2rem 0 .6rem;
  background: var(--card2); border: 1px solid var(--line); border-radius: 6px; font-size: var(--t-sm); }
.abo-avert { border-left: 3px solid var(--accent); padding: .5rem .8rem; color: var(--muted);
  font-size: var(--t-sm); line-height: 1.45; margin: 1rem 0; }
.paiement h3 { margin-top: 1.2rem; font-size: var(--t-md); }
.cgv { max-width: 760px; margin: 0 auto; line-height: 1.55; }
.cgv h2 { font-size: 1.05rem; margin-top: 1.4rem; }
.cgv .muted { font-size: var(--t-sm); }
/* LA TABLE « ce que nous conservons » (Données personnelles, 26/09/2026) : sur
   téléphone, trois colonnes ne tiennent pas — vu à l'aperçu, les colonnes
   « pourquoi » et « combien de temps » sortaient de l'écran. Une fiche par
   ligne, chaque cellule sous son étiquette (data-label), comme les bots. */
.cgv table.grid td { vertical-align: top; }
@media (max-width: 720px) {
  .cgv table.grid.fiches thead { display: none; }
  .cgv table.grid.fiches, .cgv table.grid.fiches tbody { display: block; }
  .cgv table.grid.fiches tr { display: block; padding: .55rem 0; border-bottom: 1px solid var(--line); }
  .cgv table.grid.fiches td { display: block; padding: .15rem 0; border: 0; white-space: normal; overflow: visible; }
  .cgv table.grid.fiches td[data-label]::before { content: attr(data-label); display: block;
    font-size: var(--t-xs); color: var(--muted); text-transform: uppercase; letter-spacing: .3px; opacity: .8; }
}


/* Mandats de gestion (lot 1 broker, 25/09/2026) : une ligne par compte, les
   plafonds sur une ligne, les gestes à droite. */
.mandat-ligne { padding: .55rem 0; border-top: 1px solid rgba(255,255,255,.08); }
.mandat-ligne:first-of-type { border-top: 0; }
.mandat-ligne .row-actions { margin-top: .35rem; display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.mandat-ligne strong.ok { color: #5fd08a; }
.mandat-plafonds { display: flex; flex-wrap: wrap; gap: .4rem .8rem; align-items: center; margin-top: .35rem; }
.mandat-plafonds label { display: inline-flex; gap: .3rem; align-items: center; font-size: .9rem; }


/* L'entrée (lot 5 broker, 26/09/2026) : accueil public, offre de gestion en
   clair, guide en trois pas. Deux voies côte à côte, empilées sur téléphone. */
.accueil .hero { text-align: center; padding: 2rem 1.2rem; }
.accueil .hero h1 { font-size: 2rem; margin: 0 0 .6rem; }
.accueil .lead { font-size: var(--t-lg); line-height: 1.5; max-width: 720px; margin: 0 auto .5rem; }
.voies { display: grid; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); gap: 1rem; margin: 1rem 0; }
.voie h2 { margin-top: 0; }
.voie ul { padding-left: 1.2rem; line-height: 1.5; }
.voie .prix { font-size: var(--t-lg); margin: .8rem 0 .5rem; }
.gestion .card, .confier .card { max-width: 760px; margin: 0 auto 1rem; line-height: 1.5; }
.gestion .card h2 { margin-top: 0; font-size: 1.05rem; }
.etapes { list-style: none; counter-reset: pas; padding: 0; margin: 0; }
.etapes > li { counter-increment: pas; position: relative; padding: .9rem 0 .9rem 3rem;
  border-top: 1px solid var(--line); }
.etapes > li:first-child { border-top: 0; }
.etapes > li::before { content: counter(pas); position: absolute; left: 0; top: .9rem; width: 2rem;
  height: 2rem; border-radius: 50%; background: var(--card2); border: 1px solid var(--line);
  color: var(--fg); font-weight: 700; display: flex; align-items: center; justify-content: center; }
.etapes > li.faite::before { content: "✓"; background: #1f4d34; border-color: #2e7a50; color: #5fd08a; }
.etapes h2, .etapes h3 { font-size: var(--t-md); margin: 0 0 .35rem; }
.etapes p { margin: .3rem 0; }
.etapes p.ok { color: #5fd08a; }
