/* La bulle de chat de la vitrine (2026-08-30).

   Dessinée sur le widget de l'espace client (`src/trading/static/css/chat.css`) :
   même médaillon, même en-tête teintée à liseré d'accent, mêmes bulles de
   message, même boîte de saisie qui s'allume au focus. Les deux surfaces sont
   le même produit, elles doivent se ressembler.

   ⚠️ Feuille AUTONOME, sans dépendance a une variable de charte quelconque.
   Cette bulle est chargée sur CINQ pages qui ne partagent pas les mêmes jetons
   de couleur (index.html/decisions.html en définissent un jeu, legal.css un
   autre, avec des noms différents) : lire l'un d'eux ferait dépendre le rendu
   de la page qui l'a chargée. Toutes les variables sont posées ICI.

   ⚠️ **LE DÉFAUT QUI A DÉFIGURÉ CETTE BULLE, ET QUI RECOMMENCERA.**
   `landing/index.html` applique `section { padding: clamp(72px,12vh,140px)
   clamp(20px,4vw,48px) }`, une règle de TAG, pensée pour les sections de la
   page de vente. Le panneau était un `<section>` : il héritait de 140 px de
   rembourrage, son en-tête flottait au milieu du vide et son contenu était
   décalé de 48 px. Vu à l'écran par le fondateur, jamais par un test.
   Deux protections, et il faut les DEUX :
     1. la bulle n'emploie plus aucun tag que la vitrine style au tag
        (plus de `section`, plus de `header`) ;
     2. le bloc de remise à zéro ci-dessous neutralise ce qui pourrait arriver
        demain sur `form`, `p`, `input`, `button` ou `ol`.
   Préfixer les CLASSES ne suffit pas quand le voisin style des TAGS.

   ⚠️ Pas de `zoom` : c'est un artefact de la charte de l'application, pas de la
   vitrine, qui n'a jamais eu ce problème. Les longueurs sont donc nues. */

.tv-chat {
  --tv-bg:        #14161F;
  --tv-bg-soft:   #1B1E29;
  --tv-border:    rgba(255,255,255,0.10);
  --tv-border-fort: rgba(255,255,255,0.16);
  --tv-fg:        #F5F4F0;
  --tv-fg-muted:  rgba(245,244,240,0.66);
  --tv-fg-dim:    rgba(245,244,240,0.42);
  --tv-accent:    #B4A8FF;
  --tv-accent-doux: rgba(180,168,255,0.16);
  --tv-accent-bord: rgba(180,168,255,0.32);
  --tv-erreur:    #F87171;
  --tv-ok:        #4ADE80;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, sans-serif;
  font-size: calc(14px * var(--echelle-texte, 1));
  line-height: 1.5;
}

/* La remise à zéro défensive : voir l'avertissement en tête de fichier.
   ⚠️ Elle ne touche QUE l'intérieur de `.tv-chat`, jamais la page.

   ⚠️ **`:where()` N'EST PAS UNE COQUETTERIE : il pèse ZÉRO en spécificité.**
   Écrite `.tv-chat div { background: none }`, cette remise à zéro vaut (0,1,1)
   et bat `.tv-chat-panneau { background: … }` qui ne vaut que (0,1,0) : le
   panneau s'affichait SANS AUCUN FOND, la page de vente au travers, les bulles
   transparentes et toutes les tailles de texte ramenées à celle du parent.
   Vu à l'écran, jamais par un test. Enveloppée dans `:where()`, la règle ne
   pèse plus rien et tout ce qui suit la reprend sans se battre. */
.tv-chat, .tv-chat *, .tv-chat *::before, .tv-chat *::after {
  box-sizing: border-box;
}
:where(.tv-chat) :where(div, p, ol, li, form, input, textarea, button,
                        strong, span, svg) {
  margin: 0; padding: 0; border: 0; background: none;
  font: inherit; color: inherit; list-style: none; text-align: left;
}

/* ⚠️ `display:` écrase l'attribut `hidden` : un `display: flex` posé plus bas
   gagnerait contre `hidden` et le panneau resterait visible. Cette règle doit
   rester la SEULE à fixer `display` sur ce qui porte `hidden` dans la bulle. */
.tv-chat [hidden] { display: none !important; }

/* ── Le lanceur ────────────────────────────────────────────────────────────
   Une pastille qui PARLE, contrairement à celle de l'espace client qui n'a
   qu'une icône : sur une page de vente, un rond muet ne dit pas ce qu'il fait.
   Le reste est repris tel quel : accent translucide, liseré, halo. */
