@charset "UTF-8";
/* ═══════════════════════════════════════════════════════════════════════
   TERMINAL — habillage « admin agences ogilio »
   ─────────────────────────────────────────────────────────────────────
   Relevé au pixel sur ogilio.com/httpdocs/assets/css/admin.css (3287 l.)
   et app/views/admin/partials/ai-chat-widget.php.

   PRINCIPE, et il n'a pas bougé : cette feuille est une COUCHE DE
   SURCHARGE. Elle redéfinit une rampe neutre et une grammaire de chrome —
   rayons, fonds, liserés. Elle ne déplace aucun nœud, ne renomme rien, et
   ne touche pas à une seule règle de mise en page. C'est ce qui garantit
   qu'aucune option du terminal ne se perd : app.js et saas-draw.js
   écrivent par identifiant, par classe et par variable CSS, et tout cela
   reste intact.

   CE QU'ELLE NE TOUCHE PAS, délibérément :
     · --up / --down          app.js les lit au montage du graphique
     · --warn / --info        sémantique documentée dans style.css
     · --accent               couleur de marque
     · la structure du dock, ses états d'ouverture, ses identifiants

   CE QU'ELLE SURCHARGE :
     · la rampe neutre (fonds, liserés, gris de texte)
     · la géométrie du chrome : 56 px, coin 24, rayons 8 / 12 / 99
     · le rail de dessin, le dock, les menus, les fenêtres

   Valeurs relevées sur ogilio :
     topbar      56 px · coin bas-droit 24 px
     latérale    240 px · #ebebeb · coin haut-gauche 24 px
     plateau     #f0f0f0 · coin haut-gauche 44 px (--adm-shopify-radius)
     rayons      8 · 12 · 20 · 99 px
     gris        #6d7175 · #8c9196   (famille Polaris)
     mouvement   .15s ease en micro, .3s cubic-bezier(.4,0,.2,1) en
                 structure — relevé par fréquence, pas au jugé

   SENS DE LECTURE : tout est écrit en propriétés LOGIQUES
   (`inset-inline-start`, `border-start-start-radius`, `padding-inline`).
   Le terminal existe en arabe ; une feuille en propriétés physiques
   demanderait un miroir complet, qui dérive à la première modification.
   ═══════════════════════════════════════════════════════════════════════ */

/* ═══ 1. RAMPE NEUTRE ══════════════════════════════════════════════════
   Une seule teinte (bleu 220) pour que les gris restent d'accord entre
   eux. En sombre l'élévation est une LUMIÈRE, pas une ombre : plus un
   élément est haut, plus sa surface s'éclaircit. Sur fond sombre une
   ombre portée ne se voit pas.

   Les trois niveaux de texte de la production sont CONSERVÉS dans leur
   rôle et leurs contrastes tenus au-dessus de 4,5:1, comme le documente
   style.css. On change la teinte, pas la règle. */
:root {
  /* ── POURQUOI CES ÉCARTS SONT AUSSI FRANCS ────────────────────────
     Premier jet : #0F0F0F → #101318. Mesuré, chaque valeur changeait
     bien ; à l'œil, RIEN. Le terminal d'origine est déjà quasi noir, et
     une rampe sombre posée dessus se confond avec lui.

     Or l'identité d'ogilio est une coquille CLAIRE — barre sombre,
     plateau clair, cartes blanches. Dans le noir, ce contraste-là n'existe
     plus. Ce qui peut survivre à l'obscurité, ce sont les ÉCARTS : une
     barre nettement plus basse que le plateau, des cartes nettement plus
     hautes que le fond, et le coin de 24 px qui ne se voit QUE s'il tombe
     sur une surface d'une autre valeur.

     Quatre paliers d'environ 4 points de clarté L*, au lieu de deux :
       #0b0d11  barre du haut — le plus bas, le chrome s'efface
       #0f1216  graphique     — le puits
       #15181f  plateau       — la surface de travail
       #1c2128  cartes        — dock, rail, menus : elles FLOTTENT
       #262c35  survol, champs
     C'est ce qui fait lire « carte posée sur un plateau », la grammaire
     d'ogilio, sans une once de clarté. */
  --bg:            #15181f;
  --surface:       #0b0d11;   /* barre du haut : la surface la plus basse */
  --surface-2:     #1c2128;
  --raised:        #262c35;
  --chart-bg:      #0f1216;
  --grid:          rgba(255, 255, 255, .06);

  --border:        rgba(255, 255, 255, .10);
  --border-strong: rgba(255, 255, 255, .18);

  --text:          #e8eaed;   /* 14,3:1 sur --surface-2 */
  --text-2:        #c3c9d1;   /* 10,4:1 */
  --text-3:        #929aa6;   /*  6,1:1 */
  --toolbar-fg:    #f2f4f6;

  /* Jetons propres à cet habillage. */
  --og-radius-sm:   8px;
  --og-radius-md:  12px;
  --og-radius-pill: 99px;
  --og-corner:     24px;
  --og-lift-1: 0 0 0 1px rgba(255,255,255,.08), 0 1px 2px rgba(0,0,0,.6);
  --og-lift-2: 0 0 0 1px rgba(255,255,255,.14), 0 12px 32px -8px rgba(0,0,0,.85);
  --og-hover:  rgba(255, 255, 255, .06);
  --og-active: rgba(255, 255, 255, .10);
}

/* ─── Thème clair : la coquille d'ogilio, telle qu'elle est chez eux ───
   Barre du haut SOMBRE sur contenu clair — c'est leur signature, et la
   raison pour laquelle leur administration se reconnaît d'un coup d'œil. */
:root[data-theme="light"] {
  --bg:            #f0f0f0;
  --surface:       #ffffff;
  /* Les cartes d'ogilio sont BLANCHES, pas cassées : c'est ce qui les
     fait flotter au-dessus du plateau #f0f0f0. Le #fafafa d'origine
     était mon choix, pas une valeur relevée chez eux, et il rapprochait
     la carte du plateau au lieu de l'en détacher. */
  --surface-2:     #ffffff;
  --raised:        #ebebeb;
  --chart-bg:      #ffffff;
  --grid:          #e1e3e5;

  --border:        #e1e3e5;
  --border-strong: #c9cccf;

  /* ── LA RAMPE DE TEXTE CLAIRE N'EST PAS TOUCHÉE ────────────────────
     Premier jet : j'avais posé les gris Polaris d'ogilio (#6d7175). Test
     A/B en production, peau activée puis désactivée : trois éléments
     passaient sous le seuil AA — `inst-dim` 4,32, `tp-tab` 4,27 — et
     trois autres se dégradaient. style.css documente ses trois niveaux
     avec leurs contrastes mesurés (17,93 / 7,61 / 5,60 en clair) ; un
     habillage n'a pas à les affaiblir.

     La signature d'ogilio en clair tient aux SURFACES et à la barre du
     haut sombre, pas à la nuance de gris d'un libellé de 10 px. */
  --text:          #121722;   /* 17,93:1 — valeur de style.css */
  --text-2:        #4A5468;   /*  7,61:1 */
  --text-3:        #5C6880;   /*  5,60:1 */
  --toolbar-fg:    #f2f4f6;

  --og-lift-1: 0 0 0 1px rgba(0,0,0,.045), 0 1px 1px rgba(0,0,0,.025), 0 3px 6px -1px rgba(0,0,0,.03);
  --og-lift-2: 0 0 0 1px rgba(0,0,0,.055), 0 2px 4px rgba(0,0,0,.04), 0 12px 24px -8px rgba(0,0,0,.10);
  --og-hover:  rgba(0, 0, 0, .05);
  --og-active: rgba(0, 0, 0, .08);
}

/* ═══ 2. BARRE DU HAUT ══════════════════════════ admin.css:303 ═══════
   Elle fait DÉJÀ 56 px : seuls le fond, le liseré et le coin changent.
   En sombre elle descend à #0b0d11 — la surface la plus basse de la
   rampe, sous le plateau et sous la barre latérale : le chrome s'efface,
   le contenu avance. En clair elle reste noire, comme chez ogilio. */
body #toolbar {
  background: var(--og-topbar-bg);
  /* PAS de filet sous la barre — le gabarit n'en a pas non plus. Elle est
     deja separee du contenu par l'ecart de valeur (elle est la surface la
     plus sombre de la rampe) et par le coin arrondi qui l'entaille : un
     trait par-dessus ne separait rien de plus, il ajoutait juste une
     ligne. */
  border-bottom: none;
  border-end-end-radius: var(--og-corner);
  /* ── LES DEUX BORDS DE LA BARRE N'ONT PAS LA MÊME RÉFÉRENCE ────────
     Asymétrique, et c'est le seul réglage juste : chaque côté s'aligne
     sur ce qui est RÉELLEMENT dessous.

     À gauche, la gouttière du plateau (12 px) : depuis que le bouton de
     colonne a disparu, le logo est le premier objet de la barre et il se
     lit dans la même colonne que la ligne de contexte, le bandeau et le
     graphique. Mesuré avant correction : logo à 8, contenu à 12.

     À droite, les 8 px du rail. Ses boutons font 38 px centrés dans une
     piste de 54, donc ils s'arrêtent à 8 px du bord — et les commandes
     de la barre du haut aussi. Mesuré : 1272 des deux côtés, à zéro
     pixel près. Passer la droite à 12 aurait cassé cet accord-là pour
     réparer l'autre.

     Propriétés LOGIQUES : en arabe le plateau est à droite et le rail à
     gauche, `start` et `end` suivent, la mesure tient dans les deux sens
     d'écriture sans une règle de plus. */
  padding-inline-start: var(--og-stack-gap, 12px);
  padding-inline-end: 8px;
  gap: 4px;
  transition: border-radius .3s cubic-bezier(.4, 0, .2, 1);
}

/* Dans les deux thèmes la barre est sombre : son contenu est donc clair,
   quel que soit le thème. Sans cette règle, le thème clair posait du
   texte #202223 sur du #1a1a1a. */
body #toolbar,
body #toolbar button:not(:where(.saas-acct-pop, #symbolDropdown) *),
body #toolbar input:not(:where(.saas-acct-pop, #symbolDropdown) *),
body #toolbar select:not(:where(.saas-acct-pop, #symbolDropdown) *),
body #toolbar .tb-word,
body #toolbar span:not(:where(.saas-acct-pop, #symbolDropdown) *) { color: var(--toolbar-fg); }
/* Ce qui PEND sous la barre n'est pas la barre : le menu compte et la
   liste des instruments sont des cartes, et suivent le theme comme telles.
   Sans l'exclusion ci-dessus, le theme clair y posait du texte CLAIR sur
   fond CLAIR — 1,08:1 sur la liste, illisible. */
body #toolbar .saas-acct-pop,
body #toolbar #symbolDropdown { color: var(--text); }
body #toolbar .tb-logo { color: var(--toolbar-fg); }

/* Contrôles : la grammaire d'ogilio — pilule ou rayon 12, fond surélevé
   discret, survol par la lumière. */
body #toolbar button:not(:where(.saas-acct-pop, #symbolDropdown) *),
body #toolbar .cs-btn,
body #toolbar .tf-more {
  border-radius: var(--og-radius-md);
  border: 1px solid rgba(255, 255, 255, .08);
  background: rgba(255, 255, 255, .06);
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
body #toolbar button:not(:where(.saas-acct-pop, #symbolDropdown) *):hover,
body #toolbar .cs-btn:hover,
body #toolbar .tf-more:hover {
  background: rgba(255, 255, 255, .12);
  border-color: rgba(255, 255, 255, .16);
  color: #fff;
}
/* Le sélecteur d'instrument est la commande centrale : rayon 12, comme
   la recherche ⌘K d'ogilio (admin.css:451). */
body #symbolWrap {
  border-radius: var(--og-radius-md);
  background: rgba(255, 255, 255, .08);
  border: 1px solid rgba(255, 255, 255, .06);
}
body #symbolWrap:hover { background: rgba(255, 255, 255, .12); }
body #symbolInput { color: var(--toolbar-fg); background: transparent; }
body #symbolInput::placeholder { color: rgba(255, 255, 255, .45); }
body #symbolWrap .sw-icon { color: rgba(255, 255, 255, .45); }

/* Les unités de temps sont un segment, pas des boutons isolés. */
body #tfPicker {
  border-radius: var(--og-radius-md);
  background: rgba(255, 255, 255, .06);
  border: 1px solid rgba(255, 255, 255, .08);
  padding: 3px;
  gap: 2px;
}
body #tfPicker button,
body #tfPicker .tf-more {
  border: none; background: transparent;
  border-radius: var(--og-radius-sm);
}
body #tfPicker button:hover,
body #tfPicker .tf-more:hover { background: rgba(255, 255, 255, .10); }
body #tfPicker button.active,
body #tfPicker button[aria-pressed="true"] {
  background: rgba(255, 255, 255, .16);
  color: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .35);
}

/* La cote en direct : pastille, chiffres tabulaires. */
body #priceBar {
  border-radius: var(--og-radius-pill);
  background: rgba(255, 255, 255, .06);
  border: 1px solid rgba(255, 255, 255, .08);
  padding-inline: 12px;
  font-variant-numeric: tabular-nums;
}

/* Le préréglage est un <select> natif : on ne le remplace pas, on
   l'habille — sa liste déroulante appartient au système. */
body #toolbar .select-wrap select {
  border-radius: var(--og-radius-md);
  background: rgba(255, 255, 255, .06);
  border: 1px solid rgba(255, 255, 255, .08);
  color: var(--toolbar-fg);
}
body #toolbar .select-wrap select option { color: #202223; background: #fff; }
:root:not([data-theme="light"]) body #toolbar .select-wrap select option { color: #e8eaed; background: #1e232b; }

/* Menus déroulants de la barre : surface relevée, ombre longue. */
body .tf-menu,
body #symbolDropdown,
body .saas-acct-pop {
  border-radius: var(--og-radius-md);
  background: var(--raised);
  border: 1px solid var(--border);
  box-shadow: 0 0 0 1px rgba(0,0,0,.04), 0 16px 40px -10px rgba(0, 0, 0, .55);
}

/* ═══ 3. BANDEAU D'INSTRUMENT ═════════════════════════════════════════
   La ligne de contexte sous la barre : nom, unité, place, flux, DXY,
   calendrier, tendance, OHLC. Rien n'est retiré — c'est une ligne
   d'état, elle doit rester dense. */
body #instOhlc,
body #statusMsg { color: var(--text-3); font-variant-numeric: tabular-nums; }

/* ═══ 4. DOCK D'ANALYSE ═══════════════ index.html:397, 5 sections ═══
   Fond PLEIN au lieu du verre dépoli : l'administration d'ogilio n'a pas
   de translucidité, ses surfaces sont franches. La logique du dock, ses
   identifiants et ses états d'ouverture ne sont pas touchés. */
body #chartDock {
  background: var(--surface-2);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: none;
  border-radius: var(--og-corner);
  box-shadow: var(--og-lift-2);
}
body #chartDock .dock-head {
  border-bottom: 1px solid var(--border);
  border-start-start-radius: var(--og-corner);
  border-start-end-radius: var(--og-corner);
}
body #chartDock .dock-label {
  font-size: 11px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-3);
}
body #chartDock .dock-item + .dock-item { border-top: 1px solid var(--border); }
body #chartDock .dock-link {
  border-radius: 0;
  transition: background .15s ease, color .15s ease;
}
body #chartDock .dock-link:hover { background: var(--og-hover); }
body #chartDock .dock-item.open > .dock-link .dock-label { color: var(--text); }
body #chartDock .dock-chevron { transition: transform .18s ease; }
body #chartDock .dock-item:last-child {
  border-end-start-radius: var(--og-corner);
  border-end-end-radius: var(--og-corner);
  overflow: hidden;
}
body #chartDock.collapsed { border-radius: var(--og-corner); }
/* Le bouton de repli reprend la pastille d'ogilio. */
body #dockToggle {
  border-radius: var(--og-radius-sm);
  transition: background .15s ease;
}
body #dockToggle:hover { background: var(--og-hover); }

/* Rangs de données du dock : filets discrets, survol par lavis, chiffres
   tabulaires. C'est la finition qui distingue un tableau d'un relevé. */
body #chartDock .tp-row,
body #chartDock .bt-stat {
  border-bottom: 1px solid var(--border);
  transition: background .12s ease;
}
body #chartDock .tp-row:hover { background: var(--og-hover); }
body #chartDock b,
body #chartDock strong { font-variant-numeric: tabular-nums; }

/* ═══ 5. RAIL DE DESSIN ═══════════════════ saas-draw.js:1806 ═════════
   15 boutons, 53 outils, 4 curseurs, 12 tampons. On habille, on ne
   déplace pas : saas-draw.js calcule la position de ses menus à partir
   de la géométrie du rail. */
body #drawRail {
  background: var(--surface-2);
  border: none;
  border-radius: var(--og-radius-md);
  box-shadow: var(--og-lift-1);
  padding: 4px;
  gap: 1px;
}
body #drawRail .draw-btn {
  border-radius: 7px;
  color: var(--text-3);
  transition: background .15s ease, color .15s ease;
}
body #drawRail .draw-btn:hover { background: var(--og-hover); color: var(--text); }
body #drawRail .draw-btn.on,
body #drawRail .draw-btn.open,
body #drawRail .draw-btn.is-active,
body #drawRail .draw-btn[aria-pressed="true"] { background: var(--text); color: var(--bg); }
body #drawRail .draw-sep { background: var(--border); }

body .draw-menu,
body .draw-style-pop {
  background: var(--raised);
  border: none;
  border-radius: var(--og-radius-md);
  box-shadow: 0 0 0 1px rgba(0,0,0,.04), 0 16px 40px -10px rgba(0, 0, 0, .55);
}
body .draw-menu-item {
  border-radius: 7px;
  color: var(--text-2);
  transition: background .12s ease, color .12s ease;
}
body .draw-menu-item:hover { background: var(--og-hover); color: var(--text); }
body .draw-menu-item.on,
body .draw-menu-item.is-active {
  background: var(--og-active); color: var(--text);
  box-shadow: inset 2px 0 0 var(--text);
}
[dir="rtl"] body .draw-menu-item.on,
[dir="rtl"] body .draw-menu-item.is-active { box-shadow: inset -2px 0 0 var(--text); }
body .draw-stamp { border-radius: 7px; }
body .draw-stamp:hover { background: var(--og-hover); }

/* ═══ 6. REJEU, COTE, MESSAGES ════════════════════════════════════════ */
body #replayBar {
  background: var(--raised);
  border: none;
  border-radius: var(--og-radius-pill);
  box-shadow: var(--og-lift-2);
}
body #replayBar .rp-btn { border-radius: 50%; transition: background .15s ease, color .15s ease; }
body #replayBar .rp-btn:hover { background: var(--og-hover); color: var(--text); }
body #replayBar .rp-pick { border-radius: var(--og-radius-pill); }
body #replayBar .rp-clock { font-variant-numeric: tabular-nums; color: var(--text-3); }
body #replayBar .rp-sep { background: var(--border); }

body #toastStack > * {
  border-radius: var(--og-radius-md);
  box-shadow: var(--og-lift-2);
}

/* ═══ 7. FENÊTRES ET CHAMPS ═══════════════════════════════════════════ */
body #settingsModal,
body #alertModal,
body #histModal,
body #ecoModal,
body #alertDeleteDialog,
body .sm-modal,
body .modal,
body .saas-modal {
  border-radius: 16px;
  background: var(--surface-2);
  border: none;
  box-shadow: 0 0 0 1px var(--border), 0 32px 72px -16px rgba(0, 0, 0, .6);
}
body input,
body select,
body textarea {
  border-radius: var(--og-radius-sm);
  transition: box-shadow .15s ease, border-color .15s ease;
}
body .badge,
body .tag,
body .pill,
body .spill { border-radius: var(--og-radius-pill); }

/* Anneaux de focus clavier. ogilio n'en a pas ; un terminal se pilote
   aussi sans souris, et le mode dessin passe par le clavier. */
body :focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
body #toolbar :focus-visible { outline-color: #fff; }

/* ═══ 8. MOUVEMENT ═══════════════════════════════════════════════════
   Dépouillement d'admin.css par fréquence : .15s ×48, .12s ×22, .1s ×21,
   .18s ×16 ; `ease` ×31, `cubic-bezier(.4,0,.2,1)` ×5. Deux échelles, et
   c'est la règle qui compte plus que les chiffres :
     micro-interaction  → .15s ease
     mouvement structurel → .3s cubic-bezier(.4,0,.2,1)
   Une durée courte sur une grande distance paraît nerveuse ; une longue
   sur un survol paraît collante. */
@keyframes ogRipple { 0% { transform: scale(0); opacity: .5 } 100% { transform: scale(4); opacity: 0 } }
@keyframes ogFadeIn { from { opacity: 0; transform: translateY(4px) } to { opacity: 1; transform: translateY(0) } }
body .tf-menu:not([hidden]),
body #symbolDropdown:not(:empty),
body .draw-menu,
body .saas-acct-pop { animation: ogFadeIn .15s ease both; }

@media (prefers-reduced-motion: reduce) {
  body #toolbar, body #chartDock, body #drawRail,
  body .tf-menu, body .draw-menu, body .saas-acct-pop { animation: none; transition: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   9. ATLAS QUI POUSSE LE CONTENU — la chorégraphie de coins d'ogilio
   ─────────────────────────────────────────────────────────────────────
   Relevé dans ai-chat-widget.php (l. 15-32 et 547-552) : à l'ouverture du
   chat, TROIS mouvements simultanés en .3s cubic-bezier(.4,0,.2,1) —

     · le panneau entre en translateX(0), coins hauts arrondis à 24 px ;
     · le contenu se rétracte de 388 px ET arrondit son coin haut-droit ;
     · la barre du haut APLATIT le sien, de 24 px à 0.

   Le troisième point est le vrai détail : les deux formes s'ÉCHANGENT le
   coin. Sans lui, l'arrondi de la barre chevaucherait le panneau, et
   l'ensemble paraîtrait empilé au lieu d'emboîté.

   Trois écarts assumés par rapport à ogilio, pour un terminal :
     1. La poussée ne s'applique qu'au-delà de 1024 px. En dessous, un
        panneau de 384 px laisserait un graphique illisible : Atlas y
        reste un calque.
     2. Le voile est retiré pendant la poussée. Un voile signifie « ce qui
        est derrière est inerte » ; ici le graphique reste utilisable, le
        voile mentirait et bloquerait un outil dont le trader a besoin
        pendant qu'il interroge l'assistant.
     3. La poussée est écrite en `margin-inline-end`, jamais en `right` :
        l'interface existe en arabe, elle doit se retourner seule.
   ═══════════════════════════════════════════════════════════════════════ */
@media (min-width: 1024px) {
  /* SEUL LE CADRE SE POUSSE, PAS LA BARRE. Le panneau commence sous la
     barre du haut : il n'a rien a lui prendre, et la pousser tassait son
     contenu — cloche, rejeu, Atlas, compte — vers la gauche a chaque
     ouverture du chat. Le gabarit ne deplace que .adm-content ; sa barre
     perd seulement son coin bas-droit, ce que fait deja la regle plus bas.

     Le meme jeton que le panneau : `--atlas-w` est declare SUR le
     panneau, donc invisible d'ici, et on retombait sur 384 px alors que
     la maquette en veut 380 — et 560 en mode large. */
  body #ogShell { transition: margin-inline-end .3s cubic-bezier(.4, 0, .2, 1); }
  body.atlas-pushed #ogShell { margin-inline-end: calc(var(--og-atlas-w, 384px) + var(--og-atlas-gap, 8px)); }

  body #toolbar {
    transition: border-end-end-radius .3s cubic-bezier(.4, 0, .2, 1),
                background .2s ease;
  }
  body.atlas-pushed #toolbar { border-end-end-radius: 0; }

  body #chartWrapper {
    overflow: hidden;            /* sans quoi le canvas déborde de l'arrondi */
  }

  /* ── LE COIN A CHANGÉ DE PORTEUR (2026-08-08) ──────────────────────
     Cette section arrondissait le coin haut-droit de `#chartWrapper` à
     l'ouverture d'Atlas. C'était juste tant que le plateau était le
     DERNIER élément de la coquille : son bord droit était le bord de
     l'écran, et c'est ce bord-là qui doit s'emboîter avec le panneau.

     La barre d'outils s'est intercalée entre les deux. Le plateau
     arrondissait donc un coin AU MILIEU de la coquille, contre le rail :
     un quart de cercle sombre — le fond de la coquille qui transparaît —
     apparaissait entre le panneau d'analyse et la barre à chaque
     ouverture d'Atlas. Mesuré : plateau 0→838, rail 838→892, et 24 px
     d'arrondi à 838.

     ET LA RÈGLE DE §10 NE POUVAIT PAS LE RATTRAPER : elle pose bien
     `border-start-end-radius: 0` au-dessus de 1024 px, mais
     `body.atlas-pushed #chartWrapper` pèse (0,2,0) contre (0,1,0) — une
     requête média n'ajoute AUCUNE spécificité. Le piège le plus fréquent
     de cette peau, une fois de plus.

     Rien ne remplace la règle : `#ogRail` porte déjà ce coin en
     permanence, parce qu'il est le bord droit de la coquille en
     PERMANENCE. La poussée le déplace, elle ne change pas sa forme. La
     chorégraphie se réduit donc à son vrai contenu — la barre du haut
     aplatit son coin, le panneau prend les siens — et il y a un
     mouvement de moins à animer. */

  body.atlas-pushed .atlas-panel {
    border-start-start-radius: var(--og-corner);
    border-start-end-radius: var(--og-corner);
  }

  /* Voir la note 2 : pas de voile tant que le contenu est utilisable. */
  body.atlas-pushed .atlas-scrim { opacity: 0 !important; pointer-events: none !important; }
}

@media (prefers-reduced-motion: reduce) {
  body #ogShell, body #toolbar, body #chartWrapper { transition: none !important; }
}

/* ═══════════════════════════════════════════════════════════════════════
   10. LA COQUILLE — élément 1
   ─────────────────────────────────────────────────────────────────────
   Le cadre d'ogilio : une barre du haut, un plateau de contenu, une
   barre d'outils à droite, et TROIS coins arrondis qui les emboîtent.

   Pourquoi en flux et non en `position: fixed` comme la maquette :
   saas-app.js injecte un bandeau d'essai de 39 px AU-DESSUS de la barre
   d'outils, qui apparaît et disparaît selon l'état du compte. Des `top`
   en dur passeraient dessous. `body` est déjà une colonne flexible —
   on s'en sert.

       body (flex column, 100dvh, overflow hidden)
         .saas-band     39 px, injecté, facultatif
         #toolbar       56 px, coin bas-droit 24
         #ogShell       le reste — flex row
           #chartWrapper  le plateau, coins hauts 24
           #ogRail        54 px, coin haut-droit 24  (tool-rail.css)

   LA COLONNE DE GAUCHE A CHANGÉ DE BORD ET DE FICHIER (2026-08-08). Elle
   ouvrait en rail de 64 px et se déployait à 240 ; elle portait six
   entrées, toutes télécommandes. Sa remplaçante — `#ogRail`, feuille
   `tool-rail.css` — fait 54 px, ne se déploie jamais, et se tient du
   même côté que le panneau qu'elle commande. Ce fichier ne garde d'elle
   que `--og-sb-bg` : la barre est de la navigation, elle doit RECULER
   par rapport au plateau, et c'est ce jeton qui le dit dans les deux
   thèmes.
   ═══════════════════════════════════════════════════════════════════════ */
:root {
  /* ── LA NAVIGATION N'A PAS LE MÊME SENS DANS LES DEUX THÈMES ───────
     En clair, ogilio la pose à #ebebeb sur un plateau #f0f0f0 : elle est
     plus SOMBRE que le contenu, donc elle recule — c'est de la
     navigation, pas du travail.
     En sombre, l'élévation est une lumière : la même intention demande
     l'inverse, une surface plus CLAIRE que le plateau. Un seul jeton, et
     chaque thème dit ce qu'il veut. */
  --og-sb-bg: var(--surface-2);   /* #1c2128 — au-dessus du plateau */
}
:root[data-theme="light"] { --og-sb-bg: #ebebeb; }   /* admin.css:562 */

body #ogShell {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: stretch;
  /* LE CADRE PORTE LA TEINTE DE LA BARRE — et il faut le jeton de la
     BARRE pour ça. `--surface` ne valait la barre qu'en SOMBRE (#0b0d11) ;
     en clair il vaut #ffffff alors que la barre est #1a1a1a, et le cadre
     apparaissait en liseré blanc le long du plateau. Le commentaire
     d'origine disait déjà « la teinte de la barre » : c'est le jeton qui
     ne suivait pas.
     C'est aussi la valeur du corps chez ogilio — le plateau clair est
     encadré de sombre, c'est leur signature. */
  background: var(--og-topbar-bg);
  gap: 0;
}

/* LE PLATEAU PREND LES DEUX COINS DU HAUT. Il ne portait que celui de
   droite : la colonne de gauche tenait l'autre. Elle est partie, et un
   coin qui manque d'un seul côté se voit tout de suite — le plateau
   paraissait de travers. Sous 1024 px la barre se retire aussi, donc les
   deux coins restent : rien à écrire dans la requête média. */
body #chartWrapper {
  flex: 1 1 0;
  min-width: 0;
  min-height: 0;
  background: var(--bg);
  border-start-start-radius: var(--og-corner);
  border-start-end-radius: var(--og-corner);
  overflow: hidden;   /* sans quoi le graphique déborde de l'arrondi */
}

/* Le coin haut-droit appartient à la barre d'outils tant qu'elle est là
   (tool-rail.css) : le plateau lui rend, sinon les deux se recouvrent et
   on lit deux rayons emboîtés au lieu d'un. */
@media (min-width: 1024px) {
  body #chartWrapper { border-start-end-radius: 0; }
}

/* ═══════════════════════════════════════════════════════════════════════
   11. LA BARRE DU HAUT — élément 2
   ─────────────────────────────────────────────────────────────────────
   Relevé sur ogilio (admin.css:303-505) : 56 px, padding 0 8px, gap 4,
   trois groupes — gauche, centre, droite. Le bouton d'icône fait 36 px,
   rayon 12 ; la recherche 36 px, rayon 12, max 480 px, centrée.

   La barre de production a déjà les bons contenus. Ce qui change ici :
     · le bouton de colonne prend la première place, comme chez ogilio ;
     · la recherche d'instrument passe au CENTRE et se borne à 480 px —
       elle occupait toute la largeur libre ;
     · les commandes se rangent à droite derrière un ressort.
   ═══════════════════════════════════════════════════════════════════════ */

/* Le bouton de colonne — .adm-tb-btn d'ogilio, au pixel. */
body #toolbar .og-burger {
  width: 36px; height: 36px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--og-radius-md);
  border: 1px solid rgba(255, 255, 255, .08);
  background: rgba(255, 255, 255, .06);
  color: rgba(255, 255, 255, .72);
  cursor: pointer; padding: 0;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
body #toolbar .og-burger:hover {
  background: rgba(255, 255, 255, .12);
  border-color: rgba(255, 255, 255, .16);
  color: #fff;
}
body #toolbar .og-burger svg { width: 18px; height: 18px; display: block; }

/* ── TROIS GROUPES, DEUX RESSORTS ──────────────────────────────────
   `margin-inline: auto` sur la recherche ne la centrait PAS : mesuré à
   454 px du centre. Une marge automatique répartit l'espace RESTANT,
   pas la largeur du conteneur — et il restait dix commandes à droite
   contre deux à gauche.

   La barre est donc groupée dans le document, avec un ressort de part et
   d'autre du centre. Les ressorts se rétractent quand la place manque :
   la recherche glisse au lieu de chevaucher, ce qu'un centrage absolu
   n'aurait pas fait sur une barre aussi dense.

   `.saas-user` est ajouté par saas-app.js APRÈS le groupe de droite. Il
   reste donc en bout de barre, ce qui est sa place chez ogilio aussi. */
body #toolbar { flex-wrap: nowrap; }
body #toolbar .og-tb-left,
body #toolbar .og-tb-right {
  display: flex; align-items: center; gap: 8px; flex: 0 0 auto; min-width: 0;
}
body #toolbar .og-tb-center {
  display: flex; align-items: center; flex: 0 1 480px; min-width: 0;
}
body #toolbar .og-spring { flex: 1 1 0; min-width: 0; }
body #toolbar #symbolWrap { max-width: 480px; width: 100%; }

/* Le ressort d'origine ne sert plus : les nôtres l'ont remplacé. */
body #toolbar .tb-spacer { display: none; }

/* Sous 1280 px la recherche cède la première : c'est la commande la plus
   large et la moins utilisée au clavier — ⌘K la rouvre. */
@media (max-width: 1279px) {
  body #toolbar .og-tb-center { flex: 0 1 260px; }
  body #toolbar .og-spring { flex: 0 1 8px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   12. — RETIRÉE LE 2026-08-08
   ─────────────────────────────────────────────────────────────────────
   « LES RUBRIQUES DE LA COLONNE » habillait `#ogSidebar .og-nav`, ses
   intertitres et ses rangs, plus le mode rail qui les réduisait à des
   icônes. Le nœud n'existe plus dans `index-ogilio.html` : ces
   soixante-dix lignes ne mordaient plus sur rien.

   La navigation est passée au bord DROIT — `#ogRail`, feuille
   `tool-rail.css`, section 1 et 2. La grammaire d'ogilio y est reprise
   (rang de 38×34 dans une piste de 54, rayon 8, survol `--og-hover`,
   actif `--og-active`), mais elle n'a plus d'intertitres à habiller :
   une barre d'icônes sépare ses rubriques par un filet, pas par un mot.

   Ce qui est parti avec elle, et pourquoi ce n'est pas une perte :
     · `--og-ico-preset` — le sélecteur de préréglages a quitté le
       terminal le 2026-08-06, l'icône n'avait plus de bouton ;
     · `body #ogSidebar #csMenu` — le style de bougies a déménagé dans la
       barre du graphique le 2026-08-03, son menu s'y ouvre depuis.
   Les deux étaient déjà mortes avant cette refonte.
   ═══════════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════════
   13. LA LIGNE DE CONTEXTE — élément 4
   ─────────────────────────────────────────────────────────────────────
   Le plateau prend enfin sa respiration : 12 px de marge et 12 px entre
   les pièces, comme la zone de contenu d'ogilio. Les bandes cessent
   d'être des bandeaux collés bord à bord et deviennent des CARTES posées
   — c'est cette grammaire-là qui fait lire l'administration ogilio, bien
   plus que sa palette.

   La ligne de contexte porte désormais tout ce qu'on lit sans y penser :
   l'instrument, l'unité, la place, l'état du flux, DXY, le calendrier,
   la tendance, l'OHLC — et la COTE, qui vient de la barre du haut.
   Elle passe de 40 à 44 px : la cote y prend le plus gros corps de la
   zone, parce que c'est le chiffre qu'on relit vingt fois par minute.
   ═══════════════════════════════════════════════════════════════════════ */

/* LA GOUTTIÈRE DU PLATEAU EST UN JETON, ET IL VIT SUR `html`.
   ─────────────────────────────────────────────────────────────────
   Il SUIT le `gap`, et il existait déjà pour ça : §34 replie le bandeau
   de signal a zero, et un enfant nul laisse ses DEUX ecarts en place ;
   la marge negative du mode plein cadre en reprend un, et sans jeton
   elle serait fausse a chaque point de rupture.

   IL A ÉTÉ HISSÉ DE `#chartWrapper` À `html` LE 2026-08-08, parce qu'un
   deuxième élément a besoin de la même valeur : la barre du haut. Depuis
   que le bouton de colonne a disparu, le LOGO est le premier objet de la
   barre, et il tombait à 8 px quand tout ce qui est en dessous — ligne de
   contexte, bandeau, graphique — tombe à 12. Quatre pixels, visibles
   d'un coup d'œil dès qu'on regarde le bord gauche. Le burger les
   masquait, il ne les corrigeait pas.

   Écrit deux fois en dur, l'écart serait revenu au premier point de
   rupture retouché : la gouttière vaut 12, puis 8 sous 640 px, puis 6 en
   paysage court. UNE valeur, deux lecteurs.

   TOUTES LES DÉCLARATIONS PASSENT PAR `html`, JAMAIS `:root` : les deux
   désignent le même élément mais `:root` est une pseudo-classe (0,1,0)
   quand `html` est un type (0,0,1). Mélangées, la base l'emporterait sur
   la surcharge de requête média — une requête média ne pèse rien. Avec le
   même sélecteur partout, c'est l'ordre du fichier qui tranche, et il est
   dans le bon sens. */
html { --og-stack-gap: 12px; }

body #chartWrapper {
  display: flex; flex-direction: column;
  padding: var(--og-stack-gap, 12px); gap: var(--og-stack-gap, 12px);
  background: var(--bg);
}

/* Le graphique devient une carte lui aussi : sans cela il resterait la
   seule pièce à fond perdu, et la ligne de contexte flotterait seule. */
body #chartStage {
  border-radius: var(--og-radius-md);
  box-shadow: var(--og-lift-1);
  overflow: hidden;
}

/* ─── La cote, à droite ────────────────────────────────────────────── */

/* ─── Le reste de la ligne ─────────────────────────────────────────── */

/* Sous 1280 px l'OHLC cède : c'est la seule information de la ligne qui
   se relit sur le graphique lui-même. */
/* ═══════════════════════════════════════════════════════════════════════
   14. LE BANDEAU DE SIGNAL — élément 5
   ─────────────────────────────────────────────────────────────────────
   C'est le bloc le plus utile de la page : la lecture du marché. Il était
   posé EN CALQUE sur le graphique, donc il masquait les bougies qu'il
   commente. Un calque cache le prix ; une ligne ne cache rien.

   Il devient la troisième carte du plateau, entre le contexte et le
   travail. Toutes ses pièces sont conservées : la flèche de direction,
   le verdict, la validation, le motif, les deux jauges — contexte et
   timing — et l'infobulle de détail.
   ═══════════════════════════════════════════════════════════════════════ */

body #chartWrapper > #tvDashboard {
  position: relative;
  inset: auto;
  width: auto; max-width: none; height: auto;
  flex: none;
  margin: 0;
  transform: none;
  border: none;
  border-radius: var(--og-radius-md);
  background: var(--surface-2);
  box-shadow: var(--og-lift-1);
  /* ── UNE SEULE CARTE, PAS DEUX ────────────────────────────────────────
     Ce conteneur portait `padding: 10px 14px` ET le bandeau qu'il contient
     porte le sien, `10px 16px`. Résultat à l'écran : une boîte dans une
     boîte, avec 20 px de gouttière verticale et 30 px avant la pastille —
     le lavis coloré du bandeau dessinait un rectangle visiblement rentré
     dans la carte grise.

     Dans le gabarit, `.signal` EST la carte : il n'y a pas d'enveloppe. On
     remet donc le rembourrage là où le gabarit le met — sur le bandeau —
     et chaque frère du conteneur reprend le sien plus bas. */
  padding: 0;
  z-index: 1;
  opacity: 1; visibility: visible;
}
/* L'infobulle de détail reste ancrée à son bandeau. */
body #chartWrapper > #tvDashboard #tvInsight { z-index: 30; }

/* « ALERTES ET REJEU, EN RANGS DE LA COLONNE » — RETIRÉ LE 2026-08-08.
   Ces deux boutons avaient quitté la colonne bien avant elle : le rejeu
   pour la barre du graphique (2026-08-03), les alertes pour la barre du
   haut. Les règles habillaient donc déjà un voisinage qui n'existait
   plus. La barre d'outils du bord droit les télécommande — elle ne les
   contient pas, elle les clique — et ses propres pastilles vivent dans
   `tool-rail.css`, section 3. */
/* ═══════════════════════════════════════════════════════════════════════
   15. LA CARTE DU GRAPHIQUE — élément 6
   ─────────────────────────────────────────────────────────────────────
   Le rail de dessin, la barre de rejeu et la cote en direct : ce qui vit
   SUR le graphique. Le graphique lui-même n'est pas touché — c'est un
   canvas piloté par lightweight-charts, il ne se style pas en CSS.

   Le rail était étiré sur toute la hauteur de la scène : 670 px de boîte
   pour 460 px d'outils, donc 46 px de vide sous la corbeille. Il épouse
   désormais son contenu. Quand la hauteur manque, il passe sur deux
   colonnes plutôt que de défiler : un défilement sans ascenseur visible
   rend des outils inatteignables sans le moindre signe.
   ═══════════════════════════════════════════════════════════════════════ */

body #chartStage > #drawRail {
  align-self: flex-start;
  margin: 8px 0 8px 8px;
  max-height: calc(100% - 16px);
  flex-wrap: wrap;
  align-content: flex-start;
  overflow: visible;
}

/* ── QUINZE OUTILS, UNE SEULE COLONNE ──────────────────────────────
   Premier essai : replier sur deux colonnes. `saas-draw.css` fixe
   `width: var(--draw-rail-w)`, et une largeur automatique sur un
   conteneur qui replie se calcule à partir de sa hauteur, elle-même
   issue de la largeur — la boîte restait à 52 px pour 85 px de contenu.
   Mesuré à 700 px de hauteur : SIX outils sur quinze hors du cadre,
   sans ascenseur ni le moindre signe. Un outil qu'on ne peut pas
   atteindre est un outil perdu.

   La bonne réponse n'était pas de replier mais de RÉDUIRE. Quinze
   boutons plus trois séparateurs demandent 15b + 35 px ; on choisit b
   pour que le compte tombe sous la hauteur disponible. Une seule
   colonne, quinze outils, à toutes les tailles d'écran. */
body #chartStage > #drawRail { flex-wrap: nowrap; width: var(--draw-rail-w); }

/* Seuils calculés, pas estimés. La scène reçoit environ la hauteur du
   viewport moins 280 px de chrome (bandeau 39, barre 56, marges 24,
   contexte 44, signal 93, écarts 24). Il faut 15b + 35 ≤ scène, donc
   b ≤ (viewport − 315) / 15. Chaque palier applique cette borne, arrondie
   au pixel inférieur. */
@media (max-height: 920px) {   /* scène ≈ 640 → b ≤ 40 */
  body #chartStage > #drawRail .draw-btn { width: 33px; height: 33px; }
  body #chartStage > #drawRail .draw-btn svg { width: 17px; height: 17px; }
}
@media (max-height: 840px) {   /* scène ≈ 560 → b ≤ 35 */
  body #chartStage > #drawRail .draw-btn { width: 29px; height: 29px; }
  body #chartStage > #drawRail .draw-btn svg { width: 16px; height: 16px; }
  body #chartStage > #drawRail .draw-sep { margin-block: 3px; }
}
@media (max-height: 760px) {   /* scène ≈ 480 → b ≤ 29 */
  body #chartStage > #drawRail .draw-btn { width: 25px; height: 25px; }
  body #chartStage > #drawRail .draw-btn svg { width: 14px; height: 14px; }
  body #chartStage > #drawRail .draw-sep { margin-block: 2px; }
}
@media (max-height: 700px) {   /* scène ≈ 420 → b ≤ 25 */
  body #chartStage > #drawRail .draw-btn { width: 22px; height: 22px; }
  body #chartStage > #drawRail .draw-btn svg { width: 13px; height: 13px; }
}
@media (max-height: 640px) {   /* scène ≈ 360 → b ≤ 21 */
  body #chartStage > #drawRail .draw-btn { width: 19px; height: 19px; }
  body #chartStage > #drawRail .draw-btn svg { width: 12px; height: 12px; }
  body #chartStage > #drawRail .draw-sep { margin-block: 1px; }
}
[dir="rtl"] body #chartStage > #drawRail { margin: 8px 8px 8px 0; }

/* La barre de rejeu flotte au-dessus du graphique : elle doit s'en
   détacher franchement, d'où l'ombre longue et la pilule pleine. */
body #chartStage > #replayBar {
  background: var(--raised);
  box-shadow: var(--og-lift-2);
  border: none;
}

/* La cote en direct garde les couleurs de marché — c'est de la donnée,
   pas du chrome. On ne touche qu'à son ancrage. */
body #chartStage #livePriceMarker { border-radius: var(--og-radius-sm); }

/* ═══════════════════════════════════════════════════════════════════════
   16. LE PANNEAU D'ANALYSE — élément 7
   ─────────────────────────────────────────────────────────────────────
   Le dock quitte le graphique, où il flottait en calque et masquait les
   bougies qu'il commente. Il devient une colonne, et ses cinq sections
   deviennent cinq ONGLETS.

   Un accordéon suppose qu'on veut tout voir ; un onglet suppose qu'on
   sait ce qu'on cherche. Et chaque section reçoit toute la hauteur de la
   colonne au lieu d'un cinquième.

   La chrome du panneau garde la surface relevée qui le fait lire comme
   une carte ; son CONTENU retrouve le fond sombre sur lequel les
   couleurs du terminal ont été calibrées.
   ═══════════════════════════════════════════════════════════════════════ */

/* LA LARGEUR DU PANNEAU N'A QU'UNE SOURCE : `--tl-panel-w`, déclarée par
   `terminal-layout.css` (340 px, 292 sous 1440). Elle y était déjà — le
   mode Disposition la lit pour dimensionner la piste du panneau quand le
   client le déplace. Cette feuille écrivait les mêmes deux nombres en
   dur : deux vérités qui ne pouvaient que diverger, et c'est arrivé
   nulle part uniquement parce que personne n'y avait touché. */
body #ogWs {
  flex: 1 1 0; min-height: 260px;
  display: grid; grid-template-columns: minmax(0, 1fr) var(--tl-panel-w, 340px);
  gap: 12px; align-items: stretch;
}
@media (max-width: 1023px) { body #ogWs { grid-template-columns: minmax(0, 1fr); } }

body #ogWs > #chartStage { min-height: 0; }

body #ogPanel {
  display: flex; flex-direction: column; min-height: 0; overflow: hidden;
  border-radius: var(--og-radius-md);
  background: var(--surface-2);
  box-shadow: var(--og-lift-1);
}
@media (max-width: 1023px) { body #ogPanel { min-height: 320px; } }

body #ogTabs {
  display: flex; gap: 2px; padding: 6px; flex: none;
  border-bottom: 1px solid var(--border);
  overflow-x: auto; scrollbar-width: none;
}
body #ogTabs::-webkit-scrollbar { display: none; }

/* L'onglet inactif n'affiche que son icône : cinq libellés côte à côte ne
   tiennent pas sous 300 px, et tronquer des mots est le contraire du soin.
   L'actif porte son nom en toutes lettres. */
body .og-tab {
  display: flex; align-items: center; gap: 6px; flex: none;
  padding: 6px 9px; border: none; background: none;
  border-radius: 7px; cursor: pointer;
  font: inherit; font-size: 11.5px; font-weight: 600;
  color: var(--text-3);
  transition: background .15s ease, color .15s ease;
}
body .og-tab:hover { background: var(--og-hover); color: var(--text); }
body .og-tab svg { width: 15px; height: 15px; flex: none; }
body .og-tab .og-tab-lbl { display: none; white-space: nowrap; }
body .og-tab.on { background: var(--text); color: var(--surface); }
body .og-tab.on .og-tab-lbl { display: inline; }

/* Le dock remplit le panneau et défile en lui-même : c'est la zone de
   contenu qui bouge, jamais la page. */
body #ogPanel > #chartDock {
  position: static;
  width: 100%; max-width: none; max-height: none; height: auto;
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  border: none; border-radius: 0; box-shadow: none;
  background: var(--chart-bg);
  backdrop-filter: none; -webkit-backdrop-filter: none;
  opacity: 1; visibility: visible; animation: none; transition: none;
  scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent;
}
body #ogPanel > #chartDock::-webkit-scrollbar { width: 8px; }
body #ogPanel > #chartDock::-webkit-scrollbar-thumb {
  background: var(--border-strong); border-radius: 99px;
  border: 2px solid transparent; background-clip: content-box;
}
/* La tête « Analyse » et les en-têtes de section font double emploi avec
   les onglets : c'est la seule chose que l'élément retire, et elle ne
   portait rien que les onglets ne portent pas. */
body #ogPanel > #chartDock .dock-head { display: none; }
/* Le bouton « Replier les panneaux » suit sa tête pour la même raison, et
   pour une deuxième : il ne REPLIE plus rien. Il pose `.collapsed` sur le
   dock, dont toutes les règles de repli (largeur 46, position absolue)
   sont reprises quelques lignes plus haut par la peau — mesuré, le dock
   garde exactement sa taille avant et après. C'était donc une bande
   cliquable de 40 px de haut, sur toute la largeur du panneau, qui ne
   faisait rien ; sous le pouce, dans le tiroir, elle se serait fait
   presser pour de bon. Le nœud reste dans le DOM : app.js lui attache un
   gestionnaire au montage. */
body #ogPanel > #chartDock > .dock-toggle { display: none; }
body #ogPanel > #chartDock .dock-item { border: none; }
body #ogPanel > #chartDock .dock-item:not(.open) { display: none; }
body #ogPanel > #chartDock .dock-item > .dock-link { display: none; }
body #ogPanel > #chartDock .dock-body { padding: 12px 14px 16px; }

/* ═══════════════════════════════════════════════════════════════════════
   17. LA FINITION — élément 8
   ─────────────────────────────────────────────────────────────────────
   La structure du gabarit est en place ; il lui manquait sa GRAMMAIRE.
   Six règles, reprises telles quelles de la maquette :

   1. Une carte ne se délimite pas par un trait, elle se DÉTACHE — trois
      ombres empilées plutôt qu'un liseré de 1 px.
   2. La hiérarchie passe par la GRAISSE et le CHIFFRE, pas par la
      couleur : un nombre important est gros, serré, tabulaire ; son
      libellé est petit, espacé, éteint.
   3. Les filets sont à 6-8 % — ils séparent, ils ne découpent pas.
   4. Les rangs vont d'un bord à l'autre et s'allument au survol : c'est
      ce qui donne la sensation de « lire à la souris ».
   5. Les pastilles n'ont pas de contour. Un contour sur un aplat teinté,
      c'est deux traits pour une seule information.
   6. Barres et jauges en dégradé, à bouts ronds. Une barre plate à angle
      vif est un rectangle ; une barre ronde est une mesure.

   Les sélecteurs visent les classes RÉELLES du terminal — relevées dans
   le DOM, pas devinées : `tp-*`, `bt-*`, `liq-*`, `dv-*`.
   ═══════════════════════════════════════════════════════════════════════ */

/* ─── 1 · La ligne de contexte ─────────────────────────────────────── */

/* ─── 2 · Le bandeau de signal ─────────────────────────────────────── */
/* Les deux jauges : dégradé, bouts ronds, chiffre serré et tabulaire. */

/* ─── 3 · Les rangs du panneau ─────────────────────────────────────── */
body #ogPanel .tp-field,
body #ogPanel .tp-foot-item,
body #ogPanel .liq-table tr,
body #ogPanel .bt-metrics > * {
  border-bottom: 1px solid var(--border);
  transition: background .12s ease;
}
body #ogPanel .tp-field:hover,
body #ogPanel .liq-table tr:hover { background: var(--og-hover); }
/* Tout ce qui est un nombre se lit en chiffres à chasse fixe : sans quoi
   les colonnes dansent à chaque rafraîchissement. */
body #ogPanel .tp-field b, body #ogPanel .tp-field .posv,
body #ogPanel .liq-val,
body #ogPanel .bt-metrics b,
body #ogPanel .bt-sample { font-variant-numeric: tabular-nums; }

/* ─── 4 · Le chiffre qui compte ────────────────────────────────────── */
body #ogPanel #tpPnl,
body #ogPanel .bt-sample {
  font-weight: 700; letter-spacing: -.035em;
}
body #ogPanel .dock-label {
  font-size: 10px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--text-3);
}

/* ─── 5 · Pastilles sans contour ───────────────────────────────────── */

/* ─── 6 · Barres et jauges ─────────────────────────────────────────── */
body #ogPanel .bt-strip { border-radius: 99px; overflow: hidden; height: 8px; }
body #ogPanel .bt-strip > * { border-radius: 0; }
body #ogPanel .bt-grades .bev,
body #ogPanel .mod-bar-track { border-radius: 99px; overflow: hidden; }
body #ogPanel .bt-dist, body #ogPanel .bt-range { border-radius: 99px; }

/* ─── Le conseil : un encart teinté sans liseré dur ────────────────── */
body #ogPanel .tp-advice {
  border: none;
  box-shadow: inset 0 0 0 1px var(--border);
  border-radius: var(--og-radius-sm);
}

/* ─── Boutons du panneau, grammaire d'ogilio ───────────────────────── */
body #ogPanel .bt-history-btn,
body #ogPanel .tp-advice-btn,
body #ogPanel .tv-insight-btn {
  border: none;
  border-radius: var(--og-radius-sm);
  box-shadow: inset 0 0 0 1px var(--border);
  transition: background .15s ease, box-shadow .15s ease, color .15s ease;
}
body #ogPanel .bt-history-btn:hover,
body #ogPanel .tp-advice-btn:hover {
  background: var(--og-hover); color: var(--text);
  box-shadow: inset 0 0 0 1px var(--border-strong);
}

/* ═══════════════════════════════════════════════════════════════════════
   18. FENETRES, MENUS ET ATLAS — élément 9
   ─────────────────────────────────────────────────────────────────────
   Les fenêtres du terminal parlent la famille `sm-*` : `sm-panel`,
   `sm-header`, `sm-body`, `sm-footer`, `sm-tabs`, `sm-row`, `sm-section`.
   Relevé dans le DOM avant d'écrire — c'est ce qui évite d'habiller des
   sélecteurs qui n'existent pas.

   Grammaire d'ogilio pour une fenêtre : rayon 16, en-tête et pied
   séparés par un FILET et non par un trait, pied légèrement creusé,
   champs à anneau intérieur, bouton principal plein.
   ═══════════════════════════════════════════════════════════════════════ */

/* ─── LA FENÊTRE EST DÉJÀ HABILLÉE PLUS HAUT (§7) ───────────────────
   `body .sm-panel` portait ici une SECONDE fois le dessin de fenêtre —
   rayon 16, anneau, ombre portée `0 32px 72px`. Or `.sm-panel` n'est pas
   la fenêtre : c'est le PANNEAU D'ONGLET, à l'intérieur du corps
   défilant. Le sélecteur existait bien, mais il ne désignait pas ce que
   la règle décrivait.

   Mesuré sur le banc : une carte blanche de 400 × 3 525 px, avec ses
   coins arrondis et une ombre de fenêtre, posée DANS un conteneur qui
   défile sur 508 px. D'où la carte-dans-la-carte de la capture, le coin
   arrondi qu'on ne voit qu'en haut, et l'ascenseur qui passe par-dessus.

   Le panneau redevient donc ce qu'il est : un simple bloc. */
body .sm-panel {
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
}
body .sm-header {
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  background: transparent;
}
body .sm-title { font-size: 14px; font-weight: 700; letter-spacing: -.015em; color: var(--text); }
body .sm-close {
  width: 32px; height: 32px; border-radius: var(--og-radius-sm);
  border: none; background: none; color: var(--text-3);
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .15s ease, color .15s ease;
}
body .sm-close:hover { background: var(--og-hover); color: var(--text); }
body .sm-body { padding: 14px 16px; }
/* Les onglets gardaient la gouttière de la feuille de base (18 px) alors
   que le corps passe à 16 : la barre de segments et le contenu ne
   tombaient pas sur la même verticale, de 2 px — assez pour se voir, pas
   assez pour se nommer. Une fenêtre n'a qu'une seule marge. */
body .sm-tabs { margin: 0 16px 6px; }
body .sm-footer {
  padding: 12px 16px;
  border-top: 1px solid var(--border);
  /* Le pied est légèrement CREUSÉ, pas surélevé : il porte des actions,
     pas du contenu — l'œil doit y aller en dernier. */
  background: rgba(0, 0, 0, .18);
  position: relative;
}
/* ── LE CONTENU NE DOIT PAS ÊTRE TRANCHÉ NET ────────────────────────
   La liste défile sur plus de trois mètres et le pied la coupait en
   plein milieu d'une ligne de texte : à l'arrêt, un intitulé sectionné
   par un trait horizontal se lit comme un défaut d'affichage, pas comme
   « ça continue ». Un fondu de 28 px au-dessus du pied dit la seconde
   chose. Il est posé sur le PIED et non sur le conteneur défilant :
   accroché au contenu, il défilerait avec lui. */
body .sm-footer::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: 100%;
  height: 28px;
  pointer-events: none;
  background: linear-gradient(to top, var(--surface-2), transparent);
}
:root[data-theme="light"] body .sm-footer { background: rgba(0, 0, 0, .03); }

/* ─── Onglets de fenêtre : un segment, pas des boutons isolés ─────── */
body .sm-tabs {
  display: flex; gap: 3px; padding: 3px;
  border-radius: var(--og-radius-sm);
  background: rgba(0, 0, 0, .22);
  box-shadow: inset 0 0 0 1px var(--border);
  border: none;
}
:root[data-theme="light"] body .sm-tabs { background: rgba(0, 0, 0, .04); }
body .sm-tab {
  flex: 1; border: none; background: none;
  border-radius: 6px; padding: 6px;
  font: inherit; font-size: 12px; font-weight: 500; color: var(--text-3);
  cursor: pointer; transition: background .15s ease, color .15s ease;
}
body .sm-tab:hover { color: var(--text); }
/* ── UN SEGMENT ACTIF DOIT PARAÎTRE SURÉLEVÉ ────────────────────────
   `var(--raised)` vaut #262c35 en sombre — plus clair que la fenêtre,
   donc au-dessus. En CLAIR il vaut #ebebeb, alors que la fenêtre est
   BLANCHE : le même jeton retournait le relief, et l'onglet actif se
   lisait creusé sous une piste plus pâle que lui (mesuré : piste à 4 %
   de noir, pastille à #ebebeb). Le jeton dit « surélevé » ; en clair il
   ne l'est que par rapport à un fond gris, ce que cette fenêtre n'a pas.
   L'ombre suivait le même défaut : `rgba(0,0,0,.35)` est calibrée pour
   du sombre et posait une salissure sur du blanc. */
body .sm-tab.active {
  background: var(--raised); color: var(--text); font-weight: 600;
  box-shadow: 0 0 0 1px var(--border), 0 1px 2px rgba(0, 0, 0, .35);
}
:root[data-theme="light"] body .sm-tab.active {
  background: var(--surface);
  box-shadow: var(--og-lift-1);
}

/* ─── Rangs et champs ──────────────────────────────────────────────── */
/* ── UN SEUL FILET PAR RANG ─────────────────────────────────────────
   La feuille de base souligne le titre de section (`border-bottom`) —
   c'est sa grammaire à elle, où les rangs n'ont PAS de filet. Ogilio
   pose l'inverse : le filet appartient au rang. Les deux règles se
   cumulaient, donc un titre de section et le rang qui le suit portaient
   la même ligne à 6 px d'écart, et la liste se lisait comme un tableau
   sans en-tête. Le titre rend son filet ; il garde l'air au-dessus, qui
   est ce qui sépare vraiment deux groupes. */
body .sm-section {
  font-size: 10px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--text-3);
  border-bottom: none;
  padding-bottom: 0;
  margin: 22px 0 2px;
}
/* Pas d'air perdu au-dessus du tout premier titre. */
body .sm-panel > .sm-section:first-child { margin-top: 2px; }

body .sm-row { border-bottom: 1px solid var(--border); transition: background .12s ease; }
body .sm-row:hover { background: var(--og-hover); }
/* Le rang qui précède un titre de section n'a plus rien à séparer : le
   blanc du titre suivant le fait mieux qu'un trait. */
body .sm-row:has(+ .sm-section) { border-bottom: none; }
body .sm-row input[type="number"],
body .sm-row input[type="text"],
body .sm-select {
  border: none;
  border-radius: var(--og-radius-sm);
  background: rgba(0, 0, 0, .24);
  box-shadow: inset 0 0 0 1px var(--border);
  color: var(--text);
  transition: box-shadow .15s ease;
}
:root[data-theme="light"] body .sm-row input[type="number"],
:root[data-theme="light"] body .sm-row input[type="text"],
:root[data-theme="light"] body .sm-select { background: var(--surface); }
body .sm-row input:focus, body .sm-select:focus {
  outline: none;
  box-shadow: inset 0 0 0 1px var(--text), 0 0 0 3px var(--og-active);
}
/* ── UNE SEULE COLONNE DE CONTRÔLES ─────────────────────────────────
   Mesuré : 88 px pour un nombre, 196,5 px pour une liste déroulante.
   Dans une pile de rangs alignés à droite, deux largeurs font un bord
   en dents de scie — on lit la colonne avant de lire les valeurs. Les
   deux partagent désormais la même laisse : un minimum commun, et la
   liste s'étire seulement si son intitulé le demande. */
body .sm-row input[type="number"],
body .sm-row input[type="text"] { width: 112px; }
body .sm-select { min-width: 112px; max-width: 208px; }

body .sm-hint {
  color: var(--text-3);
  /* Une explication appartient au réglage qu'elle explique : elle se
     range sous lui, pas au milieu de la colonne des libellés. */
  margin: -2px 0 10px;
  padding-inline-start: 1px;
}

/* ─── Boutons de pied ──────────────────────────────────────────────── */
/* Le fond passait à l'encre du texte (presque noir) mais l'OMBRE de la
   feuille de base restait bleue — `rgba(78,127,255,.28)`, taillée pour
   le bouton accent d'origine. Un bouton noir cerné d'un halo bleu :
   personne ne l'avait vu parce qu'on ne redéfinit d'ordinaire qu'un
   fond. Repeindre une surface, c'est reprendre TOUT ce qui la décrit. */
body .sm-btn-ok {
  border: none; border-radius: var(--og-radius-sm);
  background: var(--text); color: var(--surface);
  font-weight: 600; transition: opacity .15s ease;
  box-shadow: none;
}
body .sm-btn-ok:hover { opacity: .86; }
body .sm-btn-cancel {
  border: none; border-radius: var(--og-radius-sm);
  background: none; color: var(--text-3);
  box-shadow: inset 0 0 0 1px var(--border);
  transition: background .15s ease, color .15s ease, box-shadow .15s ease;
}
body .sm-btn-cancel:hover {
  background: var(--og-hover); color: var(--text);
  box-shadow: inset 0 0 0 1px var(--border-strong);
}

/* ─── Menus déroulants ─────────────────────────────────────────────── */
body #tfMenu, body #csMenu, body #symbolDropdown, body .saas-acct-pop {
  border: none;
  border-radius: var(--og-radius-md);
  background: var(--raised);
  box-shadow: 0 0 0 1px var(--border), 0 20px 48px -12px rgba(0, 0, 0, .7);
  overflow: hidden;
}
body #tfMenu button, body #csMenu button,
body #symbolDropdown li, body .saas-acct-pop .sa-row {
  border-radius: 7px;
  transition: background .12s ease, color .12s ease;
}
body #tfMenu button:hover, body #csMenu button:hover,
body #symbolDropdown li:hover, body .saas-acct-pop .sa-row:hover {
  background: var(--og-hover); color: var(--text);
}
/* La sélection se dit par un filet, pas par de la lumière : sur une
   surface déjà relevée, un aplat plus clair fait perdre du contraste au
   texte qu'il porte. */
body #tfMenu button.active, body #csMenu button.active,
body #symbolDropdown li.active {
  background: var(--og-active); color: var(--text);
  box-shadow: inset 2px 0 0 var(--text);
}
[dir="rtl"] body #tfMenu button.active,
[dir="rtl"] body #csMenu button.active,
[dir="rtl"] body #symbolDropdown li.active { box-shadow: inset -2px 0 0 var(--text); }



/* ═══════════════════════════════════════════════════════════════════════
   19. CONTEXTE ET SIGNAL — au gabarit près
   ─────────────────────────────────────────────────────────────────────
   Les deux bandes reprennent exactement la typographie et la mise en
   place de la maquette. Un seul écart, et il est de CONTENU, pas de
   dessin : le gabarit met le ticker en titre, la production y met le nom
   long — app.js écrit « XAUUSD · » avec son séparateur dans #pbSymbol,
   qui appartient au bloc de cote. Déplacer le ticker traînerait le
   séparateur, ou obligerait à réécrire ce qu'app.js produit. Le nom
   long tient donc le rôle de titre, avec le poids et la taille du
   gabarit ; le ticker reste en tête de la cote, où il se lit.
   ═══════════════════════════════════════════════════════════════════════ */

/* ─── La ligne de contexte ─────────────────────────────────────────── */

/* Les jetons : un aplat teinté et un anneau, jamais de contour plein.
   La VALEUR y est en gras, le libellé en gris — c'est elle qu'on lit. */

/* La cote : le plus gros corps de la zone. C'est le chiffre qu'on relit
   vingt fois par minute — il a droit au premier plan. */

/* ─── Le bandeau de signal ─────────────────────────────────────────── */
/* Le conteneur ne rembourre plus : c'est le bandeau qui porte la mesure du
   gabarit. Ses FRÈRES la reprennent chacun pour soi — sans quoi le tableau
   des indicateurs viendrait toucher les bords de la carte. */