.tv-chat-lanceur {
  /* /!\ UN CERCLE, LA MEME FORME QUE LA BULLE DE L'APPLICATION (fondateur,
     2026-09-13). Elle portait une pilule large avec le texte « Une question ? » ;
     la bulle de l'espace client, elle, est un rond de 54 px pose a 24 px du
     coin. Deux surfaces differentes pour le meme geste, sur deux ecrans que le
     meme client voit a quelques minutes d'intervalle.
     /!\ Le texte n'est pas perdu, il devient `aria-label` : un bouton qui n'a
     qu'une icone doit se nommer pour un lecteur d'ecran. */
  box-sizing: border-box;
  position: fixed; bottom: 24px; right: 24px; z-index: 999;
  display: inline-flex; align-items: center; justify-content: center;
  width: 54px; height: 54px; border-radius: 50%; padding: 0;
  background: rgba(180,168,255,0.18);
  border: 1px solid var(--tv-accent-bord);
  color: var(--tv-accent);
  cursor: pointer;
  box-shadow: 0 10px 30px -10px rgba(139,127,224,0.55), 0 2px 8px rgba(0,0,0,.35);
  transition: background .18s ease, border-color .18s ease,
              color .18s ease, box-shadow .18s ease, transform .18s ease;
}
.tv-chat-lanceur:hover {
  background: rgba(180,168,255,0.28);
  border-color: rgba(180,168,255,0.52);
  color: #FFFFFF;
  box-shadow: 0 14px 34px -10px rgba(139,127,224,0.75), 0 2px 8px rgba(0,0,0,.35);
}
.tv-chat-lanceur:focus-visible { outline: 2px solid var(--tv-accent); outline-offset: 3px; }
@media (prefers-reduced-motion: no-preference) {
  .tv-chat-lanceur:hover { transform: translateY(-2px); }
}
/* Ouverte, la bulle n'appelle plus : deux surfaces violettes l'une sur l'autre
   se concurrencent, et c'est le panneau qu'on vient regarder. Règle reprise
   telle quelle de l'espace client. */
.tv-chat--ouvert .tv-chat-lanceur {
  background: var(--tv-bg-soft);
  border-color: var(--tv-border-fort);
  color: var(--tv-fg-muted);
  box-shadow: 0 4px 14px rgba(0,0,0,.35);
}
.tv-chat--ouvert .tv-chat-lanceur:hover {
  background: var(--tv-bg-soft); color: var(--tv-fg);
  border-color: rgba(255,255,255,0.24);
}
.tv-chat-lanceur-icone { flex-shrink: 0; }

/* ── Le panneau ──────────────────────────────────────────────────────────── */
.tv-chat-panneau {
  position: fixed; bottom: 88px; right: 20px; z-index: 999;
  width: 384px; max-width: calc(100vw - 40px);
  /* /!\ PLUS HAUT DEPUIS LE 2026-09-14. A 540 px, l'en-tete, le fil, la carte
     de coordonnees et la barre d'ecriture ne tenaient plus ensemble : c'est la
     BARRE qui se faisait rogner, donc l'endroit ou l'on tape. Une fenetre de
     dialogue ou l'on ne voit pas ou ecrire n'est pas une fenetre de dialogue. */
  height: 620px; max-height: calc(100vh - 110px);
  background: var(--tv-bg);
  border: 1px solid var(--tv-border-fort);
  border-radius: 22px; overflow: hidden;
  box-shadow: 0 28px 70px -14px rgba(0,0,0,.62), 0 4px 14px rgba(0,0,0,.35);
  display: flex; flex-direction: column;
}

/* Sur téléphone, ancrée aux DEUX bords : pas de largeur propre à défendre.
   ⚠️ Deux ancrages plutôt qu'un `max-width` calculé, comme dans l'espace
   client. ⚠️ Non recetté sur un vrai téléphone : Chrome sans interface plafonne
   sa fenêtre autour de 500 px, donc une capture « 375 px » est un recadrage
   d'un rendu de 500 et la requête média n'y mord jamais. */
@media (max-width: 480px) {
  .tv-chat-panneau {
    left: 12px; right: 12px; width: auto; max-width: none;
    top: 12px; bottom: 84px; height: auto; max-height: none;
  }
}

/* ── L'en-tête : surface teintée, liseré d'accent, médaillon ──────────────
   ⚠️ Le liseré s'éteint vers la droite : un trait plein d'un bord à l'autre
   ferait une barre de titre de fenêtre, et ce panneau n'en est pas une. */
.tv-chat-entete {
  position: relative; flex-shrink: 0;
  display: flex; align-items: center; gap: 11px;
  padding: 14px 14px 13px 16px;
  background: linear-gradient(158deg, rgba(180,168,255,0.13) 0%, rgba(255,255,255,0.015) 72%);
  border-bottom: 1px solid var(--tv-border);
}
.tv-chat-entete::before {
  content: ""; position: absolute; left: 0; right: 32%; top: 0; height: 1px;
  background: linear-gradient(90deg, var(--tv-accent-bord), transparent);
}
.tv-chat-medaillon {
  flex-shrink: 0; width: 32px; height: 32px; border-radius: 11px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--tv-accent-doux);
  border: 1px solid var(--tv-accent-bord);
  color: var(--tv-accent);
}
.tv-chat-qui { min-width: 0; flex: 1; }
.tv-chat-qui strong {
  display: block; font-size: calc(13.5px * var(--echelle-texte, 1)); font-weight: 600; color: var(--tv-fg);
}
.tv-chat-qui p {
  margin-top: 2px; font-size: calc(11.5px * var(--echelle-texte, 1)); color: var(--tv-fg-muted);
}
.tv-chat-fermer {
  flex-shrink: 0; width: 28px; height: 28px; border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--tv-fg-muted); cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.tv-chat-fermer:hover { background: rgba(255,255,255,0.07); color: var(--tv-fg); }
.tv-chat-fermer:focus-visible { outline: 2px solid var(--tv-accent); outline-offset: 1px; }

/* ── Le fil ──────────────────────────────────────────────────────────────── */
/* /!\ `min-height` DE 150 PX, ET CE N'EST PAS DU CONFORT. Le panneau fait
   540 px ; quand le serveur fait surgir la carte de coordonnees (trois champs
   et un bouton, ~260 px) par-dessus la saisie, il ne restait qu'une soixantaine
   de pixels au fil : la reponse de l'assistant etait bien la, dans une bande
   trop basse pour se lire, et on croyait qu'il n'avait rien repondu. Signale
   par le fondateur le 2026-09-14, capture a l'appui.
   /!\ `flex: 1` seul ne protege rien : il donne le RESTE, et le reste peut
   valoir zero. C'est le plancher qui tient. */
.tv-chat-fil {
  flex: 1; min-height: 120px; overflow-y: auto;
  padding: 16px; display: flex; flex-direction: column; gap: 9px;
  scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.16) transparent;
}
.tv-chat-fil::-webkit-scrollbar { width: 8px; }
.tv-chat-fil::-webkit-scrollbar-thumb {
  background: rgba(255,255,255,0.14); border-radius: 999px;
}
.tv-chat-message { display: flex; }
.tv-chat-message--client { justify-content: flex-end; }
.tv-chat-message--equipe { justify-content: flex-start; }
.tv-chat-bulle {
  max-width: 84%; padding: 9px 12px; border-radius: 14px;
  font-size: calc(13px * var(--echelle-texte, 1)); line-height: 1.55;
  white-space: pre-wrap; word-break: break-word;
}
.tv-chat-message--equipe .tv-chat-bulle {
  background: var(--tv-bg-soft);
  border: 1px solid var(--tv-border);
  color: var(--tv-fg);
  border-bottom-left-radius: 6px;
}
.tv-chat-message--client .tv-chat-bulle {
  background: var(--tv-accent-doux);
  border: 1px solid var(--tv-accent-bord);
  color: var(--tv-fg);
  border-bottom-right-radius: 6px;
}
.tv-chat-auteur {
  display: block; margin-bottom: 3px;
  font-size: calc(11px * var(--echelle-texte, 1)); font-weight: 600; color: var(--tv-accent);
}

/* ── LE MESSAGE DE L'ASSISTANT IA — reconnaissable AVANT d'etre lu ──────────
   /!\ **C'ETAIT LE DEFAUT** (fondateur, 2026-09-14) : le robot et une personne
   de l'equipe rendaient la MEME bulle, au meme endroit, dans la meme couleur.
   Rien ne les distinguait, et la question « qui m'a repondu ? » n'avait pas de
   reponse a l'ecran.
   /!\ **LA DIFFERENCE EST UNE ETIQUETTE, PAS UN FOND.** Un aplat d'accent
   ferait passer les reponses du robot pour les plus importantes du fil ; un gris
   les ferait passer pour du systeme. La bulle garde son fond, et prend un lisere
   a gauche plus une etiquette nommee. */
.tv-chat-message--ia .tv-chat-bulle {
  border-left: 2px solid var(--tv-accent-bord);
  border-top-left-radius: 6px;
}
/* /!\ `flex` ET NON `inline-flex` : `.tv-chat-auteur` est posee `display: block`
   juste au-dessus, et le corps du message est un simple noeud TEXTE dans la meme
   bulle (pas un element a lui). Rendue en ligne, l'etiquette se collait au
   premier mot : « Assistant IAUn PC Windows… ». Vu a l'ecran le 2026-09-14. */
.tv-chat-auteur-ia {
  display: flex; align-items: center; gap: 5px;
  color: var(--tv-fg-muted);
}
.tv-chat-ia-point {
  width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: var(--tv-accent);
}

/* ── La présence : « Marie a vu votre message » / « Marie vous répond… » ──
   Une ligne discrète, au-dessus de la saisie : c'est le fil et le composeur
   qui portent l'attention, pas cette ligne. */
.tv-chat-presence {
  flex-shrink: 0; margin: 0 16px 8px;
  font-size: calc(11.5px * var(--echelle-texte, 1)); color: var(--tv-fg-muted);
}
.tv-chat-presence--ecrit { color: var(--tv-accent); }

/* Les trois points de « ... vous répond » : un déroulé sobre, jamais un
   clignotement. Rien ici ne vient du serveur : décoratif, `aria-hidden`. */
.tv-chat-presence-points {
  display: inline-block; overflow: hidden; vertical-align: bottom;
  width: 1.2em;
}
.tv-chat-presence-points::after { content: "..."; }
@media (prefers-reduced-motion: no-preference) {
  .tv-chat-presence-points {
    width: 0;
    animation: tv-chat-points 1.2s steps(4, end) infinite;
  }
}
@keyframes tv-chat-points {
  0%   { width: 0; }
  100% { width: 1.2em; }
}

/* ── Le lien discret vers les coordonnées, quand le serveur ne les demande
   pas : la carte reste toujours ATTEIGNABLE, jamais seulement affichée. ── */
.tv-chat-coord-lien {
  flex-shrink: 0; align-self: flex-start; margin: 0 16px 10px;
  font-size: calc(11.5px * var(--echelle-texte, 1)); color: var(--tv-fg-muted);
  text-decoration: underline; text-underline-offset: 2px;
  cursor: pointer;
}
.tv-chat-coord-lien:hover { color: var(--tv-accent); }
.tv-chat-coord-lien:focus-visible { outline: 2px solid var(--tv-accent); outline-offset: 2px; }