body #tvDashboard { padding: 0; }
body #tvDashboard > #tvDashDetail { padding: 0 16px 10px; }
body #tvDashboard #dvMotif {
  grid-column: 1; grid-row: 2;
  /* Aligné sous le verdict, pas sous la flèche : les deux lignes de
     texte forment un bloc, la flèche est à côté d'elles. */
  padding-inline-start: 46px;
  font-size: 11.5px; color: var(--text-3);
}

body #tvDashboard #dvLabel {
  font-size: 14.5px; font-weight: 700; letter-spacing: -.022em; color: var(--text);
}
body #tvDashboard #dvValidation { font-size: 11.5px; color: var(--text-3); }

/* Les deux jauges : libellé espacé en capitales, valeur serrée et
   tabulaire, barre en dégradé à bouts ronds. */
body #tvDashboard #dvCtxFill { background: linear-gradient(90deg, color-mix(in srgb, var(--warn) 55%, transparent), var(--warn)); }
body #tvDashboard #dvEntryFill { background: linear-gradient(90deg, color-mix(in srgb, var(--up) 55%, transparent), var(--up)); }

body #tvDashboard #dvDecision,
body #tvDashboard #dvCaveat { font-size: 11px; color: var(--text-3); }


/* ═══════════════════════════════════════════════════════════════════════
   20. TROIS DEFAUTS VUS À L'ŒIL, PAS À LA MESURE
   ─────────────────────────────────────────────────────────────────────
   Toutes mes valeurs étaient conformes au gabarit et le résultat ne lui
   ressemblait pas. Une capture lisible a montré pourquoi. Leçon retenue :
   une conformité valeur par valeur ne prouve rien sur l'ensemble.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 1 · LA COTE ÉTAIT AU MILIEU ────────────────────────────────────
   L'ordre du DOM est `.inst-main`, `.og-ctx-quote`, `.inst-right` : la
   cote tombait entre l'identité et l'OHLC. Le gabarit la met tout à
   droite, après l'OHLC. `order` suffit — aucun nœud déplacé, et le sens
   de lecture arabe continue de se retourner seul. */

/* ── 2 · LA FLÈCHE N'AVAIT PAS DE CLASSE D'ÉTAT ─────────────────────
   J'avais écrit `#dvArrow.down`. L'état ne vit pas sur la flèche mais
   sur `#dashVerdict` : `dash-verdict down`. Mes règles ne matchaient
   donc jamais, et la pastille restait transparente — un « ▼ » nu là où
   le gabarit montre un carré plein. */

/* ── 3 · LES JAUGES ÉTAIENT ÉCRASÉES ────────────────────────────────
   44 px de barre au lieu de 132 : les mesureurs se laissaient comprimer
   par le conteneur souple. Elles ne se compriment plus. */

/* ── Et « En direct » n'est pas une pastille ────────────────────────
   La production en fait un jeton teinté ; le gabarit en fait du texte
   vert avec un point qui bat. Un badge de plus dans une ligne qui en
   compte déjà trois, c'est un badge de trop. */
@keyframes ogPulse { 0% { opacity: 1 } 50% { opacity: .35 } 100% { opacity: 1 } }

/* ─── La position dans le bandeau de signal ────────────────────────
   Même grammaire que les jauges : libellé espacé en capitales, chiffre
   serré et tabulaire, état en pastille teintée. Séparée des jauges par
   un filet vertical — c'est une autre nature d'information : elles
   décrivent le marché, elle décrit ce qu'on y risque. */

/* Le bandeau passe à trois colonnes : lecture · jauges · position. */

/* ═══════════════════════════════════════════════════════════════════════
   21. LA LIGNE DE CONTEXTE — LES RÈGLES DU GABARIT, TELLES QUELLES
   ─────────────────────────────────────────────────────────────────────
   Les QUARANTE-ET-UNE règles qui habillaient #instrumentStrip étaient
   dispersées en sept endroits de cette feuille, à des ordres et des
   spécificités différents. Chaque retouche en ajoutait une, et le rendu
   s'éloignait du gabarit sans que rien ne le signale — c'est exactement
   la mécanique qui a produit « c'est pas la même chose ».

   Elles sont remplacées par CE bloc, seul et en fin de feuille : à
   spécificité égale il gagne par l'ordre, et il n'y a plus qu'un endroit
   où lire ce qui habille la ligne de contexte.

   Le balisage est désormais celui de la maquette, donc ces règles sont
   les SIENNES, aux jetons de production près :

     --green / --red     →  --up / --down   (lus par app.js au montage)
     --green-bg / -bd    →  --og-up-* / --og-down-*   (définis ici)
     --r-full            →  --og-radius-pill
     admBadgePulse       →  ogPulse         (déjà dans cette feuille)

   TROIS PASTILLES SONT HABILLÉES PAR IDENTIFIANT, ET C'EST OBLIGATOIRE.
   app.js REMPLACE le className de #dxyBadge, #ecoBadge et #trendBadge à
   chaque rafraîchissement : une classe .chip posée dans le HTML serait
   effacée au premier tick. Leurs classes d'état (up/down/flat/imminent)
   les teintent. Même raison, même traitement pour #pbChange.
   ═══════════════════════════════════════════════════════════════════════ */

/* Teintes d'état. L'encre est SÉPARÉE du fond : en clair, #26A69A et
   #EF5350 sur un lavis pâle tombent à 3,1:1 et 3,4:1 — c'est le défaut
   qui rendait `.trend-badge.down` illisible à 1,60:1. Le fond garde la
   couleur de marché, l'encre est assombrie jusqu'à passer AA. */
/* SOMBRE. L'encre n'est PAS --up / --down. Mesuré sur le lavis : #26A69A
   tombe a 4,25:1 et #EF5350 a 3,86:1 pour un corps de 11 px — sous AA. Le
   gabarit avait deja tranche de la meme facon (#3ecf8e / #ff6f5e en sombre
   contre #008060 / #d82c0d en clair) : une couleur semantique doit etre
   RE-REGLEE quand le fond change, pas transposee.
   La TEINTE reste celle du marche, elle : le badge et la bougie qu'il
   decrit ne peuvent pas etre de deux verts differents. Seule la clarte
   monte. Mesures apres correction : 6,07:1 et 5,11:1.
   --up et --down ne sont pas touches — app.js les lit au montage. */
:root {
  --og-up-bg:    rgba(38, 166, 154, .13);
  --og-up-bd:    rgba(38, 166, 154, .30);
  --og-up-ink:   #3FC1B0;
  --og-down-bg:  rgba(239, 83, 80, .13);
  --og-down-bd:  rgba(239, 83, 80, .30);
  --og-down-ink: #FF6F5E;
  --og-chip-bg:  rgba(255, 255, 255, .05);
  --og-chip-bd:  rgba(255, 255, 255, .09);
}
:root[data-theme="light"] {
  --og-up-bg:    rgba(38, 166, 154, .10);
  --og-up-bd:    rgba(38, 166, 154, .24);
  --og-up-ink:   #0C6E64;
  --og-down-bg:  rgba(239, 83, 80, .10);
  --og-down-bd:  rgba(239, 83, 80, .24);
  --og-down-ink: #C62828;
  --og-chip-bg:  rgba(0, 0, 0, .04);
  --og-chip-bd:  rgba(0, 0, 0, .05);
}

/* ─── .ctx — une ligne pour ce que trois bandes disaient ──────────── */
body #instrumentStrip {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 9px 16px;
  height: auto;
  min-height: 0;
  flex: none;
  border: none;
  border-radius: var(--og-radius-md);
  background: var(--surface-2);
  box-shadow: var(--og-lift-1);
}

/* LA PASTILLE NE DIT PLUS « OUVERT » PAR DÉFAUT. Elle était verte en
   permanence, halo compris, y compris sur un marché fermé depuis deux jours.
   Le repos est neutre ; le vert est posé par `body[data-market-state="open"]`,
   que renderMarketState() n'écrit qu'après une réponse d'api/info.php. */
body #instrumentStrip .inst-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-3);
  box-shadow: 0 0 0 3px var(--og-chip-bg);
  flex-shrink: 0;
}
body[data-market-state="open"] #instrumentStrip .inst-dot {
  background: var(--up);
  box-shadow: 0 0 0 3px var(--og-up-bg);
}
body[data-market-state="closed"] #instrumentStrip .inst-dot {
  background: var(--warn);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 16%, transparent);
}

/* L'instrument EST le titre de la page. */
body #instrumentStrip .ctx-sym {
  margin: 0;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -.035em;
  line-height: 1.15;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

body #instrumentStrip .ctx-sub {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  min-width: 0;
  font-size: 12.5px;
  letter-spacing: -.005em;
  color: var(--text-3);
}
body #instrumentStrip .inst-dim { color: var(--text-3); }

body #instrumentStrip .ctx-live {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--og-up-ink);
}
/* Le gabarit dessine la pastille en ::before ; #streamDot porte déjà un
   <i> réel — autant s'en servir plutôt que d'en superposer un second. */
body #instrumentStrip .ctx-live i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  animation: ogPulse 2s ease-out infinite;
}

/* ─── Pastilles ───────────────────────────────────────────────────── */
/* #adrChip EST DANS LA LISTE PAR IDENTIFIANT, bien qu'il porte `.chip` dans
   le document. Ses trois voisines y sont parce qu'app.js réécrit leur
   className ; lui, session-structure.js ne fait que basculer `warm` /
   `hot` — mais la peau ne doit pas dépendre d'une promesse que tient un
   autre fichier. Le jour où le script écrirait `className = 'warm'`, la
   pastille perdrait sa forme sans qu'aucune erreur ne le dise.
   CONSÉQUENCE, et c'est le piège déjà payé trois fois sur cette ligne :
   un identifiant pèse (2,0,1) et BAT le `[hidden]` générique de plus bas,
   qui ne pèse que (1,1,1). La règle `#adrChip[hidden]` qui suit les états
   existe pour ça. */
body #instrumentStrip .chip,
body #instrumentStrip #adrChip,
body #instrumentStrip #dxyBadge,
body #instrumentStrip #ecoBadge,
body #instrumentStrip #trendBadge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border: none;
  border-radius: var(--og-radius-pill);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: -.005em;
  line-height: 1.45;
  white-space: nowrap;
  color: var(--text-3);
  background: var(--og-chip-bg);
  box-shadow: inset 0 0 0 1px var(--og-chip-bd);
  font-variant-numeric: tabular-nums;
}
body #instrumentStrip .chip b {
  color: var(--text-2);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}

body #instrumentStrip #dxyBadge.up,
body #instrumentStrip #trendBadge.up,
body #instrumentStrip #ecoBadge.result-up {
  color: var(--og-up-ink);
  background: var(--og-up-bg);
  box-shadow: inset 0 0 0 1px var(--og-up-bd);
}
body #instrumentStrip #dxyBadge.down,
body #instrumentStrip #trendBadge.down,
body #instrumentStrip #ecoBadge.result-down {
  color: var(--og-down-ink);
  background: var(--og-down-bg);
  box-shadow: inset 0 0 0 1px var(--og-down-bd);
}
/* Un événement imminent est la seule chose ici qui demande une action :
   il garde l'ambre, la seule couleur d'alerte de la charte. */
body #instrumentStrip #ecoBadge.imminent {
  color: var(--warn);
  background: color-mix(in srgb, var(--warn) 14%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--warn) 30%, transparent);
}

/* ─── ADR CONSOMMÉ · « ADR 82 % » ─────────────────────────────────────
   Le seul chiffre que la lecture de liquidité ajoute à la ligne. Au repos
   il est une pastille neutre comme le bilan du jour : un pourcentage
   n'est pas une alerte. Il ne prend de l'encre qu'à partir de 75 % du
   range journalier moyen (`warm`) — le jour a déjà beaucoup donné — et
   devient gras à 100 % et au-delà (`hot`) : atteindre une extension après
   110 % d'ADR n'est pas la même chose qu'après 55 %, et c'est exactement
   ce que ce nombre est là pour dire.

   L'AMBRE, ET RIEN D'AUTRE. Un premier jet aurait pris le rouge du marché
   pour « hot » — c'est le vocabulaire du REFUS (news, verrou), et un
   range épuisé n'interdit rien, il prévient. L'ambre est la seule couleur
   d'alerte de la charte, la même que #ecoBadge.imminent, #newsGuardBadge
   .is-risk et #marketBadge : pas de second vocabulaire sur la même ligne.
   `warm` et `hot` partagent le lavis, seule la graisse distingue le second
   — deux teintes d'ambre côte à côte ne se liraient pas.

   Les deux classes sont NOMMÉES SÉPARÉMENT plutôt qu'en `.warm.hot` : le
   script peut poser l'une sans l'autre, et une règle qui exigerait les
   deux s'éteindrait en silence. */
body #instrumentStrip #adrChip.warm,
body #instrumentStrip #adrChip.hot {
  color: var(--warn);
  background: color-mix(in srgb, var(--warn) 14%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--warn) 30%, transparent);
}
body #instrumentStrip #adrChip.hot { font-weight: 700; }
/* PIÈGE MAISON, payé une quatrième fois sur cette ligne : `#adrChip` par
   identifiant pèse (2,0,1), et `#adrChip.warm` (2,1,1) — tous deux BATTENT
   le `body #instrumentStrip [hidden]` générique de plus bas, à (1,1,1).
   Couche éteinte avec la classe `warm` encore posée, la pastille
   s'afficherait VIDE et ambre. Cette règle pèse (2,1,1) et vient APRÈS les
   états : à égalité c'est l'ordre qui tranche, dans le bon sens. */
body #instrumentStrip #adrChip[hidden] { display: none; }

/* PIÈGE. Le display: inline-flex posé par les règles ci-dessus BAT
   l'attribut hidden de la feuille du navigateur, qui n'est qu'un
   display: none à spécificité nulle. Sans cette ligne, le témoin de flux
   direct et la pastille du jour s'affichent VIDES en permanence — app.js
   pilote #streamDot par hidden, pas par style.display. Elle est placée
   APRÈS elles : même spécificité, c'est l'ordre qui tranche. */
body #instrumentStrip [hidden] { display: none; }

/* ─── DEUX PASTILLES RETIREES DE LA LIGNE (demande du 2026-08-03) ────
   L'evenement economique et l'etat de tendance. La ligne en portait
   quatre : a ce compte-la elle cesse d'etre un CONTEXTE pour devenir un
   tableau de bord, et le prix — la seule chose qu'on y lit vingt fois par
   minute — se retrouve a egalite avec le reste.

   MASQUEES, PAS SUPPRIMEES, et pour une raison precise : renderTrendBadge
   sort en `return` si #trendBadge est absent, AVANT d'avoir pilote
   #tvInsightBtn. Retirer le noeud casserait l'infobulle de lecture du
   marche. app.js continue donc d'ecrire dedans, sans que rien ne s'affiche.

   Rien n'est perdu : la tendance se lit dans le bandeau de signal juste
   dessous — verdict et concordance — et son detail dans l'infobulle du (!).
   Pour les remettre, supprimer ces deux lignes suffit. */
body #instrumentStrip #ecoBadge,
body #instrumentStrip #trendBadge { display: none; }

/* ─── NEWS PROTECTION · la pastille de garde ──────────────────────────────
   ELLE N'EST PAS DANS LA LISTE MASQUÉE CI-DESSUS, et c'est délibéré. Les
   deux pastilles retirées le 2026-08-03 étaient du CONTEXTE — un chiffre à
   venir, un état de tendance — et la ligne en portait trop. Celle-ci dit ce
   que le terminal REFUSE de faire maintenant : ce n'est pas du contexte,
   c'est une contrainte, et une contrainte qu'on ne voit pas ne protège
   personne.

   Elle reste discrète pour autant : en voie libre elle est plus pâle que la
   pastille du jour, et elle ne prend de l'encre que lorsqu'elle bloque.

   HABILLÉE PAR IDENTIFIANT, comme ses trois voisines : app.js REMPLACE son
   className à chaque mise à jour, une classe posée dans le document serait
   effacée au premier rafraîchissement. */
body #instrumentStrip #newsGuardBadge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border: none;
  border-radius: var(--og-radius-pill);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: -.005em;
  line-height: 1.45;
  white-space: nowrap;
  max-width: 300px;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--text-3);
  background: var(--og-chip-bg);
  box-shadow: inset 0 0 0 1px var(--og-chip-bd);
  font-variant-numeric: tabular-nums;
}
body #instrumentStrip #newsGuardBadge.is-safe,
body #instrumentStrip #newsGuardBadge.is-degraded {
  background: transparent;
  color: var(--text-3);
}
/* L'ambre est la seule couleur d'alerte de la charte — la même que celle
   qu'utilise déjà #ecoBadge.imminent, pour ne pas inventer un second
   vocabulaire d'alerte sur la même ligne. */
body #instrumentStrip #newsGuardBadge.is-risk {
  color: var(--warn);
  background: color-mix(in srgb, var(--warn) 14%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--warn) 30%, transparent);
}
body #instrumentStrip #newsGuardBadge.is-lock {
  color: var(--og-down-ink);
  background: var(--og-down-bg);
  box-shadow: inset 0 0 0 1px var(--og-down-bd);
}
body #instrumentStrip #newsGuardBadge.is-cool {
  color: var(--text-2);
  background: var(--og-chip-bg);
  box-shadow: inset 0 0 0 1px var(--og-chip-bd);
}
/* PIÈGE MAISON, déjà payé une fois plus haut : le `display: inline-flex`
   ci-dessus BAT l'attribut `hidden`, qui n'est qu'un `display: none` à
   spécificité nulle. Sans cette ligne, la pastille s'afficherait VIDE en
   permanence — app.js la pilote par `hidden` autant que par `style`. */
body #instrumentStrip #newsGuardBadge[hidden] { display: none; }

/* ─── PLAGE HORAIRE DE TRADING ─────────────────────────────────────────
   HABILLÉE PAR IDENTIFIANT, comme ses voisines : app.js REMPLACE son
   className à chaque mise à jour, une classe posée dans le document serait
   effacée au premier rafraîchissement.

   Elle n'emprunte jamais l'encre du refus (--og-down-*) : celle-ci
   appartient aux news, qui annoncent un danger de marché. Une plage
   horaire n'annonce rien — elle applique une décision de l'abonné. */
body #instrumentStrip #twBadge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border: none;
  border-radius: var(--og-radius-pill);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: -.005em;
  line-height: 1.45;
  white-space: nowrap;
  max-width: 300px;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--text-3);
  background: transparent;
  font-variant-numeric: tabular-nums;
}
body #instrumentStrip #twBadge.is-closed {
  color: var(--text-2);
  background: var(--og-chip-bg);
  box-shadow: inset 0 0 0 1px var(--og-chip-bd);
}
/* PIÈGE MAISON, déjà payé deux fois sur cette même ligne : le
   `display: inline-flex` ci-dessus BAT l'attribut `hidden`, qui n'est
   qu'un `display: none` à spécificité nulle. Sans cette règle, la
   pastille s'afficherait VIDE en permanence. */
body #instrumentStrip #twBadge[hidden] { display: none; }

/* ─── MARCHÉ FERMÉ · la pastille de séance ───────────────────────────────
   ELLE N'EST PAS DANS LA LISTE MASQUÉE PLUS HAUT, pour la même raison que la
   garde des news : les deux pastilles retirées le 2026-08-03 étaient du
   CONTEXTE, celle-ci dit pourquoi le prix ne bouge plus. Un chiffre figé sans
   explication, c'est un terminal qu'on croit cassé.

   Elle ne paraît QUE fermée — un « marché ouvert » permanent redirait ce que
   la pastille verte dit déjà, en prenant une place que la ligne n'a pas.

   L'ambre est la seule couleur d'alerte de la charte, la même que
   #ecoBadge.imminent et #newsGuardBadge.is-risk. */
body #instrumentStrip #marketBadge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border: none;
  border-radius: var(--og-radius-pill);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: -.005em;
  line-height: 1.45;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: var(--warn);
  background: color-mix(in srgb, var(--warn) 14%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--warn) 30%, transparent);
}
/* Le même piège, une troisième fois : `inline-flex` bat `hidden`. */
body #instrumentStrip #marketBadge[hidden] { display: none; }

/* ─── CE QUE LA LIGNE CESSE DE DIRE, MARCHÉ FERMÉ ────────────────────────
   Demande du user, 2026-08-08, capture à l'appui. Trois blocs occupaient la
   moitié droite du bandeau en ne décrivant rien qui puisse se produire :

     · la garde NEWS — « prochaine dans 485 min » surveille des publications
       qu'aucune séance ne jouera avant dimanche soir ;
     · le bilan du jour — « 0 trade » n'est pas un résultat, c'est la
       conséquence mécanique d'un marché fermé ;
     · l'OHLC — celui de VENDREDI, présenté au même endroit et de la même
       façon que celui du jour en cours.

   ILS SONT MASQUÉS, PAS SUPPRIMÉS : app.js et terminal-ogilio.js continuent
   d'écrire dedans, et tout revient seul à la réouverture. L'OHLC garde par
   ailleurs sa source lisible aux lecteurs d'écran (`.og-src`), qui n'est pas
   touchée — la retirer priverait d'une donnée exacte ceux qui ne voient pas
   qu'elle a été remplacée.

   L'ADR CONSOMMÉ (#adrChip) N'EST PAS DANS CETTE LISTE, et c'est voulu :
   hors marché, « ADR 82 % » reste VRAI — c'est la part du range moyen que
   la journée a réellement parcourue, elle ne devient pas fausse parce que
   le prix s'arrête. « 0 trade », lui, n'était pas un bilan mais une
   conséquence. Un premier jet l'aurait aligné sur son voisin.
   ─────────────────────────────────────────────────────────────────────── */
body[data-market-state="closed"] #instrumentStrip #newsGuardBadge,
body[data-market-state="closed"] #instrumentStrip #ogDayChip {
  display: none;
}

/* L'OHLC NE PART QUE S'IL A UN REMPLAÇANT. `data-market-reopen` n'est posé
   que lorsque le cadran tourne vraiment : sans heure de réouverture — pause
   de séance, jour férié — on laisse l'OHLC, parce qu'un vide vaut moins
   qu'une donnée datée. */
body[data-market-reopen="1"] #instrumentStrip .ctx-ohlc { display: none; }

/* ─── LE CADRAN ──────────────────────────────────────────────────────────
   Il vit du côté des CHIFFRES, avec l'OHLC qu'il remplace et la cote qu'il
   précède ; l'état, lui, reste près du symbole. Un fait, un endroit. */
body #instrumentStrip #marketReopen {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  flex: none;
  font-size: 11.5px;
  white-space: nowrap;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
}
body #instrumentStrip #marketReopen .mr-clock {
  font-size: 13px;
  font-weight: 650;
  letter-spacing: -.01em;
  color: var(--warn);
  /* La largeur du cadran ne doit pas respirer au rythme des secondes : les
     chiffres tabulaires y suffisent, sans largeur figée qui casserait à la
     bascule « 1 j 02:30:12 » → « 02:30:12 ». */
  font-variant-numeric: tabular-nums;
}
body #instrumentStrip #marketReopen[hidden] { display: none; }

/* La ligne de décision reprend l'encre de son état, comme #dvCaveat juste
   au-dessus : dans cette peau les réserves sont du texte en ligne, pas des
   pastilles — la pastille, c'est le rôle du badge. */
body #tvDashboard #dvNews {
  color: var(--og-down-ink);
  font-weight: 600;
  background: none;
  border: none;
  padding: 0;
}
body #tvDashboard #dvNews[data-state="PRE_NEWS"]           { color: var(--warn); }
body #tvDashboard #dvNews[data-state="POST_NEWS_COOLDOWN"] { color: var(--text-2); }

/* La ligne de séance suit la MÊME doctrine que #dvNews juste au-dessus : dans
   cette peau, une réserve est du texte en ligne et non une pastille — la
   pastille, c'est le rôle du badge en haut.

   Mais l'encre est NEUTRE, là où celle des news est celle du refus : une
   publication économique annonce un danger de marché, une plage horaire
   n'annonce rien du tout — elle applique une décision que l'abonné a prise
   lui-même. Deux vocabulaires distincts sur la même ligne, sans quoi on ne
   saurait plus lequel des deux exige une action. */
body #tvDashboard #dvSession {
  color: var(--text-2);
  font-weight: 600;
  background: none;
  border: none;
  padding: 0;
}
body #instrumentStrip .ctx-status {
  font-size: 11.5px;
  color: var(--text-3);
  min-width: 0;
}

/* Le ressort. `margin-inline-start: auto` du gabarit ne pousse QUE le
   premier bloc de droite ; avec deux blocs (OHLC puis cote) il faut un
   ressort explicite, sinon l'OHLC se colle à la cote. */
body #instrumentStrip .og-spring { flex: 1 1 0; min-width: 0; }

body #instrumentStrip .ctx-ohlc {
  display: flex;
  gap: 13px;
  flex: none;
  font-size: 11.5px;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
body #instrumentStrip .ctx-ohlc b {
  color: var(--text-2);
  font-weight: 650;
}

/* La SOURCE de l'OHLC. Masquée à l'œil, PAS au lecteur d'écran : la copie
   dessinée est aria-hidden, c'est donc cette ligne-ci qui porte l'OHLC
   dans l'arbre d'accessibilité. `display: none` la retirerait des deux. */
body #instrumentStrip .og-src {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ─── La cote : le seul chiffre qu'on lit vingt fois par minute ────── */
body #instrumentStrip #priceBar {
  display: flex;
  align-items: baseline;
  gap: 9px;
  flex: none;
  margin: 0;
  padding: 0;
  border: none;
  background: none;
}
body #instrumentStrip #pbPrice {
  font-size: 23px;
  font-weight: 700;
  letter-spacing: -.045em;
  line-height: 1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
body #instrumentStrip #pbChange {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px 8px;
  border-radius: var(--og-radius-pill);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: -.005em;
  white-space: nowrap;
  color: var(--text-3);
  background: var(--og-chip-bg);
  font-variant-numeric: tabular-nums;
}
body #instrumentStrip #pbChange.up   { color: var(--og-up-ink);   background: var(--og-up-bg); }
body #instrumentStrip #pbChange.down { color: var(--og-down-ink); background: var(--og-down-bg); }

/* MARCHÉ FERMÉ : LA VARIATION CESSE DE SE TEINDRE. « ▲ 0,000 (+0,00 %) » en
   vert, un samedi, décrit un marché qui monte — c'est la capture qui a lancé
   ce chantier. Le chiffre reste, il est vrai ; c'est sa couleur qui affirmait
   quelque chose.

   LES DEUX CLASSES SONT NOMMÉES, et ce n'est pas de la redondance : app.js
   REMPLACE `className` par `up` ou `down` à chaque cote, et une règle sans
   elles perdrait le départage à égalité de spécificité selon l'ordre du
   fichier. Nommées, elles gagnent d'un point, quel que soit l'ordre. */
body[data-market-state="closed"] #instrumentStrip #pbChange,
body[data-market-state="closed"] #instrumentStrip #pbChange.up,
body[data-market-state="closed"] #instrumentStrip #pbChange.down {
  color: var(--text-3);
  background: var(--og-chip-bg);
}

/* La cote était masquée sous 1180 px par style.css — parce qu'elle vivait
   dans la barre du haut, où elle n'avait plus de place. Elle a désormais
   sa ligne : la raison de la masquer a disparu avec le déménagement. */
@media (max-width: 1180px) {
  body #instrumentStrip #priceBar { display: flex; }
}

/* L'OHLC est le premier à partir : c'est la seule information de la ligne
   qui soit aussi lisible sur le graphique lui-même. */
@media (max-width: 1279px) {
  body #instrumentStrip .ctx-ohlc { display: none; }
}

@media (max-width: 859px) {
  body #instrumentStrip { gap: 9px; padding: 8px 12px; }
  body #instrumentStrip .ctx-sym { font-size: 17px; }
  body #instrumentStrip #pbPrice { font-size: 20px; }
  body #instrumentStrip .ctx-status { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  body #instrumentStrip .ctx-live i { animation: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   22. LE BANDEAU DE SIGNAL — LES RÈGLES DU GABARIT
   ─────────────────────────────────────────────────────────────────────
   Même histoire que la ligne de contexte : QUARANTE-ET-UNE règles réparties
   en six endroits, dont une grille à trois colonnes qui n'existe plus. Le
   balisage est désormais celui de la maquette, ces règles sont donc les
   siennes, et elles vivent ICI et nulle part ailleurs.

   Correspondance des jetons :
     --red / --green  →  --down / --up  (et --og-*-ink pour l'ENCRE, voir §21)
     --hair           →  --border
     --r-full / --r-sm →  --og-radius-pill / --og-radius-sm

   UNE SEULE LIBERTÉ PRISE SUR LE GABARIT, ET ELLE EST DANS SON ESPRIT.
   La maquette peint le lavis et la pastille en ROUGE en dur — sa démo est
   une vente. Son propre commentaire dit « un lavis ORIENTÉ » : la couleur
   suit donc le sens réel du signal. app.js écrit up / down / flat sur
   #dashVerdict, on s'en sert.

   PIÈGE DE SPÉCIFICITÉ, DÉJÀ TOMBÉ DEDANS UNE FOIS. L'état ne vit pas sur
   la flèche mais sur son PARENT : c'est #dashVerdict qui porte .down, pas
   #dvArrow. Écrire #dvArrow.down ne matche rien — et rien ne le signale.
   ═══════════════════════════════════════════════════════════════════════ */

/* La colonne de contenu du bandeau ne GRANDIT pas d'elle-meme : style.css
   la pose en flex: 0 1 auto, donc elle se reduit a son contenu. Tant que le
   verdict etait une grille, sa largeur maximale portait la rangee ; en flex
   avec repli, elle s'effondre a 545 px et le bandeau s'empile sur cinq
   rangs. Elle doit remplir le bandeau — le bouton de lecture du marche
   reste a cote, en flex: none. */
body #tvDashboard .tv-dash-main { flex: 1 1 auto; min-width: 0; }

/* ─── .signal — la rangée : QUOI · POURQUOI · COMBIEN · OÙ J'EN SUIS ─── */
body #tvDashboard #dashVerdict {
  display: flex;
  /* EXPLICITE, et ce n'est pas du zele. style.css pose .dash-verdict en
     COLONNE — le bandeau y etait un empilement de trois rangs. app.js
     reecrit cette classe a chaque rafraichissement, donc elle sera
     toujours la : tout ce que la rangee contredit doit etre nomme ici,
     sans quoi la valeur de style.css passe en silence. */
  flex-direction: row;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  padding: 10px 16px;
  margin: 0;
  font-size: 12px;
  line-height: 1.45;
  position: relative;
  border: none;
  background: none;
}
body #tvDashboard #dashVerdict[hidden] { display: none; }

/* Le lavis. Il part du coin de la pastille et s'éteint à mi-course : il
   colore la LECTURE, il ne remplit pas une carte. */
body #tvDashboard #dashVerdict::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: none;
}
body #tvDashboard #dashVerdict.down::before {
  background: linear-gradient(100deg, rgba(239, 83, 80, .10), rgba(239, 83, 80, 0) 46%);
}
body #tvDashboard #dashVerdict.up::before {
  background: linear-gradient(100deg, rgba(38, 166, 154, .10), rgba(38, 166, 154, 0) 46%);
}
body #tvDashboard #dashVerdict > * { position: relative; }

/* ─── QUOI. La seule pastille pleine et saturée de l'écran ─────────── */
body #tvDashboard #dvArrow {
  width: 34px;
  height: 34px;
  border-radius: 10px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  font-size: 15px;
  line-height: 1;
  background: var(--raised);
  color: var(--text-3);
}
body #tvDashboard #dashVerdict.down #dvArrow {
  background: var(--down);
  color: #1a1013;
  box-shadow: 0 2px 10px rgba(239, 83, 80, .30);
}
body #tvDashboard #dashVerdict.up #dvArrow {
  background: var(--up);
  color: #06110f;
  box-shadow: 0 2px 10px rgba(38, 166, 154, .30);
}

/* ─── POURQUOI ────────────────────────────────────────────────────── */
/* ─── LA COLONNE DE TEXTE EST CELLE QUI PLIE ────────────────────────────
   Elle portait `flex: 0 1 auto`, donc une base de calcul égale à son texte
   ENTIER. Or un repli de flex se décide sur les BASES, avant tout
   rétrécissement : dès que le verdict s'allonge — un motif plus trois
   réserves, « Ne pas entrer maintenant — Mouvement déjà étendu · Ratio
   rendement/risque insuffisant · Retest non confirmé » — la somme dépassait
   la ligne et les jauges, la position et le bouton tombaient au rang
   suivant. Le bandeau s'affichait sur deux rangs sur un écran qui avait
   pourtant la place.

   Une base COURTE et un `flex-grow` renversent la décision : la ligne se
   calcule sur 260 px, tient donc dès ~1180 px de bandeau, et la colonne
   reprend ensuite tout l'espace libre — son texte passant à la ligne À
   L'INTÉRIEUR de sa propre colonne. C'est la seule qui plie, et c'est la
   seule qui doit plier : les trois blocs de droite sont des mesures, on ne
   les casse pas en deux.

   Le repli du bandeau reste possible SOUS cette largeur : là, il n'y a
   vraiment plus la place, et c'est le comportement voulu du gabarit.
   `min-width` reste à 200 px — un texte plus étroit que ça ne se lit plus. */