/* ── L'avis (une panne, un refus, un accusé) ─────────────────────────────── */
.tv-chat-avis {
  flex-shrink: 0; margin: 0 16px 10px; padding: 9px 11px;
  border-radius: 10px; font-size: calc(12px * var(--echelle-texte, 1)); line-height: 1.5;
  background: rgba(248,113,113,0.10);
  border: 1px solid rgba(248,113,113,0.28);
  color: #FCA5A5;
}
.tv-chat-avis--ok {
  background: rgba(74,222,128,0.10);
  border-color: rgba(74,222,128,0.28);
  color: #86EFAC;
}

/* ── Les coordonnées : le geste demandé APRÈS le premier message ──────────
   Une carte posée au-dessus du composeur, pas une fenêtre par-dessus le fil :
   la conversation doit rester lisible pendant qu'on la remplit. */
/* Resserree le 2026-09-14 : elle prenait la moitie du panneau. Les deux
   champs facultatifs restent la, en plus bas. */
.tv-chat-coordonnees {
  flex-shrink: 0; padding: 11px 16px 12px;
  border-top: 1px solid var(--tv-border);
  background: linear-gradient(180deg, rgba(180,168,255,0.07), transparent 90%);
  display: flex; flex-direction: column; gap: 6px;
}
.tv-chat-coordonnees input { padding: 8px 11px; }
.tv-chat-coordonnees p {
  font-size: calc(12.5px * var(--echelle-texte, 1)); font-weight: 600; color: var(--tv-fg); margin-bottom: 2px;
}
.tv-chat-coordonnees input {
  width: 100%; padding: 9px 11px; border-radius: 10px;
  background: rgba(255,255,255,0.035);
  border: 1px solid var(--tv-border);
  color: var(--tv-fg); font-size: calc(13px * var(--echelle-texte, 1));
}
.tv-chat-coordonnees input::placeholder { color: var(--tv-fg-dim); }
.tv-chat-coordonnees input:focus {
  outline: none;
  border-color: var(--tv-accent-bord);
  background: rgba(255,255,255,0.055);
}
.tv-chat-bouton {
  align-self: flex-end; padding: 8px 16px; border-radius: 10px;
  background: var(--tv-accent-doux);
  border: 1px solid var(--tv-accent-bord);
  color: var(--tv-accent); font-size: calc(12.5px * var(--echelle-texte, 1)); font-weight: 600; cursor: pointer;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.tv-chat-bouton:hover {
  background: rgba(180,168,255,0.26); color: #FFFFFF;
  border-color: rgba(180,168,255,0.52);
}
.tv-chat-bouton:focus-visible { outline: 2px solid var(--tv-accent); outline-offset: 2px; }
.tv-chat-bouton[disabled] { opacity: .5; cursor: default; }

/* ── Le composeur : UNE boîte, qui s'allume au focus ──────────────────────
   Reprise exacte du geste de l'espace client : séparés, le champ et son bouton
   se lisaient comme deux objets pour un seul geste. */
.tv-chat-envoi {
  flex-shrink: 0; padding: 12px 16px 14px;
  border-top: 1px solid var(--tv-border);
}
/* LA BARRE D'ETAT (option C, 2026-09-14) : qui repond, et la porte de sortie.
   /!\ Elle vit AU-DESSUS de la saisie et garde sa place dans les deux etats :
   c'est ce qui lui permet de changer de texte sans deplacer le champ sous le
   doigt de quelqu'un qui ecrit.
   /!\ Deux teintes SEMANTIQUES, pas decoratives : l'accent pour l'assistant, un
   ambre pour une personne. C'est la couleur qui porte l'information quand le
   texte est lu en diagonale. */
.tv-chat-barre {
  display: flex; align-items: center; gap: 8px;
  padding: 0 4px 8px; font-size: calc(12px * var(--echelle-texte, 1)); line-height: 1.4;
}
.tv-chat-point {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--tv-accent-bord);
}
.tv-chat-barre[data-qui="equipe"] .tv-chat-point { background: #d9894c; }
/* /!\ **UN TROISIEME ETAT : « attente »** (2026-09-14). La demande est partie,
   personne n'a encore repris. Il lui fallait sa teinte : peint en accent comme
   l'assistant, il dirait le contraire de ce qu'il annonce ; peint en ambre comme
   une personne, il promettrait quelqu'un qui n'est pas la.
   /!\ La pastille RESPIRE, elle ne clignote pas — et pas du tout si la personne
   a demande moins d'animation. */
.tv-chat-barre[data-qui="attente"] .tv-chat-point {
  background: var(--tv-fg-muted, rgba(255,255,255,0.5));
}
@media (prefers-reduced-motion: no-preference) {
  .tv-chat-barre[data-qui="attente"] .tv-chat-point {
    animation: tv-chat-respire 1.8s ease-in-out infinite;
  }
}
@keyframes tv-chat-respire {
  0%, 100% { opacity: .35; }
  50%      { opacity: 1; }
}
/* /!\ **CETTE REGLE S'APPELAIT `.tv-chat-qui`, ET ELLE PEIGNAIT AUSSI L'EN-TETE**
   (2026-09-14). Le bloc « qui repond » de l'en-tete porte la MEME classe, deux
   cents lignes plus haut : la couleur de la barre d'etat descendait donc sur le
   titre du panneau, et personne ne pouvait le deviner en lisant l'un des deux
   endroits. Deux roles, deux noms. */
.tv-chat-barre-qui { color: var(--tv-fg-doux, rgba(255,255,255,0.62)); min-width: 0; }
.tv-chat-humain {
  margin-left: auto; flex: none;
  background: none; border: 0; padding: 2px 0;
  color: var(--tv-accent-bord); font: inherit; font-weight: 600;
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer;
}
.tv-chat-humain:hover { color: var(--tv-fg); }
.tv-chat-humain:focus-visible { outline: 2px solid var(--tv-accent-bord); outline-offset: 2px; border-radius: 3px; }
.tv-chat-humain[hidden] { display: none; }

.tv-chat-saisie {
  display: flex; align-items: flex-end; gap: 8px;
  padding: 8px 8px 8px 12px; border-radius: 14px;
  background: rgba(255,255,255,0.035);
  border: 1px solid var(--tv-border);
  transition: border-color .15s ease, background .15s ease;
}
/* Toute la boîte s'allume, comme la communauté (fondateur, 10/11/2026). Mêmes
   valeurs que `--saisie-allumee-*` de la charte, que la vitrine ne charge pas. */
.tv-chat-envoi:focus-within .tv-chat-saisie {
  border-color: rgba(180,168,255,0.55);
  box-shadow: 0 0 0 3px rgba(139,127,224,0.16);
  background: rgba(255,255,255,0.055);
}
/* /!\ `overflow-y: auto` ET `max-height` VONT ENSEMBLE : la zone grandit avec
   le texte (c'est `ajusterSaisie` qui pose la hauteur), et au-dela de 96 px
   c'est elle qui defile. Sans le plafond, une longue question mangerait le
   panneau ; sans `overflow`, la barre de defilement du navigateur s'affichait
   des la deuxieme ligne, collee au bouton d'envoi. */
.tv-chat-envoi textarea {
  flex: 1; min-width: 0; resize: none;
  height: auto; max-height: 96px; overflow-y: auto;
  color: var(--tv-fg); font-size: calc(13.5px * var(--echelle-texte, 1)); line-height: 1.55;
  scrollbar-width: thin;
}
.tv-chat-envoi textarea::placeholder { color: var(--tv-fg-dim); }
.tv-chat-envoi textarea:focus-visible { outline: none; }
.tv-chat-envoyer {
  flex-shrink: 0; width: 34px; height: 34px; border-radius: 11px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--tv-accent-doux);
  border: 1px solid var(--tv-accent-bord);
  color: var(--tv-accent); cursor: pointer;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.tv-chat-envoyer:hover {
  background: rgba(180,168,255,0.28); color: #FFFFFF;
  border-color: rgba(180,168,255,0.52);
}
.tv-chat-envoyer:focus-visible { outline: 2px solid var(--tv-accent); outline-offset: 2px; }
.tv-chat-envoyer[disabled] { opacity: .5; cursor: default; }

/* ── LA CARTE D'INSCRIPTION (2026-09-13) ─────────────────────────────────────
   « Demarrer » ouvre la bulle LA-DESSUS, pas sur une conversation vide : celui
   qui veut juste un compte ne doit pas avoir a negocier avec un robot.

   /!\ Elle reprend la grammaire de `.tv-chat-coordonnees` (memes champs, meme
   bouton) : deux formulaires dans la meme bulle ne doivent pas se dessiner de
   deux facons. */
.tv-chat-inscription {
  flex-shrink: 0; padding: 15px 16px 6px;
  display: flex; flex-direction: column; gap: 8px;
}
.tv-chat-inscription > p {
  font-size: calc(13.5px * var(--echelle-texte, 1)); font-weight: 600; color: var(--tv-fg); margin-bottom: 2px;
}
.tv-chat-inscription input {
  width: 100%; padding: 11px 12px; border-radius: 10px;
  background: rgba(255,255,255,0.035);
  border: 1px solid var(--tv-border);
  color: var(--tv-fg); font-size: calc(13.5px * var(--echelle-texte, 1)); font-family: inherit;
}
.tv-chat-inscription input::placeholder { color: var(--tv-fg-dim); }
.tv-chat-inscription input:focus {
  outline: none; border-color: var(--tv-accent-bord);
  background: rgba(255,255,255,0.055);
}
/* AU TELEPHONE, AUCUN CHAMP DE LA BULLE SOUS 16 POINTS (TVNI-142, 21/09/2026) :
   sous 16 px, Safari sur iPhone agrandit toute la page au toucher du champ.
   Meme plancher que la charte de l'application ; `!important` parce que trois
   regles ci-dessus reduisent la taille selon le panneau (13 a 13,5 px).
   Ordinateur inchange. /!\ Monter le `?v=` de chat.css sur toute la vitrine. */
@media (max-width: 720px), (hover: none) and (pointer: coarse) {
  .tv-chat input:not([type="checkbox"], [type="radio"], [type="file"],
                     [type="hidden"], [type="submit"], [type="button"]),
  .tv-chat textarea,
  .tv-chat select { font-size: 16px !important; }
}
.tv-chat-inscription .tv-chat-bouton {
  align-self: stretch; text-align: center; padding: 12px 16px;
  font-size: calc(13.5px * var(--echelle-texte, 1)); margin-top: 2px;
  background: rgba(180,168,255,0.26); color: #FFFFFF;
  border-color: rgba(180,168,255,0.52);
}
/* La case « nouveautes et invitations » (22/09/2026). `.tv-chat-inscription
   input` vaut (0,1,1) et habillerait la coche comme un champ : la case porte
   donc le meme poids, plus un. */
.tv-chat-inscription .tv-chat-case {
  display: flex; align-items: flex-start; gap: 8px; cursor: pointer;
  font-size: calc(12.5px * var(--echelle-texte, 1)); line-height: 1.4; color: var(--tv-fg-dim); margin: 2px 0;
}
.tv-chat-inscription .tv-chat-case input {
  flex: 0 0 auto; width: 15px; height: 15px; margin: 1px 0 0; padding: 0;
  accent-color: var(--tv-accent); cursor: pointer;
}
/* /!\ `.tv-chat-inscription p` vaut (0,1,1) et ecraserait une regle de classe
   seule : la mention porte donc le MEME poids que son parent pour gagner. Meme
   piege que celui decrit en tete de ce fichier. */
.tv-chat-inscription p.tv-chat-mention {
  font-size: calc(11.5px * var(--echelle-texte, 1)); font-weight: 400; color: var(--tv-fg-dim);
  text-align: center; margin: 4px 0 0;
}
.tv-chat-bascule {
  flex-shrink: 0; background: none; border: 0; cursor: pointer;
  color: var(--tv-accent); font-size: calc(12.5px * var(--echelle-texte, 1)); padding: 8px 16px 14px;
  text-align: center; text-decoration: underline; text-underline-offset: 3px;
}

/* /!\ La carte se mesure a son CONTENU. Le panneau fait 540 px de haut, pense
   pour un fil qui defile : une carte de trois champs y flottait au-dessus d'un
   grand vide. Vu a l'ecran, jamais par un test. */
.tv-chat-panneau--carte { height: auto; }
@media (max-width: 480px) {
  .tv-chat-panneau--carte { top: auto; height: auto; bottom: 84px; }
}


/* ══════════════════════════════════════════════════════════════════════════
   L'APPEL, a cote du lanceur  (2026-09-13, fondateur)
   ──────────────────────────────────────────────────────────────────────────
   /!\ IL NE RECOUVRE JAMAIS LE LANCEUR : il se pose AU-DESSUS de lui, avec la
   meme marge droite, et le cercle reste cliquable. Une infobulle qui masque le
   bouton qu'elle annonce est une infobulle qu'on referme avec agacement.
   /!\ SUR TELEPHONE il se borne a la largeur de l'ecran moins les deux marges,
   sinon il deborde a droite et pousse une barre de defilement horizontale sur
   toute la page. */
.tv-chat-appel[hidden] { display: none; }
.tv-chat-appel {
  position: fixed; right: 24px; bottom: 90px; z-index: 998;
  width: min(300px, calc(100vw - 48px));
  padding: 16px 38px 16px 18px;
  border-radius: 16px;
  background: var(--tv-bg-soft);
  border: 1px solid var(--tv-accent-bord);
  box-shadow: 0 18px 40px -14px rgba(0,0,0,.65);
  cursor: pointer;
  animation: tv-chat-appel-entree 420ms cubic-bezier(.22,1,.36,1) both;
}
@media (prefers-reduced-motion: reduce) {
  .tv-chat-appel { animation: none; }
}
@keyframes tv-chat-appel-entree {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
.tv-chat-appel p { margin: 0; font-size: calc(13.5px * var(--echelle-texte, 1)); line-height: 1.5; color: var(--tv-fg-muted); }
.tv-chat-appel .tv-chat-appel-fort { color: var(--tv-fg); font-weight: 500; margin-bottom: 5px; }
.tv-chat-appel:hover { border-color: var(--tv-accent); }
.tv-chat-appel-fermer {
  position: absolute; top: 8px; right: 8px;
  width: 24px; height: 24px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--tv-fg-dim);
}
.tv-chat-appel-fermer:hover { color: var(--tv-fg); }
/* Ouverte, la bulle a dit ce qu'elle avait a dire : l'appel n'a plus lieu. */
.tv-chat--ouvert .tv-chat-appel { display: none; }


/* Les questions rapides, juste au-dessus de la saisie. */
.tv-chat-rapides[hidden] { display: none; }
.tv-chat-rapides {
  display: flex; flex-wrap: wrap; gap: 7px;
  padding: 0 14px 10px;
}
.tv-chat-rapide {
  font: inherit; font-size: calc(12.5px * var(--echelle-texte, 1)); line-height: 1.3;
  color: var(--tv-accent); cursor: pointer;
  padding: 7px 12px; border-radius: 999px;
  background: var(--tv-accent-doux);
  border: 1px solid var(--tv-accent-bord);
  transition: background .15s ease, border-color .15s ease;
}
.tv-chat-rapide:hover { background: rgba(180,168,255,0.26); border-color: var(--tv-accent); }
.tv-chat-rapide:focus-visible { outline: 2px solid var(--tv-accent); outline-offset: 2px; }


/* /!\ **LE BANDEAU `.tv-chat-main` A ETE RETIRE le 2026-09-14**, avec ses
   styles. Il annoncait « votre demande est partie » dans un pave d'accent, et il
   doublait la barre d'etat qui, au meme instant, affirmait le contraire. Il ne
   s'eteignait jamais non plus. Sa phrase est le troisieme etat de la barre.
   Ne pas reintroduire une seconde surface pour le meme etat. */


/* « L'assistant écrit… » : posé juste sous le fil, au-dessus de la barre.
   ⚠️ Il ne pousse rien : `flex-shrink: 0` et une seule ligne. */
.tv-chat-ecrit[hidden] { display: none; }
.tv-chat-ecrit {
  flex-shrink: 0; margin: 0 16px 8px;
  font-size: calc(11.5px * var(--echelle-texte, 1)); color: var(--tv-accent);
}


/* Le trombone, dans la barre d'écriture. Il ne paraît qu'après le premier
   message : le serveur refuse un dépôt sur un fil resté muet. */
.tv-chat-joindre[hidden] { display: none; }
.tv-chat-joindre {
  flex-shrink: 0; width: 30px; height: 30px; border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--tv-fg-dim);
  transition: color .15s ease, background .15s ease;
}
.tv-chat-joindre:hover { color: var(--tv-accent); background: var(--tv-accent-doux); }
.tv-chat-joindre:disabled { opacity: .5; cursor: default; }
.tv-chat-joindre:focus-visible { outline: 2px solid var(--tv-accent); outline-offset: 2px; }