body #tvDashboard .signal-txt { flex: 1 1 260px; min-width: 200px; }
body #tvDashboard #dvLabel {
  display: block;
  font-size: 14.5px;
  font-weight: 700;
  letter-spacing: -.022em;
  color: var(--text);
  /* `normal`, comme le gabarit — et c'est LA raison de l'écart de hauteur.
     `#dashVerdict` pose `line-height: 1.45` pour les jauges ; hérité ici, il
     étirait le titre à 21,0 px au lieu de 17,5. Le padding, lui, était déjà
     identique des deux côtés : mesuré, pas supposé. */
  line-height: normal;
}
body #tvDashboard .signal-sub {
  display: block;
  margin-top: 2px;
  font-size: 11.5px;
  /* Idem : 1,5 donnait 17,3 px là où le gabarit en fait 14. À eux deux, le
     titre et cette ligne gonflaient le bloc de 6,8 px et la carte de 6,3. */
  line-height: normal;
  color: var(--text-3);
}
/* Les réserves sont EN LIGNE dans le sous-titre : c'est la plus haute
   d'entre elles qui fixe la hauteur du bloc. Poser `normal` sur le parent
   ne suffisait donc pas — `#dvMotif` héritait 1,35 d'ailleurs et tirait le
   sous-titre à 15,5 px là où le gabarit en fait 14. On l'impose à tout ce
   qui vit dans cette ligne, pour qu'aucune réserve future ne rouvre l'écart. */
body #tvDashboard .signal-sub > * { display: inline; line-height: normal; }
body #tvDashboard .signal-sub > *[hidden] { display: none; }
/* ── LE MOTIF DU REFUS ÉTAIT COUPÉ NET, SANS MÊME UNE ELLIPSE ─────────
   style.css écrit `.dv-motif` en FLEX et son `b` en `nowrap` +
   `text-overflow: ellipsis` : dans un contexte flex, l'élément se
   rétrécit puis s'ellipse proprement. La ligne juste au-dessus change ce
   contexte — les enfants du sous-titre repassent en `display: inline` —
   et un `nowrap` EN LIGNE ne se rétrécit plus du tout : il déborde, et
   `#tvDashboard { overflow: hidden }` le tranche. `text-overflow` n'a
   aucun effet sur une boîte en ligne, d'où une coupure franche, sans les
   trois points qui auraient au moins dit qu'il manquait quelque chose.

   Mesuré à 320 px : le motif réclamait 505 px dans une colonne de 204.
   La phrase perdue est celle qui dit POURQUOI on ne doit pas entrer —
   la seule du bandeau qu'on ne peut pas déduire du reste.

   Il redevient donc du texte qui passe à la ligne. Le mode couché, lui,
   veut bien UNE ligne et la réclame explicitement plus bas : il y
   rétablit `nowrap`, où l'ellipse fonctionne parce que la boîte y est
   contrainte en largeur. */
body #tvDashboard .dv-motif b {
  overflow: visible;
  text-overflow: clip;
  white-space: normal;
}
/* Les séparateurs sont posés entre deux réserves VISIBLES. Écrits en HTML,
   ils réapparaîtraient en tête de ligne dès qu'une réserve se masque —
   « · motif sans volume » commençant par un point médian. Le combinateur
   frère général ne cible que ce qui SUIT un autre élément affiché. */
body #tvDashboard .signal-sub > *:not([hidden]) ~ *:not([hidden])::before {
  content: " · ";
  color: var(--text-3);
}
/* La valeur porte le poids, pas le libellé — c'est le <em> du gabarit. */
body #tvDashboard .signal-sub b,
body #tvDashboard .dv-motif-key {
  color: var(--text-2);
  font-weight: 600;
  font-style: normal;
}
/* Un refus reste un refus. Le gabarit met toute cette ligne en gris ; une
   entrée refusée y disparaîtrait, alors que c'est la seule chose de la
   ligne qui dise quoi NE PAS faire. Elle garde donc l'encre du sens. */
body #tvDashboard #dvValidation[data-state="no"] { color: var(--og-down-ink); font-weight: 600; }
body #tvDashboard #dvValidation[data-state="ok"] { color: var(--og-up-ink);   font-weight: 600; }
body #tvDashboard #dvCaveat { color: var(--warn); }


/* ─── Ce que style.css cachait faute de place ──────────────────────────
   Le bandeau était un CALQUE posé sur le graphique : il n'avait qu'une
   bande étroite, et style.css y sacrifiait des informations une par une
   quand l'écran rétrécissait — le libellé de concordance sous 1180 px, la
   jauge de contexte ENTIÈRE sous 1024 px, la réserve sous 900 px, les deux
   libellés sous 1180 px. Chacune de ces coupes était juste à l'époque.

   Le bandeau occupe désormais sa propre rangée, sur toute la largeur : la
   raison de couper a disparu avec le calque. On rend donc les quatre.

   `:not([hidden])` N'EST PAS DÉCORATIF. `body #tvDashboard #dvCtxMeter`
   pèse deux identifiants et bat `.gauge[hidden]` : sans la garde, rendre
   la jauge la rendrait AUSSI quand app.js la masque faute de données. */
body #tvDashboard .dv-meter-label:not([hidden]) { display: inline; }
body #tvDashboard #dvCtxMeter:not([hidden])     { display: block; }
body #tvDashboard #dvCaveat:not([hidden])       { display: inline; }
body #tvDashboard #dvDecision:not([hidden])     { display: inline; }

/* La décision et la réserve étaient des PASTILLES — l'une bleue cerclée,
   l'autre orange. Dans le gabarit elles sont du texte : la sous-ligne est
   une phrase, pas un présentoir à jetons. Trois pastilles côte à côte y
   pèseraient autant que le verdict lui-même, qui est au-dessus d'elles. */
body #tvDashboard #dvDecision,
body #tvDashboard #dvCaveat {
  padding: 0;
  border: none;
  border-radius: 0;
  background: none;
  font-size: inherit;
  font-weight: inherit;
  white-space: normal;
}
body #tvDashboard #dvDecision { color: var(--text-3); }

/* ─── COMBIEN. Deux jauges, jamais une ────────────────────────────── */
body #tvDashboard .gauges {
  display: flex;
  gap: 18px;
  margin-inline-start: auto;
  flex-wrap: wrap;
}
body #tvDashboard .gauge { min-width: 132px; flex: 0 0 auto; }
body #tvDashboard .gauge[hidden] { display: none; }
body #tvDashboard .gauge-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--text-3);
}
body #tvDashboard .gauge-top .dv-pct {
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: -.02em;
  text-transform: none;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
body #tvDashboard .gauge-bar {
  /* Le gabarit ecrit ses jauges en div ; la production les ecrit en span,
     parce que app.js pilote #dvCtxMeter par hidden et dataset. Une boite
     EN LIGNE ignore height : sans cette declaration la barre de 5 px prend
     la hauteur de sa ligne de texte, 18 px, et la jauge disparait dans un
     aplat gris. Les .gauge, elles, sont blockifiees d office par le flex. */
  display: block;
  height: 5px;
  border-radius: var(--og-radius-pill);
  background: var(--og-gauge-track);
  overflow: hidden;
  margin-top: 5px;
}
body #tvDashboard .gauge-bar i {
  display: block;
  height: 100%;
  width: 0;
  border-radius: var(--og-radius-pill);
  background: linear-gradient(90deg, #e1b878, var(--warn));
  transition: width .5s cubic-bezier(.16, 1, .3, 1);
}
/* Le timing porte un palier calculé par app.js (dataset.level) : faible,
   modérée, élevée. Le gabarit peignait la barre en vert parce que sa démo
   était à 78 — le palier dit mieux la même chose. */
body #tvDashboard #dvEntryMeter[data-level="ok"]  i { background: linear-gradient(90deg, #95c9b4, var(--up)); }
body #tvDashboard #dvEntryMeter[data-level="mid"] i { background: linear-gradient(90deg, #e1b878, var(--warn)); }
body #tvDashboard #dvEntryMeter[data-level="low"] i { background: linear-gradient(90deg, #e6a09e, var(--down)); }

@media (prefers-reduced-motion: reduce) {
  body #tvDashboard .gauge-bar i { transition: none; }
}

/* ─── OÙ J'EN SUIS ────────────────────────────────────────────────── */
body #tvDashboard .og-pos {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding-inline-start: 16px;
  border-inline-start: 1px solid var(--border);
}
body #tvDashboard .og-pos[hidden] { display: none; }
body #tvDashboard .og-pos-lbl {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--text-3);
}
body #tvDashboard .og-pos-val {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -.035em;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
body #tvDashboard .og-pos-val.pos { color: var(--og-up-ink); }
body #tvDashboard .og-pos-val.neg { color: var(--og-down-ink); }
body #tvDashboard .og-pos-state {
  display: inline-flex;
  align-items: center;
  padding: 3px 8px;
  border-radius: var(--og-radius-pill);
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--text-3);
  background: var(--og-chip-bg);
  box-shadow: inset 0 0 0 1px var(--og-chip-bd);
}

/* ─── Le détail des indicateurs, à une pression ───────────────────── */
body #tvDashboard #dashMore {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 7px 11px;
  border: none;
  border-radius: var(--og-radius-sm);
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  color: var(--text-3);
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--og-chip-bd), 0 1px 2px rgba(0, 0, 0, .4);
  transition: background .15s ease, color .15s ease, box-shadow .15s ease;
}
body #tvDashboard #dashMore:hover {
  color: var(--text);
  background: var(--raised);
}
body #tvDashboard #dashMore .dv-more-ico { display: none; }

/* ─── Thème clair ─────────────────────────────────────────────────── */
:root { --og-gauge-track: rgba(255, 255, 255, .09); }
:root[data-theme="light"] { --og-gauge-track: rgba(0, 0, 0, .06); }
:root[data-theme="light"] body #tvDashboard #dvArrow,
html[data-theme="light"] body #tvDashboard #dashVerdict.down #dvArrow,
html[data-theme="light"] body #tvDashboard #dashVerdict.up #dvArrow { color: #fff; }
html[data-theme="light"] body #tvDashboard #dashMore {
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--border), 0 1px 1px rgba(0, 0, 0, .03);
}
html[data-theme="light"] body #tvDashboard #dashMore:hover { background: var(--surface-2); }

/* ─── Étroit. La rangée se replie dans l'ordre de lecture : les jauges
       passent sous le texte, la position sous les jauges. ───────────── */
@media (max-width: 1279px) {
  body #tvDashboard .gauges { margin-inline-start: 0; }
}
@media (max-width: 859px) {
  body #tvDashboard #dashVerdict { gap: 12px; padding: 10px 12px; }
  body #tvDashboard .og-pos {
    padding-inline-start: 0;
    border-inline-start: none;
    flex-basis: 100%;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   23. LA BARRE D'OUTILS DU GRAPHIQUE — LES RÈGLES DU GABARIT
   ─────────────────────────────────────────────────────────────────────
   Le graphique cesse d'être un fond piloté depuis ailleurs : ses commandes
   vivent dans SA carte. C'est le geste qui fait qu'on pilote un objet et
   non une page — la grammaire d'ogilio.

   UNE GRILLE, PAS UN FLEX. #chartStage était en `flex-direction: row` :
   une barre pleine largeur ne peut pas s'y ranger, elle devient une
   troisième colonne de 220 px. Avec `flex-wrap` il faudrait ensuite
   arbitrer la hauteur des deux lignes, et `align-content: stretch` les
   égalise — la barre prendrait la moitié de la carte. La grille dit
   exactement ce qu'on veut : une rangée automatique pour la barre, une
   rangée élastique pour le reste.

   #drawRail est INJECTÉ par saas-draw.js et se retrouve premier enfant :
   c'est pourquoi les trois zones sont placées explicitement plutôt que
   laissées à l'auto-placement, qui suivrait l'ordre d'injection.

   Correspondance des jetons : --hair → --border, --r-sm → --og-radius-sm,
   --surface-3 → --raised.
   ═══════════════════════════════════════════════════════════════════════ */

body #chartStage {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  grid-template-columns: auto minmax(0, 1fr);
}
body #chartStage > .chart-bar      { grid-row: 1; grid-column: 1 / -1; }
body #chartStage > #drawRail       { grid-row: 2; grid-column: 1; }
body #chartStage > #chartContainer { grid-row: 2; grid-column: 2; height: auto; min-height: 0; }
/* Tout enfant inattendu (calque, ascenseur injecté) reste dans la zone du
   graphique au lieu de s'inventer une troisième rangée. */
body #chartStage > *:not(.chart-bar):not(#drawRail):not(#chartContainer) {
  grid-row: 2;
  grid-column: 1 / -1;
}

/* ─── .chart-bar ──────────────────────────────────────────────────── */
body .chart-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
  position: relative;
  /* PAS de z-index ici, et c'est le correctif d'un bug reel.
     Le gabarit en pose un (12) parce que sa barre FLOTTE sur le graphique.
     En production elle occupe sa propre rangee de grille : elle ne
     recouvre rien, donc elle n'a rien a surclasser.

     Un z-index sur un element positionne cree un CONTEXTE D'EMPILEMENT.
     #tfMenu et #csMenu portent z-index: 90, pose par app.js — enfermes
     dans une boite a 12, ce 90 ne les classe QU'ENTRE EUX. Face au monde
     exterieur ils valaient 12, et #drawRail, element de grille a 24,
     leur passait devant : la liste des unites s'ouvrait DERRIERE le rail.

     (Le rail est en `position: static` ; on croit souvent que z-index y
     est ignore, mais il s'applique bel et bien aux elements de flex et de
     grille.)

     Sans contexte d'empilement, les deux menus jouent leur 90 contre le
     24 du rail, dans le meme plan. Rien a surenchérir. */
  background: var(--surface-2);
}
body .chart-bar .sep {
  width: 1px;
  height: 18px;
  background: var(--border);
  flex-shrink: 0;
}
body .chart-bar .grow { flex: 1 1 0; min-width: 0; }
body .chart-read {
  font-size: 11.5px;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
body .chart-read b { color: var(--text-2); font-weight: 650; }

/* ─── Les unités de temps : un segment, pas six boutons ───────────── */
body .chart-bar #tfPicker {
  display: flex;
  align-items: center;
  gap: 8px;
  position: relative;
  flex: none;
}
body .chart-bar #ivBtns {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border-radius: var(--og-radius-sm);
  background: rgba(0, 0, 0, .28);
  box-shadow: inset 0 0 0 1px var(--og-chip-bd);
}
body .chart-bar #ivBtns .iv-btn {
  padding: 5px 11px;
  border: none;
  border-radius: 6px;
  background: transparent;
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.5;
  color: var(--text-3);
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
body .chart-bar #ivBtns .iv-btn:hover { background: var(--og-hover); color: var(--text); }
body .chart-bar #ivBtns .iv-btn.active {
  background: var(--raised);
  color: var(--text);
  font-weight: 600;
  box-shadow: 0 0 0 1px var(--og-chip-bd), 0 1px 2px rgba(0, 0, 0, .5);
}

/* ─── Les boutons à libellé ───────────────────────────────────────── */
body .chart-bar #tfMore,
body .chart-bar #csBtn,
body .chart-bar #presetSelect,
body .chart-bar #loadBtn,
body .chart-bar #bareBtn,
body .chart-bar #replayBtn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 7px 11px;
  border: none;
  border-radius: var(--og-radius-sm);
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.35;
  white-space: nowrap;
  cursor: pointer;
  color: var(--text-3);
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--og-chip-bd), 0 1px 2px rgba(0, 0, 0, .4);
  transition: background .15s ease, color .15s ease, box-shadow .15s ease;
}
body .chart-bar #tfMore:hover,
body .chart-bar #csBtn:hover,
body .chart-bar #presetSelect:hover,
body .chart-bar #loadBtn:hover,
body .chart-bar #bareBtn:hover,
body .chart-bar #replayBtn:hover {
  color: var(--text);
  background: var(--raised);
  box-shadow: inset 0 0 0 1px var(--border-strong), 0 1px 3px rgba(0, 0, 0, .5);
}
body .chart-bar #tfMore svg,
body .chart-bar #csBtn svg { width: 13px; height: 13px; flex: none; }
body .chart-bar #csIcon { display: inline-grid; place-items: center; }
body .chart-bar #csIcon svg { width: 14px; height: 14px; }

/* ─── Le bouton icône ─────────────────────────────────────────────── */
body .chart-bar #settingsBtn,
body .chart-bar #replayBtn {
  width: 30px;
  height: 30px;
  padding: 0;
  gap: 0;
  display: grid;
  place-items: center;
  flex-shrink: 0;
}
body .chart-bar #settingsBtn svg,
body .chart-bar #bareBtn svg,
body .chart-bar #replayBtn svg { width: 15px; height: 15px; }

/* Le gabarit fait de « Rafraîchir » un bouton ICÔNE. Le libellé n'est pas
   supprimé pour autant : il est masqué à l'œil et reste le NOM du bouton
   pour les lecteurs d'écran — un `display: none` le priverait de nom. */
body .chart-bar #loadBtn {
  width: 30px;
  height: 30px;
  padding: 0;
  gap: 0;
  display: grid;
  place-items: center;
  flex-shrink: 0;
}
body .chart-bar #loadBtn svg { width: 15px; height: 15px; }
body .chart-bar #loadBtn > span:not(.cs-lbl) {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ─── Le préréglage : un <select> NATIF habillé ───────────────────────
   C'est le piège noté dans le gabarit. On lui retire son apparence
   système et on redessine le chevron, mais on lui LAISSE son menu : c'est
   ce qui le rend utilisable au clavier et au doigt sans une ligne de
   script. Le gabarit dessinait un bouton ; le résultat se lit pareil. */
body .chart-bar .select-wrap { display: flex; flex: none; }
body .chart-bar #presetSelect {
  appearance: none;
  -webkit-appearance: none;
  padding-inline-end: 26px;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-size: 5px 5px, 5px 5px;
  background-position: right 12px center, right 7px center;
  background-repeat: no-repeat;
}
[dir="rtl"] body .chart-bar #presetSelect {
  padding-inline-end: 26px;
  background-position: left 7px center, left 12px center;
}
body .chart-bar #presetSelect option {
  background: var(--surface-2);
  color: var(--text);
}


/* ─── Hauteurs, et un cadre en trop ────────────────────────────────────
   #tfPicker N'EST PAS un simple groupe : style.css en fait LUI-MÊME le
   segment (fond, liseré, 3 px de marge intérieure) et y range à la fois
   les favoris et le bouton « toutes les unités ». Le gabarit les sépare :
   le segment, c'est la barre de favoris, et « 22 UT » est un bouton à
   part. Sans neutraliser le cadre extérieur on en dessine donc DEUX, et
   la barre passe de 48 à 57 px de haut.

   #tfPicker reste malgré tout en `position: relative` : c'est lui le
   repère de #tfMenu, qui s'ouvre en absolu par-dessous. */
body .chart-bar #tfPicker {
  padding: 0;
  height: auto;
  border: none;
  background: none;
  box-shadow: none;
}

body .chart-bar #ivBtns .iv-btn {
  height: 28px;
  padding: 0 11px;
  display: inline-flex;
  align-items: center;
}

/* Le gabarit tient sur deux hauteurs et deux seulement : 32 px pour ce qui
   porte un mot, 30 px pour ce qui n'a qu'une icône. */
/* Mesure prise sur le gabarit lui-meme : 29 px pour un bouton a mot,
   34 pour le segment, 30 pour une icone. On laisse donc la marge
   interieure decider de la hauteur, comme lui — sauf pour le <select>,
   dont la hauteur intrinseque depend du systeme. */
/* Un <select> se dimensionne sur son option la PLUS LONGUE : ici
   « Analyse complete » lui donnait 190 px, contre ~90 au bouton du
   gabarit. On le borne — le texte affiche est celui de l option choisie,
   toujours plus court que le maximum. */
body .chart-bar #tfMore,
body .chart-bar #csBtn,
body .chart-bar #presetSelect { height: 29px; padding-block: 0; }
body .chart-bar #presetSelect { width: 148px; }
body .chart-bar #presetSelect {
  padding-inline: 12px 26px;
  font-size: 12px;
  font-weight: 500;
}

/* « Actualiser » reste MASQUÉ. saas-terminal.css le retire avec un
   !important, et pour une bonne raison, écrite là-bas : les données se
   rafraîchissent seules toutes les 5 secondes, le prix toutes les 2. Le
   noeud reste dans le document parce qu'app.js lui attache un gestionnaire
   au démarrage. Les règles ci-dessus l'habillent au cas où la couche SaaS
   le rendrait un jour — elles ne le rendent pas elles-mêmes. */

/* ─── Thème clair ─────────────────────────────────────────────────── */
html[data-theme="light"] body .chart-bar { background: var(--surface); }
html[data-theme="light"] body .chart-bar #ivBtns {
  background: rgba(0, 0, 0, .04);
  box-shadow: inset 0 0 0 1px var(--border);
}
html[data-theme="light"] body .chart-bar #ivBtns .iv-btn.active {
  background: var(--surface);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .1);
}
html[data-theme="light"] body .chart-bar #tfMore,
html[data-theme="light"] body .chart-bar #csBtn,
html[data-theme="light"] body .chart-bar #presetSelect,
html[data-theme="light"] body .chart-bar #loadBtn,
html[data-theme="light"] body .chart-bar #settingsBtn,
html[data-theme="light"] body .chart-bar #replayBtn {
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--border), 0 1px 1px rgba(0, 0, 0, .03);
}
html[data-theme="light"] body .chart-bar #tfMore:hover,
html[data-theme="light"] body .chart-bar #csBtn:hover,
html[data-theme="light"] body .chart-bar #presetSelect:hover,
html[data-theme="light"] body .chart-bar #loadBtn:hover,
html[data-theme="light"] body .chart-bar #settingsBtn:hover {
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--border-strong), 0 1px 2px rgba(0, 0, 0, .06);
}

/* ─── Étroit ──────────────────────────────────────────────────────── */
@media (max-width: 859px) {
  body .chart-bar { gap: 6px; padding: 7px 9px; }
  body .chart-bar .sep { display: none; }
  body .chart-bar #ogCsLbl { display: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   24. LA BARRE DU HAUT ET LA LATÉRALE — LES RÈGLES DU GABARIT
   ─────────────────────────────────────────────────────────────────────
   La barre du haut est la seule pièce SOMBRE dans les deux thèmes : c'est
   la signature d'ogilio, et elle ne se relit pas avec les jetons de la
   page. Ses valeurs sont donc écrites en dur, exactement comme chez eux —
   des blancs à faible opacité sur #1a1a1a.
   ═══════════════════════════════════════════════════════════════════════ */

/* ─── La recherche ────────────────────────────────────────────────── */
/* Le gabarit fait de la recherche une RANGEE : loupe, texte, indice
   clavier, alignes. La production la construit autrement — un bloc avec
   la loupe en absolu et un champ de 208 px a la marge gauche reservee.
   Les deux marchent, mais on ne peut pas ajouter l'indice a la seconde :
   il tombe sous le champ. On repasse donc la boite en rangee, et la loupe
   rentre dans le flux. #symbolDropdown reste en absolu — d'ou le
   position: relative conserve. */
body #toolbar #symbolWrap {
  display: flex;
  align-items: center;
  height: 36px;
  gap: 8px;
  padding: 0 12px;
  border-radius: 12px;
}
body #toolbar #symbolWrap .sw-icon {
  position: static;
  transform: none;
  width: 16px;
  height: 16px;
  flex: none;
}
body #toolbar #symbolInput {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
  height: 100%;
  padding: 0;
  border: none;
  background: none;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0;
}
body #toolbar #symbolInput:hover,
body #toolbar #symbolInput:focus {
  border: none;
  outline: none;
  box-shadow: none;
  background: none;
}
body #toolbar .sw-kbd {
  display: flex;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
}
body #toolbar .sw-kbd span {
  padding: 2px 5px;
  border-radius: 4px;
  font: inherit;
  font-size: 11px;
  line-height: 1.4;
  background: rgba(255, 255, 255, .08);
  color: rgb(181, 181, 181);
}
/* Le champ ne doit pas pousser l'indice hors de la boîte. */
body #toolbar #symbolInput { flex: 1 1 auto; min-width: 0; font-size: 13px; }

/* ─── Les deux boutons d'action ───────────────────────────────────────
   Le calendrier a rejoint la cloche le 2026-08-03 ; ils partagent leur
   règle, parce que dans le gabarit ce sont des icônes de même boîte et
   que deux règles séparées auraient dérivé au premier ajustement.
   LE REJEU N'EST PLUS ICI (2026-08-04) : il est descendu dans la barre du
   graphique, dont il commande l'objet. Son nom est retiré de ces trois
   listes — un sélecteur qui ne peut plus rien atteindre est un piège pour
   la relecture suivante, qui le croira actif. */
body #toolbar .og-tb-right #alertsBtn,
body #toolbar .og-tb-right #ecoBtn {
  width: 36px;
  height: 36px;
  padding: 0;
  gap: 0;
  flex-shrink: 0;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 12px;
  background: transparent;
  color: rgba(255, 255, 255, .7);
  cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
body #toolbar .og-tb-right #alertsBtn:hover,
body #toolbar .og-tb-right #ecoBtn:hover {
  background: rgba(255, 255, 255, .08);
  color: #fff;
}
body #toolbar .og-tb-right #alertsBtn svg,
body #toolbar .og-tb-right #ecoBtn svg { width: 20px; height: 20px; display: block; }
/* Les libellés « Alerte » et « Agenda » disparaissent : dans le gabarit
   ces commandes sont des icônes. Le nom reste porté par aria-label. */
body #toolbar .og-tb-right #alertsBtn .ab-label,
body #toolbar .og-tb-right #ecoBtn .ab-label { display: none; }
body #toolbar .og-tb-right #alertsBtn .ab-dot {
  position: absolute;
  top: 7px;
  inset-inline-end: 8px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #ef4444;
}
/* La pastille du calendrier, à la même place que celle de la cloche. Elle
   garde ses deux couleurs — ambre à moins d'une heure, rouge à moins d'un
   quart d'heure — et prend le détourage de la barre : sans lui, un point
   posé sur le trait de l'icône se lit comme une partie du dessin. */
body #toolbar .og-tb-right #ecoBtn .eco-btn-dot {
  top: 7px;
  inset-inline-end: 8px;
  right: auto;
  width: 6px;
  height: 6px;
  border: 2px solid var(--og-topbar-bg);
  box-sizing: content-box;
  box-shadow: none;
}

/* « LA LATÉRALE, EN TÉLÉCOMMANDES » — RETIRÉ LE 2026-08-08.
   La colonne de gauche a été remplacée par `#ogRail`, au bord droit. Le
   MOTIF, lui, est conservé et généralisé : chaque entrée de la nouvelle
   barre clique la commande qui existe déjà, et une entrée dont la
   commande est absente ne se construit pas. Voir `tool-rail.js`. */

/* ═══════════════════════════════════════════════════════════════════════
   25. LES INFOBULLES
   ─────────────────────────────────────────────────────────────────────
   Elles remplacent le `title` du navigateur, qui met une seconde a
   paraitre, ne se style pas et ne suit pas le theme.

   POSEE EN `position: fixed` ET PLACEE PAR `transform`. En absolu elle
   serait rognee par le premier `overflow: hidden` du chemin — il y en a
   sur la barre du haut, sur les cartes et sur le rail de dessin. En fixed
   elle ne depend que de l'ecran, et `transform` evite de recalculer une
   mise en page a chaque deplacement.

   z-index 1000 : au-dessus des menus (90) et du panneau (720). Une
   infobulle qui passe SOUS ce qu'elle explique n'explique rien.
   ═══════════════════════════════════════════════════════════════════════ */
.og-tip {
  position: fixed;
  top: 0;
  /* `left`, ET SURTOUT PAS `inset-inline-start` — c'est le défaut qui
     rendait TOUTES les infobulles invisibles en arabe.

     Cette boîte n'est pas placée par la feuille : `placer()` calcule un
     point en pixels d'ÉCRAN, mesurés depuis le bord gauche, et l'applique
     en `transform`. Le zéro d'origine doit donc être le bord GAUCHE dans
     les deux sens d'écriture. `inset-inline-start` le fait basculer à
     DROITE en écriture droite-à-gauche : la bulle partait du bord droit
     puis se décalait encore vers la droite de la distance calculée.
     Mesuré à 1440 px : elle se peignait à x = 1977, soit 537 px hors de
     l'écran, avec le bon texte et la bonne taille — invisible pour les
     87 éléments du terminal.

     Un décalage logique ne se mélange pas à une coordonnée physique. Ici
     la coordonnée est physique : l'ancrage doit l'être aussi. Ce qui
     reste logique dans cette bulle, c'est `text-align: start`, et c'est
     juste : le texte arabe s'y aligne bien à droite. */
  left: 0;
  z-index: 1000;
  max-width: 280px;
  padding: 7px 10px;
  border-radius: 8px;
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.45;
  letter-spacing: -.005em;
  text-align: start;
  pointer-events: none;
  background: var(--raised);
  color: var(--text);
  box-shadow: 0 0 0 1px var(--og-chip-bd), 0 6px 20px rgba(0, 0, 0, .45);
  opacity: 0;
  transition: opacity .12s ease, translate .12s ease;
  translate: 0 -3px;
}
.og-tip.is-above { translate: 0 3px; }
.og-tip.on { opacity: 1; translate: 0 0; }
.og-tip[hidden] { display: none; }

/* En clair, une bulle sombre : elle doit se detacher du plateau ET des
   cartes blanches, qui sont deja deux surfaces claires voisines. */
html[data-theme="light"] .og-tip {
  background: #1a1a1a;
  color: #fff;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .08), 0 6px 20px rgba(0, 0, 0, .22);
}

@media (prefers-reduced-motion: reduce) {
  .og-tip { transition: none; translate: 0 0; }
}

/* ═══════════════════════════════════════════════════════════════════════
   26. LES SQUELETTES DE CHARGEMENT
   ─────────────────────────────────────────────────────────────────────
   Ce que le terminal montrait pendant sa première seconde : des cases
   vides, des tirets, un graphique noir. On ne sait pas si ça charge, si
   c'est cassé, ou s'il n'y a rien à voir. Le squelette répond aux trois
   d'un coup, et il dit AUSSI la forme de ce qui arrive — c'est ce qui
   fait qu'on attend sans s'inquiéter.

   L'état vit sur <html>, posé par le script SYNCHRONE de la tête (voir
   page_terminal). Appliqué plus tard, on verrait le vide, puis le
   squelette, puis le contenu : deux sauts au lieu d'un.

   IL NE SURVIT JAMAIS À UNE PANNE. terminal-ogilio.js le retire au
   premier prix reçu, et un garde-fou de 8 s le retire de toute façon.
   Un écran qui scintille indéfiniment est pire que le vide : il promet
   quelque chose qui ne vient pas.

   LÉGER, littéralement : un aplat et un reflet qui passe. Pas de
   rebond, pas de pulsation, rien qui attire l'œil plus que le contenu
   qui va le remplacer.
   ═══════════════════════════════════════════════════════════════════════ */
:root          { --og-skel: rgba(255, 255, 255, .07); --og-skel-hi: rgba(255, 255, 255, .13); }
:root[data-theme="light"] { --og-skel: rgba(0, 0, 0, .06);  --og-skel-hi: rgba(0, 0, 0, .11); }

@keyframes ogShimmer { from { background-position: 150% 0; } to { background-position: -150% 0; } }

html.og-loading [data-skel] {
  color: transparent !important;
  border-radius: 6px;
  background-color: var(--og-skel);
  background-image: linear-gradient(90deg, transparent, var(--og-skel-hi), transparent);
  background-size: 60% 100%;
  background-repeat: no-repeat;
  animation: ogShimmer 1.6s linear infinite;
  box-shadow: none;
}
/* Le contenu est MASQUÉ, pas supprimé : il garde sa place, donc rien ne
   bouge au moment où le squelette s'efface. */
html.og-loading [data-skel] > * { visibility: hidden; }