/* ── LA DICTÉE (TVNI-72, 2026-09-21) ─────────────────────────────────────────
   Le micro que pose `dictee.js` (servi par l'application, chargé par
   `chat.js`) juste après la zone d'écriture. Même allure que le trombone ;
   rouge qui pulse pendant l'écoute. Les règles de l'application vivent dans
   `static/css/dictee.css` : ici, seulement la version de la bulle, avec ses
   propres couleurs (cette feuille ne lit aucune variable de la page). */
.tv-chat .dictee-place {
  position: relative; display: inline-flex; flex: none; align-items: center;
}
.tv-chat .dictee-micro {
  flex-shrink: 0; width: 30px; height: 30px; border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 1px solid transparent; padding: 0; margin: 0;
  cursor: pointer; color: var(--tv-fg-dim);
  transition: color .15s ease, background .15s ease;
}
.tv-chat .dictee-micro:hover { color: var(--tv-accent); background: var(--tv-accent-doux); }
.tv-chat .dictee-micro:focus-visible { outline: 2px solid var(--tv-accent); outline-offset: 2px; }
.tv-chat .dictee-micro[aria-pressed="true"] {
  color: var(--tv-erreur); background: rgba(248,113,113,0.14);
  border-color: rgba(248,113,113,0.45);
  animation: tv-dictee-pulse 1.4s ease-in-out infinite;
}
@keyframes tv-dictee-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(248,113,113,0.45); }
  70%  { box-shadow: 0 0 0 7px rgba(248,113,113,0); }
  100% { box-shadow: 0 0 0 0 rgba(248,113,113,0); }
}
@media (prefers-reduced-motion: reduce) {
  .tv-chat .dictee-micro[aria-pressed="true"] { animation: none; }
}
.tv-chat .dictee-avis {
  position: absolute; bottom: calc(100% + 10px); right: -40px; z-index: 5;
  width: max-content; max-width: 230px;
  padding: 7px 10px; border-radius: 10px;
  background: var(--tv-bg-soft); color: var(--tv-fg);
  border: 1px solid var(--tv-border-fort);
  box-shadow: 0 8px 24px rgba(0,0,0,0.35);
  font-size: calc(12px * var(--echelle-texte, 1)); line-height: 1.4; pointer-events: none;
}
.tv-chat .dictee-avis[hidden] { display: none; }