/* Les tailles. Chacune est celle du contenu réel, pour que la ligne ne
   change pas de hauteur ni de rythme quand les vraies valeurs arrivent. */
html.og-loading #pbSymbol { min-width: 88px;  min-height: 22px; }
html.og-loading .ctx-sub  { min-width: 190px; min-height: 17px; }
html.og-loading #ogOhlc   { min-width: 290px; min-height: 16px; }
html.og-loading #pbPrice  { min-width: 122px; min-height: 23px; }
html.og-loading #pbChange { min-width: 74px;  min-height: 17px; }

/* ─── Le bandeau de signal ────────────────────────────────────────────
   #dashVerdict est `hidden` tant qu'app.js n'a pas de verdict : il n'y a
   donc rien à habiller. Le squelette est une pièce à part, qui reprend la
   COMPOSITION du bandeau — pastille, deux lignes, deux jauges. C'est ce
   qui fait qu'on reconnaît la page avant qu'elle existe. */
/* Le vrai bandeau cede la place au squelette. Il est deja masque au
   chargement, mais rien ne garantit que le VERDICT arrive apres le premier
   PRIX : ils viennent du meme rafraichissement, dans un ordre qui ne nous
   appartient pas. Sans cette ligne, les deux s afficheraient ensemble. */
html.og-loading #dashVerdict { display: none; }

.og-skel-band { display: none; }
html.og-loading .og-skel-band {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 10px 16px;
}
html.og-loading .og-skel-band > * {
  border-radius: 6px;
  background-color: var(--og-skel);
  background-image: linear-gradient(90deg, transparent, var(--og-skel-hi), transparent);
  background-size: 60% 100%;
  background-repeat: no-repeat;
  animation: ogShimmer 1.6s linear infinite;
}
html.og-loading .og-skel-band .s-dir { width: 34px; height: 34px; border-radius: 10px; flex: none; }
html.og-loading .og-skel-band .s-l1  { width: 168px; height: 15px; }
html.og-loading .og-skel-band .s-l2  { width: 232px; height: 12px; }
html.og-loading .og-skel-band .s-txt { display: flex; flex-direction: column; gap: 7px; background: none; animation: none; }
html.og-loading .og-skel-band .s-g   { width: 132px; height: 28px; margin-inline-start: auto; }
html.og-loading .og-skel-band .s-g + .s-g { margin-inline-start: 0; }

/* ─── Le graphique ────────────────────────────────────────────────────
   Un voile, pas un aplat : le fond du graphique reste visible dessous, et
   les premières bougies apparaissent à travers plutôt que d'un coup. */
html.og-loading #chartContainer::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background-color: var(--og-skel);
  background-image: linear-gradient(90deg, transparent, var(--og-skel-hi), transparent);
  background-size: 40% 100%;
  background-repeat: no-repeat;
  animation: ogShimmer 1.6s linear infinite;
}

/* Le reflet est le seul mouvement de l'écran : c'est aussi le premier à
   partir quand on demande moins d'animation. L'aplat, lui, reste — il
   porte l'information « ça charge », et elle n'est pas décorative. */
@media (prefers-reduced-motion: reduce) {
  html.og-loading [data-skel],
  html.og-loading .og-skel-band > *,
  html.og-loading #chartContainer::after {
    animation: none;
    background-image: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   27. ATLAS — LE PANNEAU DE CONVERSATION, AU GABARIT
   ─────────────────────────────────────────────────────────────────────
   Les valeurs viennent de la maquette (#aic-panel), la structure est celle
   qu'atlas.js construit. Table de correspondance :

     #aic-panel        .atlas-panel        #aic-messages   .atlas-body
     #aic-header       .atlas-head         .aic-msg-group  .atlas-row
     .aic-hd-title     .atlas-title        .aic-bubble     .atlas-bubble
     .aic-hd-btn       .atlas-btn          .ai-thinking-…  .atlas-dots
     #aic-welcome      .atlas-welcome      #aic-footer     .atlas-foot
     .aic-wl-avatar    .atlas-mark         #aic-input      .atlas-input
     .aic-wl-name      .atlas-hello        #aic-footer-hint .atlas-note
     .aic-wl-question  .atlas-sub
     .aic-wl-pill      .atlas-pill

   TROIS COMMANDES DE LA MAQUETTE N'EXISTENT PAS ICI, et je ne les invente
   pas : « Vérifier » dans l'en-tête, « Joindre » et « Dicter » dans le
   pied. Aucune n'a de mécanique derrière — un bouton qui ne fait rien se
   paie en confiance, et c'est la même règle que pour la cote de la barre
   du haut. Atlas a en revanche trois choses que la maquette n'a pas :
   l'historique des conversations, la jauge de quota et l'état verrouillé.
   Elles sont habillées dans la même grammaire.
   ═══════════════════════════════════════════════════════════════════════ */

/* ─── Le panneau ──────────────────────────────────────────────────────
   La maquette lui donne 380 px, des coins hauts arrondis à 24 px, et le
   fait démarrer SOUS la barre du haut — atlas.js pose déjà `--tb-bottom`
   pour ça. Le coin arrondi ne se voit QUE parce que le panneau s'arrête
   au-dessous de la barre : posé en pleine hauteur, il n'aurait rien à
   arrondir. */
:root {
  --og-atlas-w: 380px;
  /* 8 px de gouttiere, comme --adm-push (388) face a --adm-panel-w (380). */
  --og-atlas-gap: 8px;
}
body:has(.atlas-panel.is-wide) { --og-atlas-w: 560px; }

body .atlas-panel {
  --atlas-w: var(--og-atlas-w);
  border-inline-start: none;
  border-start-start-radius: 24px;
  border-start-end-radius: 24px;
  overflow: hidden;
  background: var(--surface-2);
  /* Pas d'ombre ici : au-dessus de 1024 px le panneau POUSSE le contenu,
     il n'a rien a survoler. Son ombre — 8 px de decalage plus 40 px de
     flou, soit 48 px de portee a gauche — ne faisait que baver sur la
     gouttiere et sur le bord du terminal. Elle revient plus bas, la ou il
     recouvre : c'est exactement ce que fait le gabarit (ligne 1071). */
  box-shadow: none;
}
body .atlas-panel.is-wide { --atlas-w: 560px; }

/* Sous 1024 px le panneau RECOUVRE au lieu de pousser — pousser le
   contenu d'un telephone revient a le faire disparaitre. C'est le seul
   cas ou il a quelque chose sous lui, donc le seul ou une ombre le
   detache. */
@media (max-width: 1023px) {
  body .atlas-panel {
    --atlas-w: min(380px, 92vw);
    box-shadow: -8px 0 40px rgba(0, 0, 0, .38);
  }
  html[data-theme="light"] body .atlas-panel { box-shadow: -8px 0 40px rgba(0, 0, 0, .18); }
}

/* ─── L'en-tête ───────────────────────────────────────────────────── */
body .atlas-head {
  height: 52px;
  padding-inline: 16px 10px;
  gap: 6px;
  border-block-end: 1px solid var(--border);
  background: var(--surface-2);
}
body .atlas-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  gap: 6px;
}
body .atlas-title .atlas-caret { width: 18px; height: 18px; color: var(--text-3); }
body .atlas-acts { gap: 1px; }
body .atlas-btn {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  color: var(--text-3);
  transition: background .12s ease, color .12s ease;
}
body .atlas-btn:hover { background: var(--og-hover); color: var(--text); }
body .atlas-btn svg { width: 18px; height: 18px; }

/* ─── L'accueil ───────────────────────────────────────────────────── */
body .atlas-welcome { padding: 32px 24px 24px; }
/* L'orbe. La maquette en fait un dégradé pur ; on garde la marque Atlas
   PAR-DESSUS, parce qu'une identité de produit ne se remplace pas par une
   décoration. Même silhouette, même animation, même taille. */
body .atlas-welcome .atlas-mark {
  width: 64px;
  height: 64px;
  margin: 0 auto 20px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  color: #1a1a1a;
  background:
    radial-gradient(circle at 32% 28%, #fde68a, transparent 45%),
    radial-gradient(circle at 70% 30%, #a5b4fc, transparent 48%),
    radial-gradient(circle at 50% 78%, #f9a8d4, transparent 52%),
    linear-gradient(140deg, #e0e7ff, #fce7f3);
  animation: ogOrb 9s ease-in-out infinite;
}
body .atlas-welcome .atlas-mark svg { width: 30px !important; height: 30px !important; }
@keyframes ogOrb {
  0%, 100% { transform: scale(1) rotate(0); }
  50%      { transform: scale(1.05) rotate(8deg); }
}

body .atlas-hello {
  font-size: 18px;
  font-weight: 600;
  color: var(--text-3);
  margin-bottom: 4px;
}
body .atlas-sub {
  font-size: 18px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--text);
  margin-bottom: 24px;
}
body .atlas-pills { gap: 8px; }
body .atlas-pill {
  padding: 4px 16px;
  gap: 6px;
  border: none;
  border-radius: 20px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-2);
  background: var(--og-chip-bg);
  transition: background .15s ease;
}
body .atlas-pill:hover { background: var(--og-active); }
/* La pastille de tête : c'est elle qui fait lire « suggestion » plutôt que
   « bouton ». Elle prend la couleur de la marque. */
body .atlas-pill::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--text);
  flex-shrink: 0;
}

/* ─── Les messages ────────────────────────────────────────────────── */
body .atlas-body { padding: 20px 16px; gap: 20px; display: flex; flex-direction: column; }
/* NE PAS toucher a l'alignement des rangs. atlas.css met le rang
   utilisateur en `flex-direction: row-reverse` — c'est deja ce qui colle
   sa bulle a droite. Un `justify-content: flex-end` par-dessus fait
   l'inverse de ce qu'on croit : dans un axe INVERSE, la fin est a
   GAUCHE. Premiere version : le message de l'utilisateur revenait se
   ranger a gauche, sous celui d'Atlas. */
body .atlas-row { width: 100%; }
/* La maquette ne met PAS d'avatar à côté des réponses : la position et la
   couleur de la bulle disent déjà qui parle, et l'avatar répété tous les
   deux paragraphes hache la lecture. */
body .atlas-row .atlas-av { display: none; }
body .atlas-bubble {
  max-width: 88%;
  padding: 10px 13px;
  border-radius: 14px 14px 14px 5px;
  border: none;
  font-size: 13.5px;
  line-height: 1.55;
  background: var(--raised);
  color: var(--text);
}
/* Le rayon COMPLET, pas seulement les coins du bas. atlas.css pose sa
   propre forme sur `.atlas-row.is-user .atlas-bubble`, plus specifique
   que mon `.atlas-bubble` : les coins hauts lui restaient et valaient 10
   au lieu de 14. Une regle qui n'ecrit que la moitie d'une propriete
   laisse l'autre moitie a la feuille d'en dessous. */
body .atlas-row.is-user .atlas-bubble {
  border-radius: 14px 14px 5px 14px;
  background: var(--text);
  color: var(--surface-2);
}
[dir="rtl"] body .atlas-row.is-user .atlas-bubble { border-radius: 14px 14px 14px 5px; }
[dir="rtl"] body .atlas-bubble { border-radius: 14px 14px 5px 14px; }
body .atlas-dots { display: flex; gap: 5px; padding: 6px 2px; }
body .atlas-dots i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--text-3);
  animation: ogDot .9s infinite ease-in-out;
}
body .atlas-dots i:nth-child(2) { animation-delay: .18s; }
body .atlas-dots i:nth-child(3) { animation-delay: .36s; }
@keyframes ogDot {
  0%, 80%, 100% { transform: scale(.7); opacity: .5; }
  40%           { transform: scale(1);  opacity: 1; }
}

/* ─── Le pied ─────────────────────────────────────────────────────── */
body .atlas-foot {
  padding: 10px 14px 12px;
  gap: 8px;
  border-block-start: 1px solid var(--border);
  background: var(--surface-2);
}
/* Le champ est SEUL sur sa ligne, les commandes en dessous : c'est la
   composition du gabarit, et elle rend au champ toute la largeur. */
body .atlas-form {
  display: block;
  padding: 0 2px;
  border: none;
  background: none;
}
body .atlas-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 2px;
}
body .atlas-bar-spring { flex: 1 1 0; }
body .atlas-mic {
  width: 32px;
  height: 32px;
  flex: none;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--text-3);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background .12s ease, color .12s ease;
}
body .atlas-mic:hover { background: var(--og-hover); color: var(--text); }
body .atlas-mic svg { width: 20px; height: 20px; }
/* En dictee, le bouton porte la couleur d'un enregistrement en cours —
   c'est le seul moment ou le micro est actif, il doit se voir. */
body .atlas-mic.is-on { color: var(--og-down-ink); background: var(--og-down-bg); }
html[data-theme="light"] body .atlas-mic { color: #9ca3af; }
html[data-theme="light"] body .atlas-mic:hover { background: #f3f4f6; color: #374151; }
body .atlas-input {
  /* Le conteneur est en BLOC depuis que le champ y est seul : un
     flex: 1 ne veut plus rien dire, il faut une largeur. Sans elle le
     champ gardait 169 px sur les 352 disponibles. */
  width: 100%;
  display: block;
  min-width: 0;
  padding: 0;
  border: none;
  outline: none;
  background: transparent;
  resize: none;
  max-height: 90px;
  font: inherit;
  font-size: 14px;
  line-height: 1.45;
  color: var(--text);
}
body .atlas-input::placeholder { color: var(--text-3); }
body .atlas-send {
  width: 32px;
  height: 32px;
  flex: none;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--text-3);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background .12s ease, color .12s ease;
}
body .atlas-send:hover { background: var(--og-hover); color: var(--text); }
body .atlas-send svg { width: 20px; height: 20px; }
body .atlas-note {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .02em;
  color: var(--text-3);
}
/* Le point dégradé de la maquette. Il n'existe pas dans le balisage
   d'Atlas : on le dessine en ::before plutôt que d'ajouter un nœud à un
   script qu'on ne touche pas. */
body .atlas-note::before {
  content: "";
  width: 14px;
  height: 14px;
  border-radius: 50%;
  flex-shrink: 0;
  background: linear-gradient(140deg, #a5b4fc, #f9a8d4);
}

/* ─── Ce qu'Atlas a en plus, dans la même grammaire ───────────────── */
body .atlas-hist { background: var(--surface); border-block-end: 1px solid var(--border); }
body .atlas-hist-item { padding: 10px 14px; font-size: 13px; }
body .atlas-hist-item:hover { background: var(--og-hover); }
body .atlas-quota-bar { border-radius: 20px; }

/* ─── Thème clair : les valeurs EXACTES de la maquette ─────────────── */
html[data-theme="light"] body .atlas-panel { background: #fff; }
html[data-theme="light"] body .atlas-head,
html[data-theme="light"] body .atlas-foot { background: #fff; border-color: #f0f0f0; }
html[data-theme="light"] body .atlas-title { color: #111827; }
html[data-theme="light"] body .atlas-title .atlas-caret,
html[data-theme="light"] body .atlas-btn,
html[data-theme="light"] body .atlas-send { color: #9ca3af; }
html[data-theme="light"] body .atlas-btn:hover,
html[data-theme="light"] body .atlas-send:hover { background: #f3f4f6; color: #374151; }
html[data-theme="light"] body .atlas-hello { color: #888c90; }
html[data-theme="light"] body .atlas-sub { color: #111827; }
html[data-theme="light"] body .atlas-pill { background: #f3f4f6; color: #374151; }
html[data-theme="light"] body .atlas-pill:hover { background: #e5e7eb; }
html[data-theme="light"] body .atlas-pill::before { background: #1a1a1a; }
html[data-theme="light"] body .atlas-bubble { background: #f6f6f6; color: #111827; }
html[data-theme="light"] body .atlas-row.is-user .atlas-bubble { background: #111827; color: #fff; }
html[data-theme="light"] body .atlas-dots i { background: #8c9196; }
html[data-theme="light"] body .atlas-input { color: #111827; }
html[data-theme="light"] body .atlas-input::placeholder { color: #9ca3af; }

@media (prefers-reduced-motion: reduce) {
  body .atlas-welcome .atlas-mark,
  body .atlas-dots i { animation: none; }
  body #ogShell { transition: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   28. LES NOTIFICATIONS — AU GABARIT
   ─────────────────────────────────────────────────────────────────────
   Deux choses portent ce nom dans la maquette : la PILE de messages en bas
   à droite, et la PASTILLE de la cloche.

   La pile change de place ET de nature. Elle était en haut à droite, en
   carte de verre de 304 px avec un liseré de couleur à gauche ; la
   maquette en fait une pastille sombre compacte, en bas à droite, qui
   monte à l'apparition. Le bas de l'écran est le bon endroit : en haut
   elle recouvrait la cote et la barre d'outils, c'est-à-dire ce qu'on
   regarde au moment précis où une alerte tombe.

   CE QUI RESTE DE LA PRODUCTION : la COULEUR de l'icône par nature
   d'alerte (signal, actualité, prix, entrée, TP, SL). La maquette n'a
   qu'un toast générique ; ici la couleur porte une information, elle ne
   décore pas. Le liseré de gauche, lui, disparaît : l'icône dit déjà tout.
   ═══════════════════════════════════════════════════════════════════════ */

/* Le fond de la barre du haut devient un jeton : la pastille de la cloche
   a besoin de la MÊME valeur pour se détourer dessus. */
:root                     { --og-topbar-bg: #0b0d11; }
:root[data-theme="light"] { --og-topbar-bg: #1a1a1a; }

/* ─── La pile ─────────────────────────────────────────────────────── */
body #toastStack {
  top: auto;
  bottom: 16px;
  right: auto;                 /* la règle de base la pose en physique */
  inset-inline-end: 16px;
  z-index: 950;
  width: auto;
  max-width: calc(100vw - 32px);
  align-items: flex-end;
  gap: 8px;
}
body .toast {
  width: auto;
  max-width: 340px;
  align-items: center;
  gap: 9px;
  padding: 9px 13px;
  border: none;
  border-radius: 10px;
  background: #18181b;
  color: #fff;
  font-size: 12.5px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .28);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  transform: translateY(6px);
}
body .toast.show { transform: none; }
body .toast-ico { margin-top: 0; }
body .toast-ico svg { width: 15px; height: 15px; }
body .toast-title { font-size: 12.5px; font-weight: 600; }
body .toast-body { font-size: 11.5px; color: rgba(255, 255, 255, .7); }

/* ─── La pastille de la cloche ────────────────────────────────────── */
body #alertsBtn .ab-dot {
  top: 7px;
  inset-inline-end: 7px;
  right: auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #ef4444;
  /* Le détourage : sans lui, un point rouge posé sur le trait de la
     cloche se lit comme une partie du dessin. Il porte la couleur de la
     barre, d'où le jeton. */
  border: 2px solid var(--og-topbar-bg);
  box-sizing: content-box;
}
/* L'onde. C'est elle qui fait remarquer une alerte qui vient d'arriver
   sans rien clignoter : elle part du point et s'efface. */
body #alertsBtn .ab-dot::after {
  content: "";
  position: absolute;
  inset: -2px;
  border-radius: 50%;
  border: 1px solid #ef4444;
  animation: ogBadgePulse 2s ease-out infinite;
}
@keyframes ogBadgePulse {
  0%   { transform: scale(1);   opacity: .7; }
  70%  { transform: scale(2.2); opacity: 0; }
  100% { opacity: 0; }
}

/* La cloche sonne au survol — un détail de la maquette, et le seul
   endroit du terminal où une icône bouge sans qu'on ait cliqué. */
body #alertsBtn:hover svg {
  animation: ogBellRing .9s ease;
  transform-origin: 50% 15%;
}
@keyframes ogBellRing {
  0%        { transform: rotate(0); }
  10%       { transform: rotate(14deg); }
  20%       { transform: rotate(-8deg); }
  30%       { transform: rotate(14deg); }
  40%       { transform: rotate(-4deg); }
  50%       { transform: rotate(10deg); }
  60%, 100% { transform: rotate(0); }
}

@media (prefers-reduced-motion: reduce) {
  body #alertsBtn .ab-dot::after,
  body #alertsBtn:hover svg { animation: none; }
}

/* ─── LA DÉCOUPE DU COIN DE LA BARRE ──────────────────────────────────
   La barre du haut a un coin bas-droit arrondi de 24 px. Ce que la
   découpe laisse voir, c'est le <body> — et il portait `--bg`, la couleur
   du PLATEAU (#15181f), alors que juste en dessous #ogShell porte
   `--surface` (#0b0d11). D'où le petit triangle plus clair coincé entre
   la barre et le cadre, visible seulement là.

   Le body prend donc la teinte du cadre qu'il borde. C'est la seule
   surface où il se voit — #ogShell le recouvre partout ailleurs — et en
   sombre cette teinte est exactement celle de la barre : la découpe
   disparaît, ce qui est le but.

   On ne touche PAS à `--bg` : le plateau derrière les cartes en dépend,
   et c'est un réglage séparé. */
/* Le corps porte la teinte de la BARRE, pas celle du cadre. C'est la
   valeur d'ogilio (#1a1a1a en clair) et elle règle du même coup la
   découpe du coin : en sombre comme en clair, ce que l'arrondi laisse
   voir est exactement le fond de la barre. */
body { background: var(--og-topbar-bg); }

/* ═══════════════════════════════════════════════════════════════════════
   29. LE BLOC COMPTE — AU GABARIT
   ─────────────────────────────────────────────────────────────────────
   Une seule cible : avatar, nom, pack, chevron. Les valeurs sont celles
   de la maquette (.adm-tb-user-btn et ses enfants).

   Les couleurs sont des BLANCS À FAIBLE OPACITÉ, sans jeton de thème :
   la barre est sombre dans les deux thèmes, son contenu ne change donc
   pas avec eux. C'est aussi ce que fait la maquette.
   ═══════════════════════════════════════════════════════════════════════ */
body #toolbar .saas-acct .saas-acct-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 36px;
  padding-block: 0;
  padding-inline: 6px 4px;
  border: 0;
  border-radius: 12px;
  background: transparent;
  color: rgba(255, 255, 255, .8);
  cursor: pointer;
  font: inherit;
  transition: background .12s ease, color .12s ease;
}
body #toolbar .saas-acct .saas-acct-btn:hover,
body #toolbar .saas-acct.open .saas-acct-btn {
  background: rgba(255, 255, 255, .08);
  color: #fff;
}

/* L'avatar est un CARRÉ ARRONDI, pas un rond : c'est ce qui le rattache
   aux autres commandes de la barre, toutes à 12 px de rayon. */
body #toolbar .saas-avatar {
  width: 30px;
  height: 30px;
  border-radius: 8px;
  font-size: 12px;
  font-weight: 700;
  color: #fff;
  background: linear-gradient(135deg, #7c3aed, #a855f7);
  box-shadow: none;
}
body #toolbar .saas-acct .saas-acct-btn:hover .saas-avatar,
body #toolbar .saas-acct.open .saas-avatar { box-shadow: none; }
body #toolbar .saas-avatar.is-guest svg { width: 15px; height: 15px; }

body #toolbar .saas-id {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  max-width: 140px;
  min-width: 0;
}
body #toolbar .saas-id-name {
  max-width: 140px;
  font-size: 13px;
  font-weight: 450;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Le pack passe SOUS le nom, en capitales minuscules. Il perd sa teinte
   par palier : le mot le dit deja, et trois couleurs de pastille dans une
   barre qui en compte deja beaucoup, c'est une de trop. */
body #toolbar .saas-id .saas-plan {
  display: inline-block;
  height: auto;
  padding: 2px 5px;
  border: none;
  border-radius: 4px;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: .4px;
  line-height: 1;
  text-transform: uppercase;
  background: rgba(255, 255, 255, .12);
  color: rgba(255, 255, 255, .6);
}

body #toolbar .saas-caret {
  display: flex;
  flex: none;
  color: rgba(255, 255, 255, .6);
  transition: transform .18s ease;
}
body #toolbar .saas-caret svg { width: 16px; height: 16px; }
body #toolbar .saas-acct.open .saas-caret { transform: rotate(180deg); }

/* Sous 1100 px, le nom et le pack cedent : l'avatar et le chevron
   suffisent a designer la cible, et la barre a besoin de sa largeur pour
   la recherche. */
@media (max-width: 1099px) {
  body #toolbar .saas-id { display: none; }
  body #toolbar .saas-acct-btn { padding-inline: 4px; }
}


/* ═══════════════════════════════════════════════════════════════════════
   30. LA PASTILLE DE SÉLECTION DE PRIX — LES DEUX THÈMES
   ─────────────────────────────────────────────────────────────────────
   Ses deux fonds étaient écrits en dur (#55575d et #383c48) pendant que
   son texte suivait le thème (`color: var(--text)`). En clair, cela
   donnait du #121722 sur du #383c48 : **1,63:1**, illisible.

   Les deux moitiés lisent maintenant la rampe. En sombre elles rejoignent
   au passage les surfaces de la peau — elles flottaient jusqu'ici sur des
   gris qui n'appartenaient à aucun palier.

   Le liseré n'est pas décoratif : en clair la pastille est blanche sur un
   graphique blanc, il n'y a que lui pour dire où elle commence.
   ═══════════════════════════════════════════════════════════════════════ */
body .price-selector-plus {
  background: var(--raised);
  color: var(--text-2);
}
body .price-selector-chip:hover .price-selector-plus {
  background: var(--og-active);
  color: var(--text);
}
body .price-selector-value {
  background: var(--surface-2);
  color: var(--text);
  box-shadow: inset 0 0 0 1px var(--border);
}

/* ═══════════════════════════════════════════════════════════════════════
   31. LE BOUTON DE LA BARRE DU HAUT — DÉSORMAIS « TOUS LES OUTILS »
   ─────────────────────────────────────────────────────────────────────
   Il ouvrait et fermait la colonne de navigation, avec deux icônes dont
   une seule s'affichait — un bouton qui fait l'aller ET le retour doit
   dire lequel des deux il fera. La colonne est partie le 2026-08-08 ; le
   bouton ne fait plus qu'une chose, ouvrir le menu « Tous les outils »,
   et n'a donc plus qu'une icône.

   IL NE VIT QUE SOUS 1024 px, et `tool-rail.css` §8 s'en charge : au
   dessus, le pied de la barre du bord droit porte le même menu. Deux
   boutons pour un seul menu seraient exactement le doublon que cette
   peau s'interdit — c'est la règle qui avait fait sortir le style de
   bougies de la colonne le 2026-08-03.

   DEUX identifiants, et c'est necessaire : `body #toolbar .og-burger svg`
   pese (1,1,2) — un id, une classe, deux types — et ecrasait un
   `body #ogBurger .og-burger-ico` a (1,1,1).
   ═══════════════════════════════════════════════════════════════════════ */
body #toolbar #ogBurger .og-burger-ico { display: block; }
body #toolbar #ogBurger.on { background: rgba(255, 255, 255, .14); color: #fff; }


/* ═══════════════════════════════════════════════════════════════════════
   32. LA BANNIÈRE PARTENAIRE DANS L'ESPACE DE TRAVAIL
   ─────────────────────────────────────────────────────────────────────
   `saas-promo.js` insère la bannière AVANT `#chartStage`
   (`stage.parentNode.insertBefore`). Ce parent était une simple pile quand
   le script a été écrit ; dans cette peau c'est une GRILLE à deux colonnes
   et un seul rang. La bannière prenait donc la premiere cellule : le
   graphique glissait dans la colonne du panneau (340 px au lieu de 1000)
   et le panneau tombait dans un rang implicite en dessous. Mesuré :
   graphique 340x309 au lieu de 1000x584.

   On ne touche pas au script — il sert aussi la peau d'origine, ou son
   insertion est juste. C'est la GRILLE qui lui fait une place : un rang
   automatique en tete, sur toute la largeur.

   `:has()` PLUTOT QU'UN RANG PERMANENT. Un `grid-template-rows: auto 1fr`
   toujours present laisserait, banniere absente, un rang vide de 0 px —
   mais le `gap` de 12 px s'appliquerait quand meme entre les deux rangs.
   Douze pixels de vide en permanence pour une banniere qui se ferme au
   bout de trois jours.
   ═══════════════════════════════════════════════════════════════════════ */
body #ogWs:has(> .sq-promo) { grid-template-rows: auto minmax(0, 1fr); }
body #ogWs > .sq-promo { grid-row: 1; grid-column: 1 / -1; }
body #ogWs:has(> .sq-promo) > #chartStage { grid-row: 2; grid-column: 1; }
body #ogWs:has(> .sq-promo) > #ogPanel    { grid-row: 2; grid-column: 2; }

/* Elle devient une CARTE, comme les autres pieces du plateau : son filet
   du bas separait d'un contenu qui la suivait immediatement, il n'a plus
   d'objet maintenant qu'un ecart de 12 px l'en detache. Le liseré rouge
   de la marque, lui, reste — c'est sa signature. */
body #ogWs > .sq-promo {
  border-radius: var(--og-radius-md);
  border-block-end: none;
  box-shadow: var(--og-lift-1);
  background:
    linear-gradient(90deg, rgba(213, 43, 30, .13), rgba(213, 43, 30, .04) 240px, transparent 460px),
    var(--surface-2);
}
[dir="rtl"] body #ogWs > .sq-promo {
  background:
    linear-gradient(270deg, rgba(213, 43, 30, .13), rgba(213, 43, 30, .04) 240px, transparent 460px),
    var(--surface-2);
}
/* Fermeture : la hauteur ET le rang doivent disparaitre, sinon le rang
   automatique garde ses 12 px de gap pendant l'animation de sortie. */
body #ogWs > .sq-promo.sq-out { box-shadow: none; }


/* ═══════════════════════════════════════════════════════════════════════
   32bis. LE BANDEAU D'ESSAI EN THÈME CLAIR — ENCRE CLAIRE SUR FOND SOMBRE
   ─────────────────────────────────────────────────────────────────────
   Relevé en mesurant les contrastes de la version mobile, mais le défaut
   n'a rien d'adaptatif : il est là à toutes les largeurs depuis que la
   peau existe.

   `body { background: var(--og-topbar-bg) }` (§28) peint le corps en
   #1a1a1a EN THÈME CLAIR AUSSI — c'est la signature d'ogilio, le plateau
   clair encadré de sombre. Or `.saas-band` est injecté par saas-app.js en
   PREMIER ENFANT du corps, au-dessus de la barre : il hérite donc de ce
   fond sombre, tout en gardant l'encre de la rampe CLAIRE, faite pour du
   blanc. Mesuré sur la composition réelle du dégradé sur #1a1a1a :

       « Essai libre. » (strong, #121722)      1,17:1
       le reste du texte (#4A5468)             1,79:1

   Autrement dit, la phrase qui porte l'offre d'essai est invisible pour
   qui utilise le thème clair. Même raisonnement que §24 : ce qui vit sur
   une surface sombre s'écrit en blancs, quel que soit le thème de la page.

   Portée : cette feuille n'est chargée que sur le terminal. Le même
   bandeau sur le reste du site garde son fond clair et son encre sombre,
   qui vont ensemble — il n'y a rien à corriger là-bas.
   ═══════════════════════════════════════════════════════════════════════ */
html[data-theme="light"] body > .saas-band .saas-band-txt,
html[data-theme="light"] body > .saas-band .saas-band-more {
  color: rgba(255, 255, 255, .74);
}
html[data-theme="light"] body > .saas-band .saas-band-txt strong,
html[data-theme="light"] body > .saas-band .saas-clock {
  color: #fff;
}
html[data-theme="light"] body > .saas-band .saas-clock {
  background: rgba(255, 255, 255, .10);
  border-color: rgba(255, 255, 255, .24);
}
/* Le lien « Se connecter » du bandeau, quand il est là — le bouton bleu,
   lui, porte son propre fond et n'a jamais été concerné. */
html[data-theme="light"] body > .saas-band a:not([class]) {
  color: rgba(255, 255, 255, .82);
}

/* LE MÊME DÉFAUT, DANS L'AUTRE SENS, SUR LA BANNIÈRE PARTENAIRE.
   `.sq-name` — le mot « Swissquote » — est écrit en BLANC EN DUR par
   `saas-promo.css`, ce qui allait quand la bannière était posée sur le
   fond sombre du graphique. La peau en a fait une CARTE sur `--surface-2`
   (§32), qui est claire en thème clair : le nom de la marque y devient
   blanc sur blanc, donc invisible — mesuré à 1,0:1. C'est la marque du
   partenaire qui disparaît, sur l'emplacement qu'il paie.
   Le sous-titre et la pastille « Partenaire », eux, lisent déjà la rampe
   et n'ont rien à corriger. */
html[data-theme="light"] body #ogWs > .sq-promo .sq-name { color: var(--text); }


/* ═══════════════════════════════════════════════════════════════════════
   33. LE TERMINAL AU DOIGT — LA SECTION ADAPTATIVE DE LA PEAU
   ─────────────────────────────────────────────────────────────────────
   Cette peau a été dessinée sur un écran large et n'avait, sous 860 px,
   que des retouches de marges. Mesuré avant correction sur un iPhone de
   390 px : le graphique faisait 90 × 50 px, soit SIX POUR CENT de la
   fenêtre ; sur 320 px, 20 × 22. Un terminal de trading dont on ne voit
   pas les bougies n'est pas un terminal.

   TROIS CAUSES, toutes structurelles :

     1. §32 place la bannière partenaire dans la grille avec
        `#ogPanel { grid-column: 2 }` — SANS garde de largeur. Sous
        1024 px, où la grille n'a plus qu'UNE colonne déclarée, cette
        ligne recrée une deuxième colonne implicite : le panneau reprend
        ses 264 px et le graphique se contente de ce qui reste.
     2. `body` est en `100dvh; overflow: hidden`. Rien ne défile : tout
        ce que la ligne de contexte et le bandeau de signal prennent en
        hauteur, le graphique le perd. Ils prenaient 100 px et 291 px.
     3. La barre du graphique est en `flex-wrap: wrap` : dans une colonne
        étroite elle empilait ses commandes sur cinq rangs, 178 px.

   LA LOGIQUE MÉTIER QUI ORDONNE TOUT CE QUI SUIT. Sur un téléphone, la
   place est le budget, et il se dépense dans cet ordre :

     PERMANENT   le prix, l'instrument, le sens du signal et son motif.
                 Ce sont les quatre choses qu'on ouvre l'application pour
                 voir. Elles restent à l'écran, compactées, jamais
                 masquées.
     DOMINANT    le graphique. Il prend TOUT le reste — la moitié de la
                 fenêtre au minimum. C'est la seule pièce qu'on regarde
                 plus d'une seconde.
     À UN GESTE  le plan de trade, les niveaux, le backtest, la liquidité.
                 On ne lit pas des bougies et on ne saisit pas une taille
                 de position dans le même instant : le panneau devient un
                 TIROIR qui monte du bas, replié sur sa rangée d'onglets.
     DÉLIBÉRÉ    la navigation, les réglages, le calendrier — déjà dans
                 le tiroir latéral, on n'y touche pas.

   Ce qui suit est écrit ICI et nulle part ailleurs, en un seul bloc et
   en fin de fichier : c'est la méthode de cette peau, et c'est ce qui
   avait empêché la dérive sur les élements précédents. Une retouche
   adaptative posée à côté de sa règle de base est invisible à la
   relecture, et c'est comme ça qu'on se retrouve avec un graphique de
   90 pixels sans que personne ne l'ait décidé.

   POINTS DE RUPTURE, et pourquoi ceux-là :
     1023  la latérale est DÉJÀ un tiroir à cette largeur (§10). Le
           panneau d'analyse la suit : sous 1024, les deux colonnes sont
           des calques, pas des colonnes. Un seul seuil pour une seule
           idée.
      859  le seuil de la barre du site (voir la refonte du 02/08) — la
           barre du graphique passe sur une rangée qui défile.
      640  le téléphone. La ligne de contexte se plie en deux rangs, le
           bandeau de signal se compacte.
      420  les petits téléphones (320–414). Ce qui double une
           information disponible ailleurs s'efface.
   ═══════════════════════════════════════════════════════════════════════ */


/* ───────────────────────────────────────────────────────────────────────
   33.0  LES CHIFFRES COMPOSÉS EN ÉCRITURE DROITE-À-GAUCHE
   Relevé en vérifiant la version arabe des deux bandeaux : `−0.37R · −37 $`
   s'affichait `$ 37− · 0.37R−`. Le `$` et les signes sont des caractères
   NEUTRES pour l'algorithme bidirectionnel : dans une ligne arabe ils
   suivent la direction de la ligne et sautent à l'autre bout. La valeur
   affichée n'est alors plus celle qui est écrite.

   `plaintext` fait lire la direction sur le PREMIER caractère fort de
   chaque valeur — ici un chiffre latin — et la lit donc de gauche à
   droite à l'intérieur d'une ligne qui va de droite à gauche. C'est
   exactement le traitement déjà appliqué aux durées du panneau de trade
   (« 45 min » y rendait « min 45 »).

   Ces quatre-là parce qu'ils sont dans les deux bandeaux permanents,
   c'est-à-dire ce qu'on lit vingt fois par minute sur un téléphone. Le
   même défaut existe ailleurs — `#tpPnl` dans le panneau notamment — et
   demande sa propre passe : il n'a rien à voir avec l'adaptation.

   #adrChip EST LE CINQUIÈME (2026-08-23) : « ADR 82 % » mêle un sigle
   latin, un nombre et un signe neutre — dans une ligne arabe, le « % »
   saute à l'autre bout et on lit « % 82 ADR ». Même remède que le bilan
   du jour, qu'il suit dans le document.
   ─────────────────────────────────────────────────────────────────────── */
[dir="rtl"] body #instrumentStrip #pbChange,
[dir="rtl"] body #instrumentStrip #dxyBadge,
[dir="rtl"] body #instrumentStrip #ogDayChip,
[dir="rtl"] body #instrumentStrip #adrChip,
[dir="rtl"] body #tvDashboard .og-pos-val { unicode-bidi: plaintext; }


/* ───────────────────────────────────────────────────────────────────────
   33.1  CONFINEMENT HORIZONTAL
   Un élément de grille ou de flex refuse par défaut de descendre sous la
   largeur intrinsèque de son contenu : c'est ce qui transforme un tableau
   large en défilement de la PAGE au lieu d'un défilement de sa boîte.
   La règle a été apprise page par page lors de la refonte du 02/08 ; la
   peau a introduit quatre conteneurs de plus, les voici.
   ─────────────────────────────────────────────────────────────────────── */
body #ogShell > *,
body #chartWrapper > *,
body #ogWs > *,
body #ogPanel > *,
body #chartStage > * { min-width: 0; }


/* ═══ 33.2  SOUS 1024 px — LE PANNEAU DEVIENT UN TIROIR DU BAS ═════════ */
@media (max-width: 1023px) {

  html {
    /* La part repliée : la rangée d'onglets, et rien d'autre. C'est la
       navigation de l'analyse — elle reste visible, sinon les cinq
       sections passent à deux gestes et personne ne les trouve. */
    --og-sheet-peek: 46px;
    /* La hauteur VOULUE du tiroir ouvert. */
    --og-sheet-h: min(60dvh, 460px);
    /* LA RÉSERVE DU GRAPHIQUE, et c'est la valeur qui porte tout le
       raisonnement. Un tiroir qui monte jusqu'en haut n'est plus un
       tiroir, c'est une autre page : on quitte le marché pour saisir une
       taille de position, et on y revient à l'aveugle. Ces pixels sont
       donc RÉSERVÉS quoi qu'il arrive — la barre du graphique et une
       bande de bougies. Le tiroir prend ce qui reste, et défile en
       lui-même s'il en manque : c'est lui qui cède, jamais le marché.

       Le `max()` est un garde-fou dans l'autre sens : sur un écran très
       court, la réserve mangerait le tiroir entier et il ne montrerait
       plus que sa rangée d'onglets. */
    --og-sheet-reserve: 190px;
    --og-sheet-gap: 10px;
  }

  /* LA GRILLE REDEVIENT UNE COLONNE, BANNIÈRE COMPRISE. Les trois lignes
     de §32 sont reprises à l'IDENTIQUE en spécificité — un `:has()` ne
     pèse rien mais son argument si, et `body #ogWs:has(> .sq-promo) >
     #ogPanel` vaut (2,1,1). Une règle plus légère aurait perdu en
     silence ; à poids égal, c'est l'ordre du fichier qui tranche, et
     cette section est la dernière. */
  body #ogWs {
    position: relative;
    grid-template-columns: minmax(0, 1fr);
    /* La réserve du tiroir replié. Un élément absolu se cale sur la boîte
       de REMPLISSAGE : `inset-block-end: 0` le pose donc tout en bas,
       pendant que le graphique, élément de grille, s'arrête à la boîte de
       contenu — l'écart entre les deux est ce remplissage. */
    padding-block-end: calc(var(--og-sheet-peek) + var(--og-sheet-gap));
  }
  body #ogWs:has(> .sq-promo) > #ogPanel { grid-row: auto; grid-column: auto; }
  body #ogWs:has(> .sq-promo) > #chartStage { grid-row: 2; grid-column: 1; }

  body #ogWs > #ogPanel {
    position: absolute;
    inset-inline: 0;
    inset-block-end: 0;
    z-index: 30;
    height: var(--og-sheet-h);
    min-height: 0;
    max-height: max(calc(100% - var(--og-sheet-reserve)), 180px);
    border-radius: 0;
    border-start-start-radius: var(--og-radius-md);
    border-start-end-radius: var(--og-radius-md);
    /* Replié : on ne laisse dépasser que la rangée d'onglets. */
    transform: translateY(calc(100% - var(--og-sheet-peek)));
    transition: transform .26s cubic-bezier(.4, 0, .2, 1),
                box-shadow .26s ease;
    box-shadow: var(--og-lift-1);
  }
  html.og-sheet-open body #ogWs > #ogPanel {
    transform: translateY(0);
    box-shadow: 0 -14px 34px -10px rgba(0, 0, 0, .55), 0 0 0 1px var(--border);
  }
  html[data-theme="light"].og-sheet-open body #ogWs > #ogPanel {
    box-shadow: 0 -14px 30px -12px rgba(0, 0, 0, .22), 0 0 0 1px var(--border);
  }

  /* LA POIGNÉE. Posée en absolu SUR la rangée d'onglets : #ogTabs défile
     latéralement, et un bouton rangé dedans partirait avec le défilement
     au moment précis où on le cherche. Le dégradé sous elle empêche un
     onglet de passer illisible derrière le chevron. */
  body #ogPanel > .og-sheet-grab {
    display: flex;
    position: absolute;
    inset-block-start: 0;
    inset-inline-end: 0;
    z-index: 2;
    width: 46px;
    height: var(--og-sheet-peek);
    align-items: center;
    justify-content: center;
    padding: 0;
    border: none;
    cursor: pointer;
    color: var(--text-3);
    background: linear-gradient(to left, var(--surface-2) 62%, rgba(0, 0, 0, 0));
    transition: color .15s ease;
  }
  [dir="rtl"] body #ogPanel > .og-sheet-grab {
    background: linear-gradient(to right, var(--surface-2) 62%, rgba(0, 0, 0, 0));
  }
  body #ogPanel > .og-sheet-grab:hover { color: var(--text); }
  body #ogPanel > .og-sheet-grab svg {
    width: 20px; height: 20px;
    transition: transform .26s cubic-bezier(.4, 0, .2, 1);
  }
  /* Le chevron dit ce que le geste FERA, pas où l'on est : fermé il
     montre le haut (ça monte), ouvert il montre le bas (ça descend). */
  html.og-sheet-open body #ogPanel > .og-sheet-grab svg { transform: rotate(180deg); }

  /* La rangée d'onglets est la poignée visible : elle a la hauteur d'une
     cible tactile, et laisse la place du chevron à son extrémité. */
  body #ogTabs {
    flex: none;
    height: var(--og-sheet-peek);
    align-items: center;
    padding: 0 8px;
    padding-inline-end: 50px;
  }
  body .og-tab {
    min-height: 34px;
    padding: 6px 10px;
    font-size: 12px;
  }
  body .og-tab svg { width: 17px; height: 17px; }

  /* Le dock ne porte plus le min-height de la colonne : c'est le tiroir
     qui donne la hauteur, et le dock la remplit. */
  body #ogPanel { min-height: 0; }

  /* La latérale au doigt n'a plus lieu d'être : sous ce seuil il n'y a
     plus de colonne du tout. Les rangs à porter au pouce sont ceux du
     menu « Tous les outils », et `tool-rail.css` les dimensionne. */

  /* Les onglets Plan / Risque / Résultat du plan de trade faisaient 22 px
     de haut — et ils vivent désormais dans le tiroir, c'est-à-dire sous le
     pouce. */
  body #ogPanel .tp-tab { min-height: 34px; }
}

@media (max-width: 1023px) and (prefers-reduced-motion: reduce) {
  body #ogWs > #ogPanel,
  body #ogPanel > .og-sheet-grab svg { transition: none; }
}

/* Au-dessus de 1024 px la colonne est là : il n'y a rien à ouvrir. */
@media (min-width: 1024px) {
  body #ogPanel > .og-sheet-grab { display: none; }
}


/* ═══ 33.2bis  LA HAUTEUR RÉELLE D'UN NAVIGATEUR MOBILE ═══════════════════
   §33.2 a été réglée sur la hauteur de l'APPAREIL — 812, 844, 932 px. Or
   Safari garde en permanence sa barre d'adresse et sa barre d'outils : sur
   un iPhone debout il reste **environ 677 px utilisables**. `dvh` rapporte
   bien cette valeur-là ; c'est la RÉSERVE de 190 px, elle, qui avait été
   calibrée sur la grande.

   Mesuré dans ces conditions (677 px, bandeau de signal rempli, bannière
   partenaire présente) : le tiroir ouvert tombe à 223 px, dont 46 pour sa
   rangée d'onglets. Il reste 177 px pour un panneau qui en fait 600 — moins
   d'un tiers. Il défile bien, mais on lit un plan de trade par le trou d'une
   serrure, et on ne devine pas qu'il continue.

   Le bloc « couché » (§33.6, ≤ 620 px de haut) traitait déjà ce cas ; c'est
   la bande INTERMÉDIAIRE — un téléphone DEBOUT, entre 620 et 760 px utiles —
   qui n'était atteinte par aucune règle. Elle est le cas le plus courant.

   Deux gestes seulement, et aucun ne touche au graphique :
   on reprend ce qui est de la PUBLICITÉ, et ce qui est DÉJÀ RÉPÉTÉ dans le
   tiroir qu'on vient d'ouvrir. Le verdict et son motif — la raison d'être du
   produit — ne bougent pas.
   ═══════════════════════════════════════════════════════════════════════ */
@media (max-width: 1023px) {
  /* ── LE TIROIR MONTE JUSQU'AU HEADER ──────────────────────────────────
     Décision du user, prise en connaissance de l'arbitrage : sur un
     téléphone, ouvrir l'analyse veut dire LIRE l'analyse. Le graphique
     n'est pas rogné, il est momentanément recouvert — et il revient entier
     au geste inverse, qui reste le même : le même onglet, la poignée, ou
     Échap. La réserve de 190 px n'a donc plus d'objet dans l'état ouvert ;
     elle continue de décrire l'état replié, où elle n'a jamais servi.

     LA HAUTEUR NE SE CALCULE PAS À PARTIR DU BANDEAU, et c'est ce qui rend
     la règle solide : le bandeau de signal change de hauteur à chaque tick
     de prix (une réserve qui apparaît, un motif qui passe sur deux lignes).
     Toute soustraction qui le mentionnerait serait fausse une fois sur
     deux. On part de la fenêtre et on retire la seule chose FIXE au-dessus :
     la barre du haut.

     ET ON LA LAISSE DÉBORDER SOUS ELLE. `#toolbar` porte `z-index: 60`
     contre 30 pour le tiroir : les quelques pixels qui dépassent passent
     DERRIÈRE la barre, invisibles. C'est volontaire — viser le bord exact
     demanderait de connaître l'écart que le plateau laisse en bas, une
     valeur qui varie avec la peau, et un pixel d'erreur ferait une fente
     lumineuse. Un recouvrement, lui, ne se voit jamais. */
  html { --og-topbar-h: 56px; }

  html.og-sheet-open body #ogWs > #ogPanel {
    height: calc(100dvh - var(--og-topbar-h));
    max-height: none;
    /* Le coin arrondi n'a plus lieu d'être quand la feuille touche la
       barre : un angle rond contre un angle droit laisse voir deux pixels
       de graphique de chaque côté, et c'est tout ce que l'œil retient. */
    border-start-start-radius: 0;
    border-start-end-radius: 0;
  }

  /* ── LE TIROIR DÉFILAIT « EN THÉORIE » ────────────────────────────────
     `#chartDock` porte bien `overflow-y: auto` depuis l'origine, et la
     mesure le confirme : 606 px de contenu pour 191 visibles, il y a bien
     de quoi défiler. Mais il lui manquait les deux déclarations SANS
     LESQUELLES un défilement interne ne tient pas sous iOS — celles-là
     mêmes que la barre du graphique (§33.3) et le tableau du bandeau
     avaient déjà reçues, et lui non.

     Sans `overscroll-behavior: contain`, un glissement qui atteint le bout
     de la zone se propage au parent ; `body` étant en `overflow: hidden`,
     Safari fait rebondir la PAGE au lieu de faire défiler le panneau, et
     le geste paraît mort. C'est le genre de défaut qu'aucune mesure de
     géométrie ne montre : la boîte est correcte, c'est le doigt qui ne
     passe pas. */
  body #ogPanel > #chartDock {
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }

  /* ── ET IL FAUT DIRE QU'IL Y A UNE SUITE ──────────────────────────────
     Même raison que le pied de la fenêtre des réglages : un panneau coupé
     net en plein milieu d'une ligne se lit comme un défaut d'affichage,
     pas comme « ça continue ». Le fondu est posé sur le TIROIR, en absolu,
     donc il ne défile pas avec le contenu. Il s'efface dès qu'on touche le
     bas, sinon il mentirait à son tour. */
  body #ogWs > #ogPanel::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    inset-block-end: 0;
    height: 24px;
    pointer-events: none;
    z-index: 1;
    background: linear-gradient(to top, var(--chart-bg), transparent);
    opacity: 0;
    transition: opacity .18s ease;
  }
  html.og-sheet-open body #ogWs > #ogPanel:not([data-scroll-end])::after { opacity: 1; }
}

/* Sous 900 px, style.css passe la barre du haut à 58 px (`--tb-h`, déclarée
   SUR la barre, donc illisible d'ici). On tient la valeur jumelle sur `html`,
   là où le tiroir peut la lire. Deux nombres pour une même mesure : si l'un
   bouge, l'autre doit suivre. */
@media (max-width: 900px) { html { --og-topbar-h: 58px; } }


/* ═══ 33.3  SOUS 860 px — LA BARRE DU GRAPHIQUE SUR UNE SEULE RANGÉE ═══
   Elle empilait ses commandes sur cinq rangs — 178 px pris au graphique.
   Elle devient ce qu'est déjà la barre du haut : une rangée qui défile.

   CONSÉQUENCE À NE PAS OUBLIER, elle a déjà coûté un débogage sur la
   barre du haut : UNE BOÎTE À DÉFILEMENT ROGNE SES DESCENDANTS. Les deux
   menus de la barre (unités de temps, style de bougies) sont en
   `position: absolute` dans leur bouton ; ils s'ouvriraient DANS la
   rangée et seraient coupés net. Ils passent donc en `position: fixed`,
   posés sur l'écran — et leur `top` vient de `--og-cb-bottom`, mesurée
   par terminal-ogilio.js, jamais d'une constante : la ligne de contexte
   au-dessus fait un ou deux rangs selon la largeur.
   ─────────────────────────────────────────────────────────────────────── */
@media (max-width: 859px) {
  body .chart-bar {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    /* La barre ne se rogne pas elle-même à la première pression : sans
       ça, un doigt qui glisse dessus fait défiler la page. */
    overscroll-behavior-x: contain;
  }
  body .chart-bar::-webkit-scrollbar { display: none; }
  body .chart-bar > * { flex: none; }
  /* Le ressort n'a plus de sens dans une boîte qui défile : il pousserait
     la lecture d'état hors de portée à chaque fois. */
  body .chart-bar .grow { display: none; }
  body .chart-bar .chart-read { flex: none; white-space: nowrap; }

  /* Les commandes montent à la taille du pouce. Elles tenaient sur 28 px
     de haut — le sélecteur d'unité de temps est la commande la plus
     pressée de tout le terminal. */
  body .chart-bar #ivBtns .iv-btn,
  body .chart-bar #tfMore,
  body .chart-bar #csBtn,
  body .chart-bar #presetSelect,
  body .chart-bar #settingsBtn,
  body .chart-bar #replayBtn {
    min-height: 36px;
  }
  body .chart-bar #ivBtns .iv-btn { min-width: 40px; }
  body .chart-bar #settingsBtn,
  body .chart-bar #replayBtn { min-width: 40px; }
  /* Le segment des favoris ajoute 3 px de sa poche autour de boutons déjà
     montés à 36 : la barre passait à 57 px pour rien. */
  body .chart-bar #ivBtns { padding: 2px; }

  /* ─── LE BANDEAU DE SIGNAL SE PLIE DANS L'ORDRE DE LECTURE ──────────
     Mesuré à 768 px avant correction : QUATRE rangs, 182 px, dont un rang
     entier pour le seul bouton « Détail des indicateurs » et 390 px de
     vide à droite du verdict. La rangée du gabarit ne tient plus à cette
     largeur — les deux jauges demandent 358 px à elles seules — mais elle
     se replie sans ordre, et l'ordre est justement ce qui rend un
     empilement lisible.

     Les quatre blocs répondent à quatre questions ; on les empile dans
     l'ordre où elles se posent :

       rang 1   QUOI      la pastille de sens, le verdict, le chevron
       rang 2   POURQUOI  le motif et les réserves, sous le verdict
       rang 3   COMBIEN   les deux jauges, côte à côte, à parts égales
       rang 4   OÙ J'EN SUIS   la position — SEULEMENT si elle existe.

     Ce dernier point est le principe entier : `.og-pos` porte `hidden`
     tant qu'aucune position n'est ouverte, donc ce rang ne coûte rien
     tant qu'il n'a rien à dire. On paie la place quand l'information est
     là, pas avant.
     ─────────────────────────────────────────────────────────────────── */
  body #tvDashboard #dashVerdict { align-items: flex-start; row-gap: 10px; }
  body #tvDashboard #dvArrow    { order: 1; }
  /* `flex: 1 1 0` et pas la base automatique : sinon le verdict garde sa
     largeur de contenu, ne cède rien, et pousse les jauges au rang
     suivant alors qu'il restait la place de les serrer. */
  body #tvDashboard .signal-txt { order: 2; flex: 1 1 0; min-width: 0; }
  body #tvDashboard #dashMore   { order: 3; flex: none; align-self: flex-start; }
  body #tvDashboard .gauges     { order: 4; flex: 1 0 100%; flex-wrap: nowrap; margin-inline-start: 0; }
  /* Deux jauges, deux moitiés exactes. Le libellé est réécrit par app.js
     (« concordance haussière ») et déborde alors sa moitié : il s'ellipse,
     et l'infobulle de la jauge porte déjà la phrase entière. */
  body #tvDashboard .gauge      { flex: 1 1 0; min-width: 0; }
  body #tvDashboard .gauge-top .dv-meter-label {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  body #tvDashboard .gauge-top .dv-pct { flex: none; }
  body #tvDashboard .og-pos     { order: 5; flex: 1 0 100%; }

  /* ─── LES PASTILLES D'ACTUALITÉ SUR LE GRAPHIQUE ────────────────────
     Elles font 24 px réduits à 0,85 — vingt pixels sous le doigt. On ne
     les AGRANDIT pas : ce sont des repères posés à l'heure exacte de
     l'événement sur l'axe du temps, et les grossir les ferait mentir sur
     l'instant qu'elles marquent, en plus de charger le graphique. On
     agrandit leur ZONE SENSIBLE, qui n'a pas de couleur et ne dit rien
     sur le prix : 32 px sous le doigt, 20 px à l'œil. */
  body .saas-news-layer .saas-news-mark::after {
    content: "";
    position: absolute;
    inset: -6px;
  }

  /* Les deux menus sortent de la rangée. `--og-cb-bottom` est le BAS réel
     de la barre : au-dessus d'elle vivent le bandeau d'essai, la barre du
     haut, la ligne de contexte et le bandeau de signal, dont les hauteurs
     dépendent toutes de la largeur et de l'état du compte. */
  body #chartStage #tfMenu,
  body #chartStage #csMenu {
    position: fixed;
    /* `inset-inline` et rien d'autre : `.cs-menu` pose `left: auto;
       right: 0`, et réécrire ces deux longhands ICI, après le raccourci
       logique, annulerait le raccourci — ils désignent les mêmes
       propriétés. La spécificité suffit (deux identifiants contre une
       classe), il n'y a rien à neutraliser. */
    inset-block-start: calc(var(--og-cb-bottom, 300px) + 6px);
    inset-inline: 10px;
    width: auto;
    min-width: 0;
    max-width: none;
    max-height: min(52dvh, 380px);
  }
  /* `hidden` doit rester le dernier mot : une règle qui pose `position`
     ne le contredit pas, mais la règle de largeur ci-dessus est plus
     lourde que `.tf-menu[hidden]`, et ce genre d'oubli fait apparaître
     des boîtes vides en permanence. */
  body #chartStage #tfMenu[hidden],
  body #chartStage #csMenu[hidden] { display: none; }
}


/* ═══ 33.4  SOUS 640 px — LE TÉLÉPHONE ════════════════════════════════ */
@media (max-width: 640px) {

  /* Le plateau resserre ses marges : 12 px de remplissage et deux écarts
     de 12 px, c'est 48 px de vide sur une fenêtre de 844. */
  html { --og-stack-gap: 8px; }
  html { --og-sheet-gap: 8px; }

  /* ─── LA RECHERCHE D'INSTRUMENT — DE ZÉRO PIXEL À TOUTE LA BARRE ────
     MESURÉ, ET C'ÉTAIT LE PIRE DÉFAUT DE CETTE BARRE : sous 480 px, le
     champ de recherche fait **ZÉRO PIXEL DE LARGE**. Il est bien là, il
     prend le doigt, il reçoit les frappes — et on ne voit pas ce qu'on
     tape. La liste de suggestions, elle, s'affichait correctement
     (`style.css` la sort déjà de la barre en `position: fixed`) : on
     tapait donc en aveugle en regardant des propositions arriver.

     La cause est mécanique : `#symbolWrap` est un bloc élastique de
     260 px de base dans une barre qui en a 390 à partager entre sept
     commandes. Il se comprime jusqu'à son contenu incompressible — la
     loupe et l'indice clavier — et le champ, seul élément qui accepte de
     rétrécir, absorbe tout le manque.

     Changer d'instrument est une action PRIMAIRE : c'est même la seule
     raison d'ouvrir un terminal sur un autre marché. Elle ne peut pas
     dépendre de la place qui reste.

     LA RECHERCHE DEVIENT DONC UNE ICÔNE QUI S'OUVRE. Repliée, elle ne
     demande que ses 40 px de loupe. À la mise au point elle se pose EN
     CALQUE sur toute la barre, et le champ retrouve sa largeur.

     `:focus-within` ET RIEN D'AUTRE : pas de classe, pas d'état à tenir,
     pas de fermeture à écrire. Le champ perd le focus, le calque se
     replie — y compris quand on choisit un symbole dans la liste, quand
     on presse Échap, ou quand on touche ailleurs. Un état qu'on ne
     stocke pas est un état qui ne peut pas se désynchroniser. Le script
     n'ajoute qu'une chose, parce que le CSS ne sait pas la faire : un
     appui sur la loupe donne le focus au champ.

     L'indice clavier part : il n'y a pas de ⌘ sur un téléphone. */
  body #toolbar .og-tb-center { flex: 0 0 auto; }
  body #toolbar #symbolWrap {
    width: 40px;
    max-width: none;
    padding-inline: 0;
    justify-content: center;
    cursor: text;
  }
  body #toolbar #symbolWrap .sw-kbd { display: none; }
  /* `flex: 0 0 0` ET `overflow: hidden` sur la boîte. Avec la base
     `flex: 1 1 auto`, le champ reprenait les quatorze pixels laissés par
     la loupe et laissait dépasser la PREMIÈRE LETTRE du symbole — un « B »
     orphelin à côté de la loupe, vu en production. */
  body #toolbar #symbolWrap:not(:focus-within) { overflow: hidden; }
  body #toolbar #symbolWrap #symbolInput { flex: 0 0 0; width: 0; min-width: 0; padding: 0; }

  body #toolbar #symbolWrap:focus-within {
    position: absolute;
    inset-inline: 8px;
    /* CENTRÉ SANS `transform`, ET C'EST UNE OBLIGATION, PAS UN GOÛT. Un
       `translateY(-50%)` aurait fait de cette boîte le BLOC CONTENEUR de
       ses descendants en `position: fixed` — or `#symbolDropdown`, la
       liste de suggestions, en est un : `style.css` la sort de la barre
       en `fixed` sous 640 px, précisément parce que la barre la rognait.
       Elle serait revenue se caler dans le champ, et s'y serait fait
       couper. `inset-block: 0` + `margin-block: auto` centre une boîte
       absolue de hauteur connue sans rien transformer. */
    inset-block: 0;
    margin-block: auto;
    z-index: 6;
    width: auto;
    padding-inline: 12px;
    justify-content: flex-start;
    /* UN CALQUE DOIT ÊTRE OPAQUE, et ça s'est vu tout de suite : la boîte
       repliée porte un blanc à .08, qui est juste sur une barre pleine.
       Étalé sur toute la largeur, ce même blanc laissait voir le logo et
       les trois boutons qu'il recouvre — on lisait « XAUUSD » à travers
       une cloche. On empile donc un aplat de la BARRE sous la teinte :
       le dégradé plat est le seul moyen de superposer une couleur à une
       autre dans une seule déclaration `background`.
       La barre est sombre dans les deux thèmes (§24), le jeton suffit. */
    background:
      linear-gradient(rgba(255, 255, 255, .14), rgba(255, 255, 255, .14)),
      var(--og-topbar-bg);
    border-color: rgba(255, 255, 255, .22);
  }
  body #toolbar #symbolWrap:focus-within #symbolInput {
    flex: 1 1 auto;
    width: auto;
    min-width: 0;
    padding-inline-start: 8px;
  }

  /* ─── LA LIGNE DE CONTEXTE EN DEUX RANGS ────────────────────────────
     Rang 1, permanent : l'instrument et son prix. Rang 2 : le contexte
     de marché — le dollar, le bilan du jour.

     LA COUPURE EST FAITE PAR LE RESSORT. `.og-spring` existe déjà dans le
     balisage, exactement à l'endroit de la coupure : passé en
     `flex-basis: 100%` et hauteur nulle, il occupe une ligne entière et
     renvoie tout ce qui le suit au rang suivant. Aucun élément ajouté,
     aucun ordre inventé — c'est le balisage du gabarit qui se plie.
     ─────────────────────────────────────────────────────────────────── */
  body #instrumentStrip {
    column-gap: 8px;
    row-gap: 4px;
    padding: 7px 10px;
    align-items: center;
  }
  body #instrumentStrip .inst-dot  { order: 1; }
  body #instrumentStrip .ctx-sym   { order: 2; font-size: 16px; }
  body #instrumentStrip .ctx-sub   { order: 3; font-size: 12px; min-width: 0; }
  body #instrumentStrip .ctx-live  { order: 4; }
  /* Même rang que le témoin de flux : les deux disent si ce prix bouge
     encore, et ils tiennent le premier rang avec le symbole. À égalité
     d'`order`, c'est l'ordre du document qui départage — la pastille est
     après #streamDot dans le balisage, elle le reste à l'écran.

     LA MARGE AUTOMATIQUE N'EST PAS UN CENTRAGE, C'EST UN CALE. Le bandeau
     est en `space-between` : le vide d'une ligne se répartit ENTRE ses
     éléments, et une pastille courte suffisait à faire flotter « XAUUSD »
     au milieu de nulle part, à 80 px de sa propre puce. Une marge `auto`
     après la pastille absorbe tout ce vide d'un coup — les trois éléments
     se rangent à gauche, comme partout ailleurs. Les marges automatiques
     passent AVANT `justify-content`, c'est ce qui rend le calage fiable. */
  body #instrumentStrip #marketBadge { order: 4; margin-inline-end: auto; }
  body #instrumentStrip #priceBar  { order: 5; margin-inline-start: auto; gap: 7px; }
  body #instrumentStrip #pbPrice   { font-size: 19px; }
  body #instrumentStrip #pbChange  { font-size: 10.5px; padding: 2px 7px; }

  body #instrumentStrip .og-spring {
    order: 6;
    flex: 0 0 100%;
    height: 0;
    min-width: 0;
    margin: 0;
    padding: 0;
  }
  body #instrumentStrip #dxyBadge  { order: 7; }
  body #instrumentStrip #ogDayChip { order: 8; }
  /* LE MÊME RANG QUE LE BILAN DU JOUR, PAS LE SUIVANT. À `order` égal,
     c'est l'ordre du document qui départage, et la pastille ADR suit
     #ogDayChip dans le balisage : elle se range donc juste après lui, et
     AVANT le cadran de réouverture qui porte aussi 8. Un rang 9 l'aurait
     envoyée derrière l'OHLC… qui est masqué à cette largeur, donc en bout
     de ligne, loin du contexte de marché auquel elle appartient. */
  body #instrumentStrip #adrChip   { order: 8; }
  /* SANS RANG EXPLICITE, IL VAUDRAIT ZÉRO et sauterait en tête de la
     première ligne, devant la pastille d'état. Il prend celui du bilan du
     jour, qu'il remplace de fait : marché fermé, l'un s'en va, l'autre
     arrive, et le second rang garde sa composition. */
  body #instrumentStrip #marketReopen { order: 8; font-size: 10.5px; }
  body #instrumentStrip #marketReopen .mr-clock { font-size: 12px; }
  body #instrumentStrip .ctx-ohlc  { order: 9; }

  /* Le nom long et la place doublent le symbole, qui est juste à côté et
     que la recherche affiche en entier. L'unité de temps reste : elle
     change, eux non. */
  body #instrumentStrip #instName,
  body #instrumentStrip #instExchange { display: none; }

  /* Le rang de contexte n'a pas droit à la même graisse que le prix.
     #adrChip y est NOMMÉ bien qu'il porte `.chip` : sa règle de base est
     posée par identifiant (§21, même raison), et une règle de largeur qui
     ne l'atteindrait que par sa classe perdrait contre elle — (2,0,1)
     contre (1,1,1), la requête média ne pèse rien. */
  body #instrumentStrip .chip,
  body #instrumentStrip #adrChip,
  body #instrumentStrip #marketBadge,
  body #instrumentStrip #dxyBadge { font-size: 10.5px; padding: 2px 7px; }

  /* ─── LE BANDEAU DE SIGNAL, COMPACT ─────────────────────────────────
     Il faisait 291 px — un tiers de l'écran pour quatre informations.
     Il en fait un peu plus de cent, et n'en perd aucune :

       rang 1   la pastille de sens, le verdict, le chevron de détail
       rang 2   le motif et les réserves, alignés sous le verdict
       rang 3   les deux jauges, côte à côte
       rang 4   la position — SEULEMENT si elle existe (elle porte
                `hidden` le reste du temps, donc elle ne coûte rien).

     Ce dernier point est le principe entier de ce bandeau : on paie la
     place quand l'information est là, pas avant.
     ─────────────────────────────────────────────────────────────────── */
  /* DEUX REMPLISSAGES POUR UN SEUL CADRE. #tvDashboard est la carte,
     #dashVerdict a le sien : vingt pixels de vide empilés. La carte cède
     le sien, le bandeau garde le sien — et le tableau déplié reprend le
     sien à son compte, plus bas.

     LE SÉLECTEUR N'EST PAS DÉCORATIF : le remplissage vient de
     `body #chartWrapper > #tvDashboard`, qui pèse DEUX identifiants. Un
     `body #tvDashboard` perdait en silence — c'est le piège le plus
     fréquent de cette peau, et c'est la quatrième fois. */
  body #chartWrapper > #tvDashboard { padding: 0; }
  body #tvDashboard .tv-dash-scroll { padding-inline: 8px; padding-block-end: 8px; }

  /* Un reste de l'ancienne grille : le motif y était le rang 2 de la
     colonne 1 et devait s'aligner sous le verdict, d'où ce retrait. Il
     vit maintenant DANS la ligne des réserves, elle-même déjà décalée par
     la pastille — le retrait n'aligne plus rien, il ouvre juste un trou
     de 46 px au milieu d'une phrase, et sur un téléphone ce trou coûte
     une ligne entière. (Laissé tel quel au-dessus de 640 px : la ligne y
     tient de toute façon, et ce n'est pas le sujet de cette section.) */
  body #tvDashboard #dvMotif { padding-inline-start: 0; }

  body #tvDashboard #dashVerdict {
    column-gap: 10px;
    row-gap: 9px;
    padding: 9px 10px;
  }
  body #tvDashboard #dvArrow {
    width: 30px; height: 30px;
    border-radius: 9px;
    font-size: 14px;
  }
  body #tvDashboard #dvLabel    { font-size: 13.5px; line-height: 1.25; }
  body #tvDashboard .signal-sub { font-size: 11px; line-height: 1.45; }

  body #tvDashboard .gauges { gap: 12px; }
  body #tvDashboard .gauge-top { gap: 6px; }
  body #tvDashboard .gauge-top .dv-pct { font-size: 11.5px; }
  body #tvDashboard .gauge-bar { margin-top: 4px; }

  body #tvDashboard .og-pos { align-items: center; gap: 7px; }
  body #tvDashboard .og-pos-val { font-size: 15px; }

  /* Le tableau des six indicateurs, une fois déplié, ne prend pas le
     graphique en otage : il défile en lui-même. */
  body #tvDashboard .tv-dash-scroll {
    max-height: 34dvh;
    overflow: auto;
    overscroll-behavior: contain;
  }

  /* Le tiroir d'analyse gagne un peu de hauteur : sur un téléphone c'est
     là qu'on saisit une taille de position. La réserve, elle, ne bouge
     pas — c'est le point fixe. */
  html { --og-sheet-h: min(66dvh, 460px); }

  /* ─── LA BANNIÈRE PARTENAIRE ────────────────────────────────────────
     Elle reste — c'est un actif, et elle se ferme pour trois jours d'une
     pression. Mais 42 px sur une fenêtre de 844, c'est 5 % de l'écran :
     elle se resserre, garde sa marque et son appel, et abandonne les
     arguments qui la suivaient sur un grand écran (elle les a déjà
     abandonnés à cette largeur, ils ne tenaient pas). */
  body #ogWs > .sq-promo { min-height: 0; }
  body #ogWs > .sq-promo .sq-main { min-height: 36px; }
}