/* La pièce, dans la bulle du message qui la porte. */
.tv-chat-piece {
  display: block; margin-top: 2px;
  font-size: calc(12.5px * var(--echelle-texte, 1)); color: var(--tv-accent);
  text-decoration: underline; text-underline-offset: 2px;
  word-break: break-all;
}
.tv-chat-piece:hover { color: var(--tv-fg); }


/* ── LE BADGE DES RÉPONSES NON LUES (TVNI-28, 2026-09-18) ────────────────────
   Le même rond rouge que la pastille de l'espace client (`.chat-pastille`) :
   deux surfaces, un seul signe pour « une réponse vous attend ». Posé sur le
   bord du lanceur, il ne cache pas le casque. Ouverte, la bulle n'en a pas.
   ⚠️ `display` n'est posé que sur le badge VISIBLE : la règle `[hidden]` du
   haut de ce fichier doit rester la seule à décider de sa présence. */
/* ⚠️ Réglé avec celui de l'espace client le même jour (fondateur : « l'affichage
   du 1 n'est pas top ») : plus grand, chiffre centré en flex, rouge franc sur la
   pastille SEULE — le cercle reste violet et s'allume, il ne vire plus au rouge. */
.tv-chat-badge:not([hidden]) {
  display: inline-flex; align-items: center; justify-content: center;
}
.tv-chat-badge {
  position: absolute; top: -6px; right: -6px;
  min-width: 22px; height: 22px; padding: 0 6px; box-sizing: border-box;
  border-radius: 999px; border: 2px solid var(--tv-bg);
  background: #EF4444; color: #FFFFFF;
  font-size: calc(12px * var(--echelle-texte, 1)); font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums;
  box-shadow: 0 2px 6px rgba(0,0,0,0.45);
}
.tv-chat--ouvert .tv-chat-badge { display: none; }
.tv-chat-lanceur:has(.tv-chat-badge:not([hidden])) {
  border-color: var(--tv-accent);
  box-shadow: 0 10px 30px -10px rgba(139,127,224,0.55), 0 0 0 3px var(--tv-accent-doux);
}