/* ═══ 33.4bis  LE DÉTAIL DES INDICATEURS DEVIENT UN CHEVRON ═══════════
   Sur LES DEUX formats du téléphone, debout comme couché. « Détail des
   indicateurs » mesure 146 px : debout il prend le tiers du bandeau,
   couché il pousse les jauges au rang suivant, ce qui coûte 28 px de
   hauteur — la ressource rare dans chaque cas.

   LA LISTE DE REQUÊTES SÉPARÉE PAR UNE VIRGULE EST UN « OU ». C'est la
   seule façon en CSS d'écrire « téléphone debout OU téléphone couché »
   sans dupliquer le bloc, et une règle dupliquée est une règle qui
   divergera.

   Le texte disparaît à l'œil, PAS au lecteur d'écran : le bouton porte
   son `data-i18n-aria`, donc son nom accessible est intact. Sans cette
   précaution, `font-size: 0` l'aurait rendu anonyme.
   ─────────────────────────────────────────────────────────────────────── */
@media (max-width: 640px), (max-height: 620px) and (max-width: 1023px) {
  body #tvDashboard #dashMore {
    width: 30px; height: 30px;
    padding: 0;
    gap: 0;
    font-size: 0;
    align-items: center;
    justify-content: center;
  }
  body #tvDashboard #dashMore::after {
    content: "";
    width: 7px; height: 7px;
    border: solid currentColor;
    border-width: 0 1.8px 1.8px 0;
    transform: translateY(-2px) rotate(45deg);
    transition: transform .2s ease;
  }
  body #tvDashboard #dashMore[aria-expanded="true"]::after {
    transform: translateY(1px) rotate(-135deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  body #tvDashboard #dashMore::after { transition: none; }
}


/* ═══ 33.5  SOUS 420 px — LES PETITS TÉLÉPHONES ═══════════════════════
   320 à 414 px. Ici tout ce qui double une information disponible à un
   geste s'efface — et rien d'autre.
   ─────────────────────────────────────────────────────────────────────── */
@media (max-width: 419px) {
  /* L'unité de temps est écrite en toutes lettres dans la barre du
     graphique, juste dessous, et c'est là qu'on la change. */
  body #instrumentStrip .ctx-sub { display: none; }
  body #instrumentStrip .ctx-sym { font-size: 15px; }
  body #instrumentStrip #pbPrice { font-size: 17px; }
  body #instrumentStrip #pbChange { font-size: 10px; padding: 2px 6px; }

  /* Le bilan du jour est une STATISTIQUE : elle a son onglet (Backtest),
     et elle ne change pas d'une minute à l'autre. Le dollar, lui, reste —
     il bouge, et il explique l'or et les paires en USD.
     L'ADR CONSOMMÉ (#adrChip) RESTE AUSSI, et c'est une décision : il
     change d'une minute à l'autre avec le range du jour, et il n'a pas
     d'onglet à lui — le panneau de séance le répète, mais dans le tiroir,
     à un geste. Un premier jet l'aurait masqué avec son voisin parce
     qu'ils partagent toutes les autres règles ; ici ils se séparent. */
  body #instrumentStrip #ogDayChip { display: none; }

  body #tvDashboard .gauges { gap: 9px; }
  body #tvDashboard .gauge-top { font-size: 9.5px; }
}


/* ═══ 33.6  LES ÉCRANS COURTS — LE TÉLÉPHONE COUCHÉ ═══════════════════
   Un téléphone en paysage donne 390 px de HAUT. Le bandeau d'essai, la
   barre, la ligne de contexte et le bandeau de signal en prendraient
   les deux tiers. On coupe dans le contexte, jamais dans le signal.
   ─────────────────────────────────────────────────────────────────────── */
/* ── `and (orientation: landscape)` AJOUTÉ LE 2026-08-07, ET C'EST UNE
      CORRECTION, PAS UN RESSERRAGE ─────────────────────────────────────
   Ce bloc se décrit lui-même comme celui du mode COUCHÉ — « Couché, la
   largeur abonde — 844 à 932 px » — et il en tire tout : la grille à deux
   colonnes qui met la ligne de contexte et le bandeau de signal côte à
   côte, les jauges sur un rang, les cibles tactiles serrées d'un cran.

   Mais sa condition ne disait que « moins de 620 px de haut ». Or un
   iPhone SE mesure 452 px utiles DEBOUT, un SE2 553, un Fold fermé 600 :
   tous les trois recevaient la mise en page du mode couché sur une
   largeur de 320 px, là où le bloc en suppose 844.

   Mesuré à 320 × 452 avant correction : le bandeau de signal réduit à
   205 px de large, sa colonne de texte à 139, et le MOTIF DU REFUS coupé
   par une ellipse — c'est-à-dire la seule phrase qui dise pourquoi on ne
   doit pas entrer. Plus des onglets à 30 px là où la règle voisine en
   demande 34.

   Rien n'est perdu pour ces trois formats : ils retombent sur les règles
   de portrait (§33.3 et suivantes), qui empilent au lieu de juxtaposer —
   ce que le commentaire ci-dessous appelle lui-même « l'inverse exact ».
   ─────────────────────────────────────────────────────────────────────── */
@media (max-height: 620px) and (max-width: 1023px) and (orientation: landscape) {
  /* La réserve tombe à la barre du graphique plus deux ou trois bougies :
     sur 390 px de haut, 190 px de réserve ne laisseraient au tiroir que
     sa rangée d'onglets — il ne servirait plus à rien. */
  html { --og-sheet-h: min(80dvh, 320px); --og-sheet-reserve: 112px; }

  /* ─── LE BANDEAU DE SIGNAL SE RESSERRE, SANS S'EMPILER ──────────────
     ET C'EST L'INVERSE EXACT DE CE QU'ON FAIT EN PORTRAIT, pour la raison
     exacte inverse. Debout, la largeur manque : on empile. Couché, la
     largeur abonde — 844 à 932 px — et c'est la HAUTEUR qui manque.
     Empiler y coûtait 205 px sur 414, la moitié de l'écran, et il restait
     quatre-vingt-quinze pixels de graphique : moins qu'à la verticale, sur
     un geste qu'on ne fait QUE pour voir plus de bougies.

     CE BLOC EST AUTONOME, ET C'EST LE PIÈGE QUI M'A REPRIS. Un téléphone
     couché mesure 896 px de LARGE : aucune des règles de §33.3 (≤ 859) ne
     l'atteint. Les jauges y reprenaient donc leur forme large — 150 px
     chacune, `flex-wrap: wrap` — et se remettaient l'une SOUS l'autre dans
     une colonne de 300 : quatre-vingt-neuf pixels à elles deux. Tout ce
     que la rangée doit tenir doit être écrit ICI, en toutes lettres.

     `flex: 0 1 …` partout : chaque bloc prend sa largeur naturelle et se
     serre plutôt que de se replier. On ne leur impose pas la rangée, on
     cesse de la leur interdire. */
  body #tvDashboard #dashVerdict {
    align-items: center;
    column-gap: 10px;
    row-gap: 8px;
    padding: 8px 10px;
  }
  body #tvDashboard #dvArrow {
    order: 1;
    width: 30px; height: 30px;
    border-radius: 9px;
    font-size: 14px;
  }
  body #tvDashboard .signal-txt { order: 2; flex: 1 1 0; min-width: 0; }
  body #tvDashboard #dvLabel    { font-size: 13.5px; line-height: 1.25; }
  body #tvDashboard .signal-sub { font-size: 11px; line-height: 1.45; }
  /* Couché, la rangée vaut une seule ligne : le motif reprend sa coupe.
     L'ellipse fonctionne ici parce que le sous-titre est lui-même en
     `nowrap` et borné en largeur — ce qui n'est pas le cas debout. */
  body #tvDashboard .dv-motif b { white-space: nowrap; }
  body #tvDashboard #dashMore   { order: 3; flex: none; align-self: center; }
  body #tvDashboard .gauges {
    order: 4;
    flex: 0 1 auto;
    flex-wrap: nowrap;
    gap: 12px;
    margin-inline-start: 0;
  }
  body #tvDashboard .gauge { flex: 0 1 130px; min-width: 0; }
  body #tvDashboard .gauge-top { gap: 6px; }
  body #tvDashboard .gauge-top .dv-meter-label {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  body #tvDashboard .gauge-top .dv-pct { flex: none; font-size: 11.5px; }
  body #tvDashboard .gauge-bar { margin-top: 4px; }
  body #tvDashboard .og-pos { order: 5; flex: 0 1 auto; align-items: center; gap: 7px; }
  body #tvDashboard .og-pos-val { font-size: 15px; }

  /* ─── LA LIGNE DE CONTEXTE ET LE BANDEAU DE SIGNAL PARTAGENT UN RANG ──
     C'est le geste principal du mode couché, et il découle du calcul, pas
     du goût. Sur 390 px de haut : bandeau d'essai 45, barre 56, barre du
     graphique 55, rangée d'onglets 46, marges 24 — deux cent vingt-six
     pixels avant d'avoir affiché quoi que ce soit. Empilés, la ligne de
     contexte (34) et le bandeau de signal (123) prenaient les cent
     soixante restants et il ne demeurait QUATRE-VINGT-ONZE pixels de
     graphique. On tourne son téléphone pour voir des bougies ; on en
     voyait moins qu'à la verticale.

     La largeur, elle, abonde : 844 px. Les deux cartes tiennent donc
     côte à côte. `#chartWrapper` passe en GRILLE — deux cellules sur le
     premier rang, l'espace de travail sur toute la largeur du second.
     En flex il aurait fallu arbitrer le repli ; la grille le dit.

     Le rang vaut la plus haute des deux cartes, soit une seule fois le
     prix de la plus chère au lieu de la somme. */
  html { --og-stack-gap: 6px; }
  body #chartWrapper {
    padding: var(--og-stack-gap, 6px);
    gap: var(--og-stack-gap, 6px);
    display: grid;
    /* `fit-content(34%)` ET PAS `auto`, et la différence coûte cent
       pixels de hauteur. Une piste `auto` prend la largeur de son contenu
       tant qu'il y a de la place : la ligne de contexte s'octroyait 390
       des 832 px pour afficher un symbole et un prix, pendant que le
       bandeau de signal, réduit à 490, empilait son verdict sur TROIS
       lignes. `fit-content()` dit exactement ce qu'on veut — prends ce
       qu'il te faut, sans dépasser le tiers. Le bandeau garde le reste,
       et c'est lui qui a le texte long. */
    grid-template-columns: fit-content(34%) minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
    align-content: stretch;
  }
  body #chartWrapper > #instrumentStrip { grid-column: 1; grid-row: 1; }
  body #chartWrapper > #tvDashboard     { grid-column: 2; grid-row: 1; }
  body #chartWrapper > #ogWs            { grid-column: 1 / -1; grid-row: 2; }
  /* Tout enfant injecté (calque, bandeau) reste dans le second rang au
     lieu de s'inventer un troisième — même précaution qu'en §23. */
  body #chartWrapper > *:not(#instrumentStrip):not(#tvDashboard):not(#ogWs) {
    grid-column: 1 / -1;
    grid-row: 2;
  }

  body #instrumentStrip { padding: 5px 10px; align-content: center; }

  /* LA COLONNE DE GAUCHE SE MESURE SUR SON CONTENU, et c'est ce qui a
     failli faire capoter la grille : la ligne de contexte réclamait 588
     des 832 px du rang parce qu'à 844 px de LARGE elle est encore dans sa
     forme tablette — nom long et place compris. Le bandeau de signal se
     retrouvait à 237 px et s'empilait sur cinq rangs : 222 px de haut,
     pire qu'avant.

     Les mêmes coupes qu'en portrait s'appliquent donc ici. C'est le piège
     propre au mode couché : un téléphone y est LARGE, donc aucune règle
     de largeur ne l'atteint. */
  body #instrumentStrip #instName,
  body #instrumentStrip #instExchange { display: none; }

  /* LE MOTIF TIENT SUR UNE LIGNE, ET S'ELLIPSE. C'est la seule coupe de
     tout ce travail qui retire vraiment quelque chose à l'œil : deux
     lignes de réserves coûtent ici 17 px sur 390, et le texte entier
     reste à un geste — le chevron de détail, et l'infobulle du bandeau.
     En portrait il garde ses deux lignes : c'est là qu'on lit. */
  body #tvDashboard .signal-sub {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* La bannière partenaire cède. Quarante-huit pixels de haut sur trois
     cent quatre-vingt-dix, pour une bannière qu'on a déjà vue en portrait
     et qui se ferme d'une pression : c'est la dépense la moins défendable
     de l'écran. Elle reste entière partout ailleurs. */
  body #ogWs > .sq-promo { display: none; }

  /* La rangée d'onglets et la barre du graphique se serrent d'un cran —
     elles restent au-dessus du seuil tactile de 24 px de la WCAG, avec
     leurs écarts. */
  html { --og-sheet-peek: 40px; }
  body .chart-bar { padding: 5px 9px; }
  body .chart-bar #ivBtns .iv-btn,
  body .chart-bar #tfMore,
  body .chart-bar #csBtn,
  body .chart-bar #presetSelect,
  body .chart-bar #settingsBtn,
  body .chart-bar #replayBtn { min-height: 32px; }
  body #ogTabs { padding-block: 0; }
  body .og-tab { min-height: 30px; }

  /* Les deux menus de la barre sortent de la carte, pour la même raison
     qu'en §33.3 mais par un autre chemin : ici la barre ne défile pas,
     c'est `#chartStage` qui rogne (`overflow: hidden`). Une liste
     d'unités de temps mesure 380 px ; sur un écran de 414 px de haut,
     ouverte depuis une barre à mi-hauteur, elle serait coupée net. */
  body #chartStage #tfMenu,
  body #chartStage #csMenu {
    position: fixed;
    inset-block-start: calc(var(--og-cb-bottom, 200px) + 4px);
    inset-inline: 10px;
    width: auto;
    min-width: 0;
    max-width: none;
    max-height: min(62dvh, 300px);
  }
  body #chartStage #tfMenu[hidden],
  body #chartStage #csMenu[hidden] { display: none; }
  /* Le second rang entier : dollar, bilan du jour, ADR consommé, OHLC. Ce
     sont quatre contextes, et un écran de 390 px de haut n'a plus de place
     pour du contexte — il a le prix, le signal et les bougies. L'ADR
     part ICI alors qu'il reste sous 420 px de large : debout on coupe
     dans ce qui double une information, couché on coupe dans TOUT ce qui
     n'est pas le marché lui-même. */
  body #instrumentStrip .og-spring { display: none; }
  body #instrumentStrip #dxyBadge,
  body #instrumentStrip #ogDayChip,
  body #instrumentStrip #adrChip,
  body #instrumentStrip .ctx-ohlc { display: none; }
  /* LE CADRAN RESTE, seul de son rang. Le second rang tombe parce qu'il ne
     portait que du contexte ; un marché fermé et l'heure à laquelle il
     rouvre ne sont pas du contexte — c'est la réponse à la question que
     pose un écran figé, et elle vaut plus ici qu'ailleurs. Il se glisse
     sur la première ligne, où le ressort ne le renvoie plus. */
  body #instrumentStrip #marketReopen { order: 4; }

  body #tvDashboard { padding: 4px 8px; }
  body #tvDashboard #dashVerdict { row-gap: 6px; padding: 7px 10px; }
  body #tvDashboard .tv-dash-scroll { max-height: 46dvh; }
}


/* ═══ 33.7  LA SAISIE AU DOIGT ════════════════════════════════════════
   Sous 16 px, Safari iOS ZOOME la page à la mise au point du champ — et
   ne la dézoome jamais. Le plancher porte `!important` et c'est le seul
   cas où il est justifié dans ce fichier : la moitié des champs du
   terminal tiennent leur taille d'un sélecteur d'identifiant, qu'aucun
   sélecteur d'élément ne peut reprendre. Les énumérer un à un laissait
   chaque fois passer les suivants.

   LA SECONDE REQUÊTE N'EST PAS UN DOUBLON : un téléphone COUCHÉ mesure
   896 px de large et échappe donc au seuil de 859. Deux champs y
   restaient sous 16 px — dont la recherche d'instrument, celui qu'on
   touche pour changer de marché. Le zoom d'iOS y était garanti.
   ─────────────────────────────────────────────────────────────────────── */
@media (max-width: 859px), (max-height: 620px) and (max-width: 1023px) {
  body input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
  body select,
  body textarea { font-size: 16px !important; }
}


/* ═══════════════════════════════════════════════════════════════════════
   34. LE GRAPHIQUE PLEIN CADRE — `html.og-focus`
   ─────────────────────────────────────────────────────────────────────
   Une bascule, et un seul principe : tout ce qui COMMENTE le marché
   s'efface, tout ce qui le MONTRE reste.

   S'efface — le bandeau de signal, la bannière partenaire, le panneau
   d'analyse, la colonne de navigation. Ce sont des lectures, des avis,
   des outils : on les rouvre d'une pression.

   Reste — le graphique, sa barre d'outils, le rail de dessin, et une
   ligne de contexte réduite à ce qu'on ne peut pas ne pas savoir :
   l'instrument, l'unité de temps, le prix et sa variation. Un terminal
   qui cache le nom de ce qu'on regarde n'est pas « plein cadre », il est
   ambigu — et une position se prend sur un instrument, pas sur un
   graphique anonyme. Ces quarante pixels sont le seul péage du mode, et
   ils font passer le graphique de 47 % à environ 75 % de la fenêtre sur
   un téléphone, de 46 % à la totalité du plateau sur un écran large.

   LA BARRE DU HAUT NE BOUGE PAS. Elle porte la recherche, les alertes et
   le compte : la masquer enfermerait dans un mode dont on ne sait plus
   sortir que par une seule commande. Ici il y en a trois — le bouton,
   Échap, et la touche F.

   SPÉCIFICITÉ. Cette section vient APRÈS la section adaptative et doit
   pouvoir la contredire : §33 écrit `body #chartWrapper > #tvDashboard`
   (deux identifiants). Un `html.og-focus body #tvDashboard` n'en pèse
   qu'un et aurait perdu en silence — le piège le plus fréquent de cette
   peau. Les sélecteurs qui suivent gardent donc le chemin complet.
   ═══════════════════════════════════════════════════════════════════════ */

/* ─── Le bouton, calé sur #settingsBtn ────────────────────────────── */
body .chart-bar .cb-focus {
  display: inline-grid;
  place-items: center;
  width: 36px;
  min-height: 34px;
  flex: none;
  padding: 0;
  border: none;
  border-radius: var(--og-radius-sm);
  cursor: pointer;
  color: var(--text-3);
  background: transparent;
  transition: background .15s ease, color .15s ease;
}
body .chart-bar .cb-focus:hover { color: var(--text); background: var(--og-hover); }
body .chart-bar .cb-focus .cbf-ico { width: 15px; height: 15px; grid-area: 1 / 1; }
/* Une seule des deux icônes à la fois — motif du bouton de colonne. */
body .chart-bar .cb-focus .cbf-out { display: none; }
html.og-focus body .chart-bar .cb-focus .cbf-in  { display: none; }
html.og-focus body .chart-bar .cb-focus .cbf-out { display: block; }
html.og-focus body .chart-bar .cb-focus {
  color: var(--text);
  background: var(--og-active);
}

/* ═══ CE QUI S'EFFACE, ET COMMENT ══════════════════════════════════════
   `display: none` ne s'anime pas, et c'est tout le problème d'une bascule
   de mise en page. Trois mécaniques différentes selon ce qui bouge :

   1. LES DEUX BLOCS QUI SE REPLIENT EN HAUTEUR — le bandeau de signal et
      la bannière partenaire — voient leur `height` animée depuis leur
      hauteur RÉELLE, mesurée par le script juste avant la bascule.

      PAS `max-height`, ET C'EST UN CHOIX DE SÛRETÉ. Le motif habituel
      (`max-height: 260px → 0`) demande un plafond en dur : trop bas, il
      ROGNE le bandeau dans l'état normal — silencieusement, et c'est le
      bandeau de signal, la raison d'être du produit ; trop haut, la
      moitié de l'animation se joue dans le vide. Une hauteur mesurée n'a
      ni l'un ni l'autre défaut. Le script rend la main à `auto` sitôt la
      transition finie : le bandeau change de hauteur à chaque tick de
      prix, une hauteur figée finirait par le rogner aussi.

   2. LE PANNEAU D'ANALYSE glisse. Sous 1024 px il est déjà un tiroir en
      `position: absolute` avec sa transition : on le descend d'un cran
      de plus. Au-dessus, c'est sa COLONNE de grille qui se referme —
      `340px → 0px`, deux pistes des deux côtés, sans quoi les listes de
      pistes ne sont pas interpolables.

   3. LA COLONNE DE NAVIGATION réutilise la transition qu'elle a déjà
      (§10, .3s sur `flex-basis` et `width`) : on lui passe une largeur
      de zéro au lieu de la masquer.

   `visibility` part en dernier, après la transition : à hauteur nulle un
   bloc reste FOCUSABLE, et on tabulerait dans un bandeau invisible.
   ═══════════════════════════════════════════════════════════════════════ */

body #chartWrapper > #tvDashboard,
body #ogWs > .sq-promo {
  overflow: hidden;
  visibility: visible;
  transition: height .24s cubic-bezier(.4, 0, .2, 1),
              opacity .16s ease .06s,
              margin-block-start .24s cubic-bezier(.4, 0, .2, 1),
              visibility 0s;
}
html.og-focus body #chartWrapper > #tvDashboard,
html.og-focus body #ogWs > .sq-promo {
  height: 0;
  opacity: 0;
  visibility: hidden;
  transition: height .24s cubic-bezier(.4, 0, .2, 1),
              opacity .14s ease,
              margin-block-start .24s cubic-bezier(.4, 0, .2, 1),
              visibility 0s linear .24s;
}
/* LA GOUTTIÈRE SURVIT À SON OCCUPANT. `#chartWrapper` est une colonne
   flexible avec un `gap` : un enfant réduit à zéro laisse ses DEUX écarts
   en place, soit douze pixels de vide permanent entre la ligne de
   contexte et l'espace de travail. La marge négative en reprend un — il
   en reste exactement un, celui qui doit rester. Le jeton suit le `gap`
   à chaque point de rupture ; c'est pour ça qu'il existe. */
html.og-focus body #chartWrapper > #tvDashboard {
  margin-block-start: calc(-1 * var(--og-stack-gap, 12px));
}

html.og-focus body #ogWs {
  padding-block-end: 0;
  /* L'écart de la grille tombe avec ses deux occupants : plus de panneau
     à droite, plus de bannière au-dessus. Il porte la gouttière de la
     bannière, la marge négative est donc inutile ici. */
  gap: 0;
}

/* Sous 1024 px le panneau est un tiroir absolu : il descend. Au-dessus,
   c'est sa colonne qui se referme (voir plus bas). */
@media (max-width: 1023px) {
  html.og-focus body #ogWs > #ogPanel { transform: translateY(110%); }
}

@media (min-width: 1024px) {
  /* DEUX PISTES DES DEUX CÔTÉS. `minmax(0,1fr) 340px` → `minmax(0,1fr)`
     change le NOMBRE de pistes : les deux listes ne sont pas
     interpolables et le navigateur bascule d'un coup. Avec une seconde
     piste à zéro, la colonne se referme en glissant. */
  html.og-focus body #ogWs { grid-template-columns: minmax(0, 1fr) 0px; }
  html.og-focus body #ogWs > #ogPanel { opacity: 0; }

  /* LA BARRE D'OUTILS RESTE (décision du 2026-08-04, reconduite pour la
     barre qui a remplacé la colonne le 2026-08-08). Elle partait avec le
     reste dans la première version du mode, pour ses 64 px de largeur.
     C'était une erreur de catégorie : le plein cadre efface ce qui
     COMMENTE le marché, or une barre de navigation ne commente rien —
     c'est le moyen d'aller ailleurs. La faire disparaître enfermait dans
     un mode dont on ne sortait que pour changer de section. Elle coûte
     désormais 54 px, soit 3,4 % d'un écran de 1600.

     Rien à écrire pour ça : il suffit de ne pas la masquer. Les états
     ACTIFS de ses boutons s'éteignent en revanche — aucune section n'est
     ouverte — et c'est `tool-rail.css` §7 qui le dit. */
}
body #ogWs {
  transition: grid-template-columns .26s cubic-bezier(.4, 0, .2, 1),
              gap .26s cubic-bezier(.4, 0, .2, 1),
              padding-block-end .26s cubic-bezier(.4, 0, .2, 1);
}
/* LES DEUX TRANSITIONS SONT REPRISES EN ENTIER, pas complétées : une
   déclaration `transition` REMPLACE la précédente à spécificité égale, et
   celle-ci vient après. Omettre `box-shadow` ici aurait fait sauter
   l'ombre du tiroir (§33.2) et le rayon de droite du plateau (§9) sans
   qu'aucune erreur ne le signale. */
body #ogWs > #ogPanel {
  transition: transform .26s cubic-bezier(.4, 0, .2, 1),
              box-shadow .26s ease,
              opacity .2s ease;
}
body #chartWrapper {
  transition: border-start-start-radius .3s cubic-bezier(.4, 0, .2, 1),
              border-start-end-radius   .3s cubic-bezier(.4, 0, .2, 1);
}

/* ─── LE MODE COUCHÉ RESTE INSTANTANÉ, ET C'EST ASSUMÉ ─────────────────
   Là, `#chartWrapper` est une grille à deux cellules (§33.6) et le
   bandeau occupe la SECONDE : le replier en hauteur ne libère rien, c'est
   la COLONNE qui doit se refermer. Or passer de
   `fit-content(34%) minmax(0,1fr)` à une piste unique change le nombre de
   pistes — deux listes de longueurs différentes ne s'interpolent pas, le
   navigateur bascule d'un coup. Animer la moitié du mouvement et laisser
   l'autre sauter serait pire que tout faire sauter ensemble.
   ─────────────────────────────────────────────────────────────────────── */
@media (max-height: 620px) and (max-width: 1023px) {
  body #chartWrapper > #tvDashboard,
  body #ogWs > .sq-promo,
  body #ogWs,
  body #chartWrapper { transition: none; }
  html.og-focus body #chartWrapper > #tvDashboard {
    display: none;
    height: auto;
    margin-block-start: 0;
  }
}

/* Mouvement réduit : la bascule reste immédiate, l'état final est le
   même. On ne « ralentit » pas une animation dont on ne veut pas. */
@media (prefers-reduced-motion: reduce) {
  body #chartWrapper > #tvDashboard,
  body #ogWs > .sq-promo,
  body #ogWs,
  body #ogWs > #ogPanel,
  body #chartWrapper { transition: none !important; }
}