/* ── LES LIENS D'UNE RÉPONSE (TVNI-29) ───────────────────────────────────────
   Construits par `corpsAvecLiens` à partir des morceaux triés par le serveur :
   un lien ici mène toujours chez nous. */
.tv-chat-lien {
  color: var(--tv-accent); font-weight: 600;
  text-decoration: underline; text-underline-offset: 2px;
}
.tv-chat-lien:hover { color: var(--tv-fg); }


/* ══ LE THÈME DU JEU (30/10/2026) ════════════════════════════════════════════
   Fondateur : la vitrine passe au thème de l'espace client. La bulle suit :
   pastille ronde au liseré violet vers rose, panneau de verre à liseré violet,
   gestes principaux en pilule au dégradé du jeu (`src/trading/static/css/jeu.css`,
   `.btn-primary`). Toujours AUTONOME (voir la tête du fichier) : les valeurs sont
   posées ici, pas lues dans la page. Rien ne bouge davantage : seuls la lumière
   et les formes changent. /!\ Monter le `?v=` de chat.css sur toute la vitrine. */
.tv-chat {
  --tv-degrade: linear-gradient(100deg, #7C6FD8, #A78BFA 55%, #C084FC);
  --tv-lisere:  rgba(180,168,255,0.24);
  font-family: 'Geist', -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, sans-serif;
}
.tv-chat-lanceur {
  color: #FFFFFF; border: 1px solid transparent;
  background: linear-gradient(140deg, rgba(139,127,224,0.55), rgba(244,114,182,0.30)) padding-box,
              linear-gradient(140deg, rgba(200,190,255,0.85), rgba(244,114,182,0.70)) border-box;
  box-shadow: 0 12px 32px -10px rgba(139,127,224,0.75), 0 2px 8px rgba(0,0,0,.35);
}
.tv-chat-lanceur:hover {
  color: #FFFFFF; border-color: transparent;
  background: linear-gradient(140deg, rgba(139,127,224,0.75), rgba(244,114,182,0.42)) padding-box,
              linear-gradient(140deg, rgba(220,212,255,0.95), rgba(244,114,182,0.85)) border-box;
  box-shadow: 0 16px 36px -10px rgba(139,127,224,0.85), 0 2px 8px rgba(0,0,0,.35);
}
/* Ouverte, la bulle n'appelle plus (règle d'origine, plus haut) : elle repasse
   en gris, sans dégradé. */
.tv-chat--ouvert .tv-chat-lanceur, .tv-chat--ouvert .tv-chat-lanceur:hover {
  background: var(--tv-bg-soft); border-color: var(--tv-border-fort);
}
.tv-chat-panneau {
  border-color: var(--tv-lisere);
  background: radial-gradient(120% 60% at 100% 0%, rgba(139,127,224,0.10), transparent 60%), var(--tv-bg);
  box-shadow: 0 28px 70px -14px rgba(0,0,0,.62), 0 0 0 1px rgba(139,127,224,0.06),
              0 20px 60px -30px rgba(139,127,224,0.45);
}
.tv-chat-entete {
  background: linear-gradient(158deg, rgba(139,127,224,0.20) 0%, rgba(244,114,182,0.05) 55%, rgba(255,255,255,0.01) 80%);
}
.tv-chat-entete::before {
  right: 0;
  background: linear-gradient(90deg, rgba(139,127,224,0.70), rgba(244,114,182,0.50) 55%, rgba(252,211,77,0.35) 80%, transparent);
}
.tv-chat-medaillon {
  color: #FFFFFF; border-color: rgba(255,255,255,0.14);
  background: linear-gradient(140deg, #8B7FE0, #F472B6);
  box-shadow: 0 6px 16px rgba(139,127,224,0.35);
}
.tv-chat-message--client .tv-chat-bulle {
  background: linear-gradient(120deg, rgba(139,127,224,0.30), rgba(167,139,250,0.18));
  border-color: rgba(180,168,255,0.38);
}
/* Les gestes principaux : la pilule au dégradé du jeu. */
.tv-chat-bouton, .tv-chat-inscription .tv-chat-bouton {
  border-radius: 999px; color: #FFFFFF;
  background: var(--tv-degrade);
  border: 1px solid rgba(255,255,255,0.14);
  box-shadow: 0 6px 18px rgba(139,127,224,0.30), inset 0 1px 0 rgba(255,255,255,0.18);
  transition: filter .15s ease, box-shadow .15s ease;
}
.tv-chat-bouton:hover:not([disabled]), .tv-chat-inscription .tv-chat-bouton:hover:not([disabled]) {
  background: var(--tv-degrade); color: #FFFFFF; border-color: rgba(255,255,255,0.24);
  filter: brightness(1.08); box-shadow: 0 10px 24px rgba(139,127,224,0.40), inset 0 1px 0 rgba(255,255,255,0.18);
}
.tv-chat-envoyer {
  border-radius: 50%; color: #FFFFFF;
  background: var(--tv-degrade);
  border: 1px solid rgba(255,255,255,0.14);
  box-shadow: 0 4px 12px rgba(139,127,224,0.30);
}
.tv-chat-envoyer:hover:not([disabled]) { background: var(--tv-degrade); color: #FFFFFF; filter: brightness(1.08); }
.tv-chat-saisie { border-radius: 20px; }
.tv-chat-rapide { border-color: var(--tv-lisere); background: rgba(139,127,224,0.12); }
.tv-chat-rapide:hover { background: rgba(139,127,224,0.24); border-color: rgba(180,168,255,0.55); }