/* ─── La ligne de contexte, réduite à l'essentiel ─────────────────── */
html.og-focus body #chartWrapper > #instrumentStrip {
  padding: 4px 10px;
  gap: 10px;
  row-gap: 0;
  background: none;
  box-shadow: none;
  border-radius: 0;
}
/* L'ADR consommé part avec le bilan du jour : c'est une lecture du marché,
   pas le marché — et le principe du mode est que tout ce qui COMMENTE
   s'efface. Le chiffre reste à un geste, dans le panneau de séance. */
html.og-focus body #instrumentStrip #instName,
html.og-focus body #instrumentStrip #instExchange,
html.og-focus body #instrumentStrip #ogDayChip,
html.og-focus body #instrumentStrip #adrChip,
html.og-focus body #instrumentStrip #dxyBadge { display: none; }
/* Le ressort redevient un ressort. En §33.4 il sert de COUPURE de rangée
   pour le second rang du téléphone ; ce rang n'ayant plus d'occupant, la
   coupure ne ferait qu'ajouter deux interlignes de vide. */
html.og-focus body #instrumentStrip .og-spring {
  flex: 1 1 0;
  height: auto;
}
html.og-focus body #instrumentStrip .ctx-sym  { font-size: 15px; }
html.og-focus body #instrumentStrip #pbPrice  { font-size: 17px; }

/* ─── Le mode couché : la grille à deux cellules n'a plus qu'un
       occupant, elle redevient une colonne. ───────────────────────── */
@media (max-height: 620px) and (max-width: 1023px) {
  html.og-focus body #chartWrapper { grid-template-columns: minmax(0, 1fr); }
  html.og-focus body #chartWrapper > #instrumentStrip { grid-column: 1 / -1; }
}

/* ─── Le bouton reste ATTEIGNABLE quand la barre défile ────────────
   Sous 860 px la barre du graphique est une rangée qui défile (§33.3).
   Un bouton posé à son extrémité partirait hors de l'écran — et ce
   serait précisément la sortie du mode qu'on ne trouverait plus. Il se
   colle donc au bord, et porte le fond de la barre pour que les
   commandes ne défilent pas en transparence dessous. */
@media (max-width: 859px), (max-height: 620px) and (max-width: 1023px) {
  /* LE BOUTON MANGE LA MARGE DE DROITE DE LA BARRE, et ce n'est pas un
     détail : un élément collant se cale sur la boîte de REMPLISSAGE du
     conteneur qui défile. Avec les 9 px de marge de la barre, il restait
     une fente de neuf pixels à sa droite dans laquelle on voyait défiler
     le sélecteur de préréglage — mesuré, « Épuré » y montrait ses deux
     dernières lettres. La barre rend donc sa marge de fin, et le bouton
     la reprend en remplissage : sa boîte couvre le bord, son icône reste
     centrée sur ses 40 px utiles. */
  body .chart-bar { padding-inline-end: 0; }
  body .chart-bar .cb-focus {
    position: sticky;
    inset-inline-end: 0;
    min-height: 36px;
    width: 49px;
    padding-inline-end: 9px;
    margin-inline-start: auto;
    border-radius: 0;
    background: var(--surface-2);
    box-shadow: -12px 0 12px -8px var(--surface-2);
  }
  /* Deux couches, donc un dégradé plat pour la teinte : dans un
     `background`, seule la DERNIÈRE valeur peut être une couleur. Écrire
     `var(--og-active), var(--surface-2)` rend la déclaration entière
     invalide, et elle disparaît sans erreur. */
  html.og-focus body .chart-bar .cb-focus {
    background: linear-gradient(var(--og-active), var(--og-active)), var(--surface-2);
  }
}


/* ═══════════════════════════════════════════════════════════════════════
   35. LE PANNEAU D'ANALYSE — AU GABARIT
   ─────────────────────────────────────────────────────────────────────
   Portage du panneau de `templates/ogilio-admin/terminal.html` sur le
   balisage de la production. MÊME MÉTHODE QUE LES ÉLÉMENTS PRÉCÉDENTS :
   on prend les valeurs du gabarit et on les applique aux identifiants
   qu'app.js écrit — on ne recrée aucun nœud, on n'invente aucune donnée.

   Table de portage, gabarit → production :

     .ptab .n            →  .og-tab-n            pastille de valeur
     .kv-hero            →  #tpPosHead + #tpStatus   bloc de tête
     .kv-hero .n         →  #tpPnl               le grand chiffre
     .trend-badge        →  #tpStateBadge        la pastille de droite
     .tp-life span       →  #tpLife li           rail à points
     .advice             →  .tp-advice           encadré de conseil
     .tp-tabs / .tp-tab  →  identiques           segment à trois volets
     .kv                 →  .tp-row, .tp-foot,
                            #tpRmath > div       lignes clé-valeur

   Correspondance des jetons :
     --hair → --border · --r-sm → --og-radius-sm · --r-full → --og-radius-pill
     --green/--red → --up/--down, et --og-up-ink/--og-down-ink pour l'ENCRE
     posée sur un lavis (§21) · --green-bg/--red-bg → --og-up-bg/--og-down-bg
     --surface-3 → --raised

   DEUX LIBERTÉS PRISES, ET LEURS RAISONS.

   1. LE GABARIT MONTRE CINQ JALONS, LA PRODUCTION EN MONTRE QUATRE tant
      que le trade n'est pas clos — `renderTradeLife()` masque « Terminée »
      avec un motif écrit noir sur blanc : « un jalon futur n'est pas une
      information, c'est une promesse ». On ne le contredit pas. Le rail
      s'accommode de quatre cellules comme de cinq.

   2. LE GRAND CHIFFRE PORTE LE R **ET** LA MONNAIE. Le gabarit les sépare
      (« −1,4 R » en grand, « −142 CHF » en pastille) ; app.js les écrit
      dans UNE chaîne, dans UN nœud (`−0.37R · −37 $`). Les séparer
      demanderait soit de couper un nœud de texte en CSS — impossible —
      soit de recopier la moitié d'une valeur ailleurs, c'est-à-dire deux
      sources pour un chiffre. La pastille de droite garde donc l'ÉTAT du
      trade, qui est la seule autre chose que le gabarit met à cet endroit.
   ═══════════════════════════════════════════════════════════════════════ */

/* ─── 1. LA PASTILLE DE VALEUR SUR L'ONGLET ───────────────────────── */
body #ogTabs .og-tab-n {
  font-size: 9.5px;
  font-weight: 700;
  padding: 1px 5px;
  border-radius: var(--og-radius-pill);
  background: var(--og-chip-bg);
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  flex: none;
}
body #ogTabs .og-tab-n.up { background: var(--og-up-bg);   color: var(--og-up-ink); }
body #ogTabs .og-tab-n.dn { background: var(--og-down-bg); color: var(--og-down-ink); }
/* Sur l'onglet actif, le fond est l'encre de la page : les trois tons de
   pastille y deviennent illisibles. Le gabarit les ramène tous à un aplat
   translucide — la couleur a dit ce qu'elle avait à dire avant le clic.

   `currentColor` ET PAS UN BLANC EN DUR. L'onglet actif s'inverse d'un
   thème à l'autre — clair sur sombre en thème sombre, sombre sur clair en
   thème clair (`background: var(--text)`). Un blanc translucide y est
   juste dans un cas et invisible dans l'autre ; une pastille faite de
   18 % de l'encre DE L'ONGLET est juste dans les deux, sans règle de
   thème à tenir à jour.

   VALEUR DE SECOURS AVANT, ET CE N'EST PAS DU ZÈLE. Une déclaration que le
   navigateur ne comprend pas est JETÉE : sans la ligne grise qui précède,
   la pastille retombait sur `--og-chip-bg`, un blanc à 5 % — invisible sur
   un onglet actif clair. Mesuré en forçant le repli. Le gris neutre à 30 %
   se voit sur les deux fonds ; `color-mix` le remplace là où elle existe. */
body #ogTabs .og-tab.on .og-tab-n,
body #ogTabs .og-tab.on .og-tab-n.up,
body #ogTabs .og-tab.on .og-tab-n.dn {
  background: rgba(128, 128, 128, .30);
  background: color-mix(in srgb, currentColor 18%, transparent);
  color: inherit;
}
/* `hidden` doit rester le dernier mot : la règle ci-dessus pose un fond,
   pas un `display`, mais l'oubli de cette ligne est le défaut le plus
   fréquent de cette feuille — une pastille vide et colorée en permanence. */
body #ogTabs .og-tab-n[hidden] { display: none; }

/* ─── 2. LE BLOC DE TÊTE ──────────────────────────────────────────────
   Le gabarit pose l'intitulé et le grand chiffre à gauche, la pastille à
   droite, et un filet sous l'ensemble qui court jusqu'aux bords de la
   carte — d'où les marges négatives, qui annulent le remplissage du
   panneau pour le seul filet. */
body #ogPanel #tpPosHead {
  display: block;
  margin: -2px 0 5px;
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--text-3);
}
body #ogPanel #tpPosHead[hidden] { display: none; }

body #ogPanel #tpStatus {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 10px;
  /* LE RETRAIT NÉGATIF VAUT LE REMPLISSAGE DE `.tp-body`, PAS CELUI DU
     GABARIT. Le gabarit met -14 parce que c'est son panneau qui pade à
     14 ; ici le bloc de tête est dans `.tp-body`, qui pade à 12 — et
     `.dock-body`, qui pade bien à 14, est DEUX niveaux plus haut. Repris
     tel quel, le filet dépassait de 2 px et `#tradePanel` débordait
     (312 de large pour 316 de contenu). Mesuré, pas déduit.
     Si `.tp-body` change de remplissage, ces deux valeurs le suivent. */
  margin: 0 -12px 10px;
  padding: 0 12px 11px;
  border: none;
  border-bottom: 1px solid var(--border);
  background: none;
}
body #ogPanel #tpPnl {
  order: 1;
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -.038em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
/* app.js REMPLACE le className de #tpPnl à chaque tick (`pos` / `neg`) :
   on habille par identifiant et on laisse la classe d'état teinter. */
body #ogPanel #tpPnl.pos { color: var(--og-up-ink); }
body #ogPanel #tpPnl.neg { color: var(--og-down-ink); }

body #ogPanel #tpStateBadge {
  order: 2;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  flex: none;
  padding: 3px 9px;
  border: none;
  border-radius: var(--og-radius-pill);
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: var(--text-3);
  background: var(--og-chip-bg);
  box-shadow: inset 0 0 0 1px var(--og-chip-bd);
}

/* ─── 3. LE RAIL À POINTS DU CYCLE DE VIE ─────────────────────────────
   La production écrivait les cinq jalons en une phrase capitalisée
   (« DÉTECTION ✓ · CONFIRMATION ✓ · ENTRÉE — EN COURS »). Le gabarit en
   fait un rail : un point par étape, un trait entre eux, le nom dessous.
   On lit la POSITION dans le processus d'un coup d'œil au lieu de
   déchiffrer une ligne.

   Le nœud est un `<ol>` de `<li>` : c'est le `li` qui porte le point et
   le trait, pas le `span` du gabarit. */
body #ogPanel #tpLife {
  display: flex;
  align-items: flex-start;
  gap: 0;
  margin: 4px 0 12px;
  padding: 0;
  list-style: none;
  border: none;
  background: none;
}
body #ogPanel #tpLife[hidden] { display: none; }
body #ogPanel #tpLife li {
  /* `display: block` ET PAS le `flex` de `style.css` : un conteneur flex
     ignore `text-align`, et le libellé se collait à gauche de sa cellule
     pendant que le point restait au centre. */
  display: block;
  flex: 1;
  min-width: 0;
  position: relative;
  padding: 14px 2px 0;
  text-align: center;
  font-size: 9px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-3);
  /* LE LIBELLÉ REVIENT À LA LIGNE PLUTÔT QUE DE SE FAIRE COUPER.
     `renderTradeLife()` suffixe l'étape courante — « Gestion — en cours »
     — et la cellule fait 78 px : mesuré, ça donnait « Gestion — en co… ».
     Tronquer un libellé de quatre mots n'informe personne, et la phrase
     change avec la langue : deux lignes coûtent onze pixels et disent
     tout. */
  white-space: normal;
  overflow-wrap: break-word;
  /* L'opacité de `style.css` (.38 sur les étapes à venir) se cumulait avec
     l'encre pâle du gabarit : deux atténuations pour une. La couleur
     suffit, c'est elle qui porte l'état. */
  opacity: 1;
  background: none;
}
/* La coche de `style.css` faisait double emploi avec le point vert du
   rail — et elle allongeait un libellé déjà juste. Le point dit
   « franchie », le mot dit laquelle. */
body #ogPanel #tpLife li.past span::after { content: none; }
body #ogPanel #tpLife li::before {
  content: "";
  position: absolute;
  top: 3px;
  inset-inline-start: 50%;
  transform: translateX(-50%);
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--og-gauge-track);
}
/* Le trait part du point et rejoint le suivant. Le dernier n'a personne à
   rejoindre — un trait qui sort dans le vide donne un rail inachevé.
   `style.css` mettait un point médian ici (« Détection · Confirmation ») :
   c'était le séparateur de la forme en phrase, il n'a plus d'objet. */
body #ogPanel #tpLife li::after {
  content: "";
  position: absolute;
  top: 6px;
  inset-inline-start: 50%;
  width: 100%;
  height: 1px;
  background: var(--og-gauge-track);
}
[dir="rtl"] body #ogPanel #tpLife li::after { transform: scaleX(-1); transform-origin: right; }
body #ogPanel #tpLife li:last-child::after { display: none; }

body #ogPanel #tpLife li.past          { color: var(--og-up-ink); }
body #ogPanel #tpLife li.past::before  { background: var(--up); }
body #ogPanel #tpLife li.current       { color: var(--text); }
body #ogPanel #tpLife li.current::before {
  background: var(--text);
  box-shadow: 0 0 0 3px var(--og-hover);
}
/* `past current` — l'étape atteinte d'un trade clos : elle est franchie
   ET c'est la dernière. La règle de `current` la surclasse déjà par
   l'ordre, on ne réécrit que le point pour qu'il reste l'aboutissement. */
body #ogPanel #tpLife li.past.current::before { background: var(--up); box-shadow: 0 0 0 3px var(--og-up-bg); }
body #ogPanel #tpLife li.past.current { color: var(--og-up-ink); }

/* ─── 4. L'ENCADRÉ DE CONSEIL ─────────────────────────────────────────
   Le liseré ambre du gabarit n'a pas de jeton en production : `--warn-soft`
   existe (le lavis, §hiérarchie du 02/08), le CONTOUR non. On le pose ici,
   à côté des trois autres paires encre/lavis de §21 — et on le tire de
   `--warn` plutôt que d'écrire une couleur, pour qu'un changement de teinte
   d'alerte l'emporte avec lui. */
:root                     { --og-warn-bd: rgba(226, 160, 63, .30); }
:root[data-theme="light"] { --og-warn-bd: rgba(226, 160, 63, .34); }

body #ogPanel .tp-advice {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  padding: 9px 10px;
  margin: 0 0 10px;
  border-radius: var(--og-radius-sm);
  background: var(--warn-soft);
  box-shadow: inset 0 0 0 1px var(--og-warn-bd);
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--text-2);
}
body #ogPanel .tp-advice[hidden] { display: none; }

/* ─── LA TEINTE SUIT LA NATURE DU CONSEIL, PAS LE GABARIT ─────────────
   DÉFAUT INTRODUIT PUIS CORRIGÉ, ET IL VALAIT LA PEINE D'ÊTRE VU. Le
   gabarit n'a qu'UN encadré, ambre, parce que sa démo montre une mise en
   garde. La production en a quatre états — app.js écrit `is-done` et
   `tone-win` / `tone-loss` / `tone-flat`. En reprenant l'ambre pour tous,
   un trade GAGNANT s'annonçait dans une boîte d'alerte : « Trade terminé
   — TP3 exécuté, +0.80R » sur fond orange. Vu en production.

   C'est exactement la règle déjà posée pour les notifications : le
   gabarit n'a qu'un toast générique, ici la couleur porte une
   information, et on la garde. L'ambre reste ce qu'il doit être — un
   conseil de gestion sur un trade EN COURS.

   Le lavis et le liseré suivent donc l'encre de l'icône, que `style.css`
   teinte déjà par ton : une seule source pour la couleur du bloc. */
body #ogPanel .tp-advice.is-done.tone-win {
  background: var(--up-soft);
  box-shadow: inset 0 0 0 1px var(--og-up-bd);
}
body #ogPanel .tp-advice.is-done.tone-loss {
  background: var(--down-soft);
  box-shadow: inset 0 0 0 1px var(--og-down-bd);
}
body #ogPanel .tp-advice.is-done.tone-flat {
  background: var(--og-chip-bg);
  box-shadow: inset 0 0 0 1px var(--og-chip-bd);
}

/* L'ENCRE SE RE-RÈGLE QUAND LE FOND CHANGE — §21, et je viens de le
   revérifier à mes dépens. `style.css` peint le titre du conseil en
   `--up` / `--down`, justes sur le lavis NEUTRE qu'il avait. En posant un
   lavis de la même teinte dessous, j'ai fait tomber le titre à **4,49**
   en thème clair : vert sur vert, à un centième du seuil, du mauvais
   côté. Mesuré en production.

   Les jetons d'ENCRE existent pour exactement ça : même teinte, clarté
   remontée, mesurée sur le lavis correspondant. La pastille et le texte
   restent du même vert — c'est la clarté qui bouge, pas la couleur. */
body #ogPanel .tp-advice.is-done.tone-win  #tpAdviceLead,
body #ogPanel .tp-advice.is-done.tone-win  .tp-advice-ico { color: var(--og-up-ink); }
body #ogPanel .tp-advice.is-done.tone-loss #tpAdviceLead,
body #ogPanel .tp-advice.is-done.tone-loss .tp-advice-ico { color: var(--og-down-ink); }

/* L'icône garde l'encre que `style.css` lui donne par ton — d'où le
   `:not(.is-done)` : sans lui, l'ambre du conseil de gestion écrasait le
   vert du gain et le rouge de la perte, et la couleur cessait de dire
   quoi que ce soit. */
body #ogPanel .tp-advice:not(.is-done) .tp-advice-ico { color: var(--warn); }
body #ogPanel .tp-advice .tp-advice-ico { flex: none; margin-top: 1px; }
body #ogPanel .tp-advice .tp-advice-ico svg { width: 15px; height: 15px; }
/* Le titre du conseil : `style.css` le teinte déjà par ton sur un trade
   clos (vert au gain, rouge à la perte). On ne lui impose l'encre neutre
   que sur les conseils EN COURS, où aucun ton n'est écrit. */
body #ogPanel .tp-advice:not(.is-done) #tpAdviceLead { color: var(--text); }
body #ogPanel .tp-advice #tpAdviceLead { font-weight: 650; }
body #ogPanel .tp-advice #tpAdviceWhy,
body #ogPanel .tp-advice #tpAdviceNext { font-style: normal; color: var(--text-2); }

/* ─── 5. LE SEGMENT À TROIS VOLETS ────────────────────────────────────
   La production soulignait l'onglet actif d'un liseré ; le gabarit en
   fait un segment — une poche creusée, un volet en relief. Le second dit
   mieux qu'il s'agit de TROIS vues d'un même objet et non de trois liens. */
body #ogPanel .tp-tabs {
  display: flex;
  gap: 3px;
  padding: 3px;
  margin: 0 0 10px;
  border: none;
  border-radius: var(--og-radius-sm);
  background: rgba(0, 0, 0, .28);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .08);
}
html[data-theme="light"] body #ogPanel .tp-tabs {
  background: rgba(0, 0, 0, .035);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .07);
}
body #ogPanel .tp-tab {
  flex: 1;
  padding: 5px 6px;
  border: none;
  border-radius: 6px;
  background: transparent;
  box-shadow: none;
  font: inherit;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--text-3);
  cursor: pointer;
  transition: background .15s ease, color .15s ease, box-shadow .15s ease;
}
body #ogPanel .tp-tab:hover { color: var(--text-2); }
body #ogPanel .tp-tab.is-on {
  background: var(--raised);
  color: var(--text);
  font-weight: 600;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .10), 0 1px 2px rgba(0, 0, 0, .5);
}
html[data-theme="light"] body #ogPanel .tp-tab.is-on {
  background: #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .05), 0 1px 2px rgba(0, 0, 0, .09);
}

/* ─── 6. LES LIGNES CLÉ-VALEUR ────────────────────────────────────────
   Un intitulé à gauche, une valeur à droite, un filet pointillé entre
   deux. Le gabarit applique la même ligne aux niveaux, au pied et à la
   comptabilité en R : trois blocs qui répondent à la même question et se
   lisaient dans trois grammaires différentes. */
body #ogPanel .tp-row,
body #ogPanel .tp-foot,
body #ogPanel #tpRmath > div {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 5px 0;
  border: none;
  border-bottom: 1px dashed var(--border);
  border-radius: 0;
  background: none;
  font-size: 12px;
  color: var(--text-3);
}
body #ogPanel #tpRmath > div:last-child,
body #ogPanel .tp-foot { border-bottom: none; }
body #ogPanel .tp-row b,
body #ogPanel #tpRmath dd {
  color: var(--text);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
body #ogPanel #tpRmath { margin: 0; padding: 0; }
body #ogPanel #tpRmath dt { margin: 0; font-size: 12px; color: var(--text-3); }
body #ogPanel #tpRmath dd { margin: 0; font-size: 12px; }
body #ogPanel #tpRmath .tp-rmath-total dt { color: var(--text-2); font-weight: 600; }

/* ─── 6bis. LES NIVEAUX DU PLAN — UNE GRILLE, PAS UNE RÉPARTITION ──────
   La règle clé-valeur ci-dessus convient au pied et à la comptabilité en R :
   deux éléments, un à gauche, un à droite. Elle ne convient PAS aux niveaux,
   qui en portent QUATRE — pastille, intitulé, prix, écart.

   `justify-content: space-between` répartit alors chaque ligne pour
   elle-même : les prix ne s'alignent que par coïncidence, tant que les
   intitulés font la même largeur. « Initial stop » et « open » ont suffi à
   rompre la coïncidence, et la colonne des prix partait en escalier.

   On rend donc aux niveaux une VRAIE grille, dont les colonnes sont
   partagées par toutes les lignes. C'est la seule façon qu'un relevé de
   nombres soit lisible en diagonale. */
body #ogPanel .tp-rows {
  display: grid;
  grid-template-columns: minmax(0, 1fr) max-content minmax(58px, max-content);
  column-gap: 14px;
  row-gap: 0;
}
body #ogPanel .tp-row {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / -1;
  align-items: baseline;
  gap: 0 14px;
  padding: 7px 0;
  border: none;
  border-bottom: 1px solid rgb(var(--veil) / .07);
  border-radius: 0;
  background: none;
}
body #ogPanel .tp-row:last-child { border-bottom: none; }

/* LA PASTILLE DE LIGNE DISPARAÎT : le rail, à deux millimètres à gauche,
   porte déjà la même couleur à la même hauteur. Deux colonnes de points pour
   une seule information, c'est ce qui donnait au bloc son air encombré. */
body #ogPanel .tp-dot { display: none; }

body #ogPanel .tp-label {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-2);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Le prix est la colonne qu'on lit en diagonale : elle est CADRÉE À DROITE et
   en chasse fixe, pour que les unités tombent les unes sous les autres. */
body #ogPanel .tp-price {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text);
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
body #ogPanel .tp-meta {
  font-size: 11.5px;
  font-weight: 500;
  text-align: right;
  justify-self: end;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* L'ENTRÉE EST L'ANCRE DU PLAN : tout le reste se mesure depuis elle. Elle
   gagne un lavis pleine largeur et une encre pleine, au lieu de se fondre
   entre deux objectifs. Le filet passe au-dessus d'elle plutôt qu'en dessous :
   il sépare ce qui gagne de ce qui risque. */
body #ogPanel .tp-row.entry {
  background: rgb(var(--veil) / .045);
  border-top: 1px solid rgb(var(--veil) / .12);
  border-bottom: 1px solid rgb(var(--veil) / .12);
  padding: 8px 8px;
  margin: 3px -8px;
  border-radius: var(--og-radius-sm);
}
body #ogPanel .tp-row.entry .tp-label { color: var(--text); font-weight: 600; }
body #ogPanel .tp-row.entry .tp-price { font-size: 13px; font-weight: 700; }

/* Le stop est du côté du risque : il s'efface d'un cran sans disparaître —
   on doit pouvoir le lire, pas le chercher. */
body #ogPanel .tp-row.muted { opacity: 1; }
body #ogPanel .tp-row.muted .tp-label,
body #ogPanel .tp-row.muted .tp-price { color: var(--text-3); font-weight: 500; }

/* LA LIGNE SANS PRIX garde sa colonne mais pas son poids : « open » n'est pas
   un nombre, et l'aligner en gras tabulaire sous cinq cotations le faisait
   passer pour une valeur illisible. */
body #ogPanel .tp-price.is-open {
  font-weight: 500;
  color: var(--text-2);
  font-variant-numeric: normal;
  letter-spacing: .04em;
}

/* ─── LE REJEU DANS LA BARRE DU GRAPHIQUE ─────────────────────────────
   Le bouton descend de la barre du haut (voir le commentaire du document
   de peau). Il rejoint le GROUPE des boutons d'icône de cette barre —
   ajouté à leurs listes de sélecteurs plus haut, pas redécrit ici : une
   commande qui a le même rôle doit porter le même habillage par
   construction, pas par recopie.

   Deux choses lui restent en propre.

   Son LIBELLÉ interne part : la barre du graphique est en icônes, et
   « Rejeu » y ferait la seule étiquette de la rangée. Le nom reste dans
   l'infobulle et dans le nom accessible, tous deux déjà posés.

   Son ÉTAT ACTIF est conservé tel quel. `style.css` le peint à l'accent
   quand le rejeu tourne, et c'est la seule chose à l'écran qui dise
   qu'on ne regarde plus le marché en direct : le masquer ferait prendre
   une rediffusion pour la réalité.

   ET IL FALLAIT LE RÉÉCRIRE, PAS SEULEMENT L'ESPÉRER. `style.css` porte
   `#replayBtn.on` à (0,2,0) ; la règle de groupe ci-dessus pèse (1,1,1)
   et la battait. Résultat mesuré : rejeu en cours, bouton gris — la seule
   chose à l'écran qui disait qu'on ne regardait plus le direct était
   éteinte. J'avais écrit le contraire dans ce commentaire sans l'avoir
   ouvert : c'est le genre d'affirmation qui coûte cher. */
body .chart-bar #replayBtn .ab-label { display: none; }
body .chart-bar #replayBtn.on {
  border-radius: var(--og-radius-sm);
  color: var(--accent);
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 1px rgba(78, 127, 255, .38);
}
body .chart-bar #replayBtn.on:hover {
  color: var(--accent);
  background: var(--accent-soft);
}
/* Et une seconde fois pour le thème clair, où le fond blanc des boutons
   d'icône est posé par `html[data-theme="light"] body .chart-bar …`,
   qui pèse un type de plus que la règle d'état. Mesuré : l'encre passait
   à l'accent, le lavis restait blanc — l'état ne se voyait qu'à moitié.
   Le sélecteur reprend donc le préfixe de thème pour peser autant. */
html[data-theme="light"] body .chart-bar #replayBtn.on,
html[data-theme="light"] body .chart-bar #replayBtn.on:hover {
  color: var(--accent);
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 1px rgba(47, 95, 224, .34);
}


/* ═══════════════════════════════════════════════════════════════════════
   36. ENCRES SUR LAVIS — CE QUE L'AUDIT A TROUVÉ
   ─────────────────────────────────────────────────────────────────────
   Trois familles de `style.css` posent une couleur SÉMANTIQUE sur un
   lavis de la MÊME teinte. C'est le cas exact pour lequel §21 a séparé
   l'encre du fond, et elles n'en profitaient pas. Mesuré à l'audit du
   2026-08-04, thème par thème :

     .bt-scope.today   **1,38:1** en clair — le pire de tout le terminal.
                       Son encre est un vert menthe ÉCRIT EN DUR
                       (#7FE0CF), réglé pour le fond sombre ; posé sur le
                       lavis clair il disparaît. Une couleur en dur ne
                       peut pas servir deux thèmes.
     .tv-bull/.tv-bear 4,49:1 en clair — `color: var(--up)` sur
                       `var(--up-soft)`. Un centième sous le seuil, du
                       mauvais côté : le panneau Liquidité et le tableau
                       des indicateurs sont concernés.
     .tv-info          3,88:1 en sombre — même schéma avec le bleu.

   ON NE TOUCHE PAS À LA TEINTE, SEULEMENT À LA CLARTÉ. Les jetons d'encre
   existent pour ça : le badge et la bougie qu'il décrit restent du même
   vert. Et on ne touche pas non plus `--up` / `--down` eux-mêmes — app.js
   les lit au montage du graphique, ce sont les couleurs du marché.

   CE QUI N'EST PAS CORRIGÉ ICI, ET POURQUOI. La cote en direct sur l'axe
   des prix (`#livePriceValue`, `#livePriceCountdown`) est à **3,00:1** :
   du blanc sur la couleur de bougie. Y arriver demanderait d'assombrir la
   pastille jusqu'à ~#0F7A70, c'est-à-dire de lui donner un autre vert que
   les bougies qu'elle surmonte — exactement ce que la règle interdit.
   C'est un arbitrage de couleur de marché, il revient au produit.
   ═══════════════════════════════════════════════════════════════════════ */
:root                     { --og-info-ink: #7FA8FF; }
:root[data-theme="light"] { --og-info-ink: #2F5FE0; }

body #ogPanel .bt-scope.today,
body #chartWrapper .bt-scope.today {
  color: var(--og-up-ink);
  background: var(--og-up-bg);
}
body #ogPanel .tv-bull,
body #chartWrapper .tv-bull { color: var(--og-up-ink); }
body #ogPanel .tv-bear,
body #chartWrapper .tv-bear { color: var(--og-down-ink); }
body #ogPanel .tv-info,
body #chartWrapper .tv-info { color: var(--og-info-ink); }

/* ── GRAPHIQUE NU ─────────────────────────────────────────────────────────
   L'état actif doit se VOIR : sans lui, un utilisateur qui a masqué
   l'indicateur cherche pourquoi son graphique est vide. Même signal que le
   rejeu, dont c'est déjà la convention dans cette peau. */
body .chart-bar #bareBtn.on {
  color: var(--og-accent-ink, #4E7FFF);
  background: var(--og-accent-bg, rgba(78, 127, 255, .14));
  box-shadow: inset 0 0 0 1px rgba(78, 127, 255, .45);
}
body .chart-bar #bareBtn .ab-label { display: none; }

/* ═══════════════════════════════════════════════════════════════════════
   37. LA SECTION OUVERTE OCCUPE LA COLONNE
   ─────────────────────────────────────────────────────────────────────
   Constaté sur « Échelle de prix » : le module s'arrêtait à treize
   niveaux et laissait 468 px de vide sous lui, dans une colonne de 900.
   Deux plafonds se cumulaient — `.dock-body` à 640 px, `.lad-scroll` à
   268 — et aucun des deux n'avait tort au moment où il a été écrit : le
   dock FLOTTAIT alors au-dessus du graphique, sans hauteur définie, et un
   panneau sans plafond y aurait couvert le prix.

   Dans le panneau d'ogilio, le dock est une COLONNE à hauteur définie. Le
   plafond n'y protège plus rien, il ampute. La chaîne est donc rendue
   explicite de bout en bout — chaque maillon nommé, aucun ne repose sur
   une hauteur devinée :

     #chartDock (flex, colonne)
       .dock-list  flex: 1        →  hauteur de la colonne
         .dock-item.open flex: 1  →  la seule section affichée la remplit
           .dock-body    flex: 1  →  et lui transmet, sans plafond

   `animation: none` sur le corps : le dépliement anime `max-height` de 0
   à 640 : laissé en place, il rejouerait ce plafond pendant 1,2 s contre
   la hauteur qu'on vient de lui donner.

   NE CHANGE RIEN AUX QUATRE AUTRES SECTIONS : chaque `.dock-body` n'a
   qu'UN enfant, et il ne porte pas `flex` — il garde donc sa hauteur de
   contenu, en haut de la section, exactement comme avant. Seule
   « Échelle de prix » réclame le reste (voir ladder.css).

   Le dock FLOTTANT, lui, n'est pas touché : la règle est ancrée à
   `#ogPanel`.
   ═══════════════════════════════════════════════════════════════════════ */

body #ogPanel > #chartDock { display: flex; flex-direction: column; }

body #ogPanel > #chartDock > .dock-list {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

body #ogPanel > #chartDock .dock-item.open {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

body #ogPanel > #chartDock .dock-item.open > .dock-body {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
  animation: none;
  display: flex;
  flex-direction: column;
}
