/* shared/pages/espace-patient.css — feuille de espace-patient.html, sortie de son bloc \3c style> le 17/10/2026 (lot 8 du dossier 1 : CSP sans
   style en ligne). Chargée par une balise <link> à la place exacte de l’ancien bloc : même ordre dans la cascade. */
/* ============================================================================
 * CSS spécifique à la page Espace patient (messagerie)
 * ============================================================================ */

/* Layout 2 colonnes : sidebar | conversation centrée — IDENTIQUE au funnel
   conversation.html (continuité parfaite après paiement, le patient ne doit pas
   « changer de conversation »). L'ancienne 3e colonne (aside RDV + carte psy,
   340px) a été RETIRÉE : la conversation est désormais centrée sur une seule
   colonne (max-width 760px, cf. .thread / .composer-wrap ci-dessous), exactement
   comme le funnel. « Prendre rendez-vous » est relocalisé dans l'en-tête.
   On force `height: 100vh` (au lieu du `min-height: 100vh` global) pour que
   l'écran de messagerie tienne pile dans le viewport — sinon le composer +
   thread-head + thread minimum dépassent et l'utilisateur doit scroller
   la page entière. `overflow: hidden` clip toute marge de dépassement. */
.app {
  grid-template-columns: 248px minmax(0, 1fr);
  height: 100vh;
  overflow: hidden;
}

.main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;            /* permet à .thread (flex:1) de shrink + scroll */
  background: var(--surface);
  /* Plus de border-right : l'aside de droite a été retiré, la colonne
     conversation occupe tout l'espace restant (comme .conv-main du funnel,
     qui n'a aucune bordure droite). */
}

/* États messagerie : cachés par défaut, révélés par `.is-visible`.
   On utilise `display: none !important` pour gagner contre tout style inline. */
.msg-state { display: none !important; }
.msg-state.is-visible { display: flex !important; }
.msg-state-empty { flex: 1; align-items: center; justify-content: center; padding: var(--palette-espacement-components-3); }
.msg-state-thread { flex: 1; flex-direction: column; min-height: 0; }
/* (Vague 2 UX) état de chargement = SQUELETTE de fil (plus de « Chargement… »). */
#loadingMessaging { flex: 1; padding: var(--palette-espacement-composants-2) var(--palette-espacement-cabinet-conversation-2); }
#loadingMessaging .skel-thread { display: flex; flex-direction: column; gap: var(--palette-espacement-cabinet-conversation-5); width: 100%; max-width: 760px; margin: 0 auto; }

/* Header conversation */
.thread-head {
  display: flex;
  align-items: center;
  gap: var(--palette-espacement-cabinet-conversation-5);
  padding: var(--palette-espacement-cabinet-conversation-10) var(--palette-espacement-invitation-4);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.thread-head .av {
  width: 46px;
  height: 46px;
  border-radius: var(--palette-rayon-composants-2);
  background: linear-gradient(135deg, var(--accent-soft), var(--adaptation-fond-action-accent, var(--accent)));
  color: var(--palette-texte-inverse-components);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: var(--palette-typographie-cabinet-conversation-5);
  position: relative;
  flex: 0 0 46px;
  border: 2px solid var(--adaptation-bordure-surface, var(--surface));
  box-shadow: 0 0 0 1px var(--adaptation-reflet-line-strong, var(--line-strong));
}
.thread-head .av::after {
  content: "";
  position: absolute;
  right: -2px;
  bottom: -2px;
  width: 12px;
  height: 12px;
  border-radius: var(--palette-rayon-composants-2);
  background: var(--palette-fond-succes-espace-patient);
  border: 2px solid var(--adaptation-bordure-surface, var(--surface));
}
.thread-head h1 { font-size: var(--palette-typographie-composants-9); margin: 0; font-weight: 600; letter-spacing: var(--palette-typographie-layout-4); color: var(--ink); }
.thread-head .sub { font-size: var(--palette-typographie-cabinet-conversation-3); color: var(--muted); }
/* head-spacer SUPPRIMÉ (cf. demarrer.html) : il dupliquait le flex:1 du
   bloc texte → en mobile le texte se retrouvait dans 50% de la largeur. */
.head-spacer { display: none; }
.head-pill { display: inline-flex; align-items: center; gap: var(--palette-espacement-cabinet-conversation-3); padding: var(--palette-espacement-cabinet-conversation-3) var(--palette-espacement-cabinet-conversation-6); border-radius: var(--palette-rayon-components-2); border: 1px solid var(--line); background: var(--surface-2); font-size: var(--palette-typographie-cabinet-conversation-4); color: var(--muted); }
.head-pill .dot-sm { width: 6px; height: 6px; border-radius: var(--palette-rayon-composants-2); background: var(--palette-fond-succes-espace-patient); }
/* Icône compte (haut droite) → fiche du psy — cohérence avec le funnel (conversation.html #headUser). */
.head-user { width: 34px; height: 34px; flex: 0 0 34px; border-radius: var(--palette-rayon-composants-2); background: var(--surface-2); color: var(--muted); display: inline-flex; align-items: center; justify-content: center; text-decoration: none; transition: background .15s, color .15s; }
.head-user:hover { background: var(--accent-softer, var(--palette-surface-action-composants-2)); color: var(--accent, var(--palette-texte-action-composants)); }
.head-user svg { width: 18px; height: 18px; }
/* Bilan en cours (classe posée par le JS au montage du widget) → le composer se
   REPLIE pour donner TOUTE la hauteur du fil au bilan (.thread est flex:1, il
   récupère automatiquement l'espace du composer). Comme dans conversation.html
   (funnel lead), un bouton flottant « ⌃ Messagerie » (.ep-foot-peek) permet de
   le remonter/replier ponctuellement (toggle body.foot-open) sans quitter le
   bilan. Tout est retiré à la fin du bilan. */
body.bilan-active .composer-wrap {
  max-height: 0; padding-top: 0; padding-bottom: 0; border-top: 0;
  opacity: 0; overflow: hidden; pointer-events: none;
  transition: max-height .3s ease, padding .3s ease, opacity .25s ease;
}
body.bilan-active.foot-open .composer-wrap {
  max-height: 420px; padding-top: var(--palette-espacement-cabinet-conversation-10); padding-bottom: var(--palette-espacement-composants-3);
  border-top: 1px solid var(--line);
  opacity: 1; overflow: visible; pointer-events: auto;
}
.ep-foot-peek {
  /* En bas à DROITE — ne recouvre pas les boutons du widget bilan, centrés en bas. */
  position: fixed; right: 18px; bottom: 14px;
  display: none; align-items: center; gap: var(--palette-espacement-components); z-index: 40;
  background: var(--surface); color: var(--accent);
  border: 1px solid var(--line); border-radius: var(--palette-rayon-components-2);
  padding: var(--palette-espacement-cabinet-conversation-4) var(--palette-espacement-conversation-2); cursor: pointer;
  font-family: var(--palette-police-interface-conversation); font-size: var(--palette-typographie-cabinet-conversation-3); font-weight: 600;
  box-shadow: 0 8px 22px -8px var(--palette-ombre-composants-3);
}
.ep-foot-peek:hover { border-color: var(--adaptation-bordure-action-accent, var(--accent)); }
body.bilan-active .ep-foot-peek { display: inline-flex; }
.ep-foot-peek .cfp-chev { display: inline-block; transition: transform .25s ease; font-size: var(--palette-typographie-composants-3); line-height: 1; }
body.foot-open .ep-foot-peek .cfp-chev { transform: rotate(180deg); }
.icon-btn { width: 36px; height: 36px; border-radius: var(--palette-rayon-cabinet-conversation-4); border: 1px solid var(--line); background: var(--surface); color: var(--ink-2); display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
.icon-btn:hover { background: var(--surface-2); }

/* Zone messages */
.thread {
  flex: 1;
  overflow-y: auto;
  padding: var(--palette-espacement-components-5) var(--palette-espacement-invitation-4) var(--palette-espacement-cabinet-conversation);
  background: radial-gradient(60% 40% at 50% 0%, var(--surface-2) 0%, transparent 60%), var(--surface);
  display: flex;
  flex-direction: column;
  gap: var(--palette-espacement-cabinet-conversation-5);
}
.day-divider { display: flex; align-items: center; gap: var(--palette-espacement-cabinet-conversation-5); color: var(--muted); font-size: var(--palette-typographie-cabinet-conversation-4); margin: var(--palette-espacement-cabinet-conversation-6) 0 var(--palette-espacement-cabinet-2); }
.day-divider::before, .day-divider::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.msg-row { display: flex; gap: var(--palette-espacement-cabinet-conversation-6); max-width: 78%; }
.msg-row.them { align-self: flex-start; }
.msg-row.me { align-self: flex-end; flex-direction: row-reverse; }
.msg-av { width: 28px; height: 28px; border-radius: var(--palette-rayon-composants-2); background: linear-gradient(135deg, var(--accent-soft), var(--adaptation-fond-action-accent, var(--accent))); flex: 0 0 28px; align-self: flex-end; margin-bottom: var(--palette-espacement-cabinet-2); }
.msg-row.me .msg-av { background: linear-gradient(135deg, var(--palette-surface-secondaire-espace-patient-2), var(--palette-surface-secondaire-espace-patient)); display: none; }
/* white-space: pre-line → préserve les \n du texte (chaque retour à la ligne
   devient un break visuel) tout en collapsant les espaces multiples normaux.
   Sans ça, les messages stockés en BDD avec des sauts de paragraphe (cas des
   welcome messages des psy : "Bonjour,\n\nJe suis ravie...") s'affichaient
   en un seul bloc sans interlignes. */
.bubble { padding: var(--palette-espacement-cabinet-conversation-7) var(--palette-espacement-cabinet-conversation-2); border-radius: var(--palette-rayon-cabinet-conversation); font-size: var(--palette-typographie-cabinet-conversation-5); line-height: 1.55; letter-spacing: var(--palette-typographie-conversation-2); white-space: pre-line; word-wrap: break-word; }
/* Toute image dans une bulle (contenu psy pouvant contenir un <img>, ex. photo
   embarquée) est BORNÉE à la largeur de la bulle. Sans ce garde-fou, une image
   au format intrinsèque s'affichait en GÉANT (régression 03/07 : la photo de la
   psy occupait tout l'écran). object-fit + radius pour un rendu propre. */
.bubble img { max-width: 100%; height: auto; border-radius: var(--palette-rayon-cabinet-conversation-3); display: block; margin: var(--palette-espacement-cabinet-2) 0; }
/* Bulles CONTRASTÉES (Vague 1 UX, 03/07 — retour « trop fondu ») : la bulle
   psy se détache par un bord + double ombre, la bulle patient prend le
   dégradé accent avec ombre colorée. */
.msg-row.them .bubble { background: var(--palette-surface-cabinet-conversation); color: var(--ink); border: 1px solid var(--line-strong, var(--palette-bordure-composants)); border-bottom-left-radius: var(--palette-rayon-conversation); box-shadow: 0 1px 2px var(--palette-ombre-conversation-2), 0 8px 20px -14px var(--palette-ombre-conversation); }
.msg-row.me .bubble { background: linear-gradient(135deg, var(--palette-fond-action-composants), var(--palette-fond-action-conversation)); color: var(--palette-texte-inverse-components); border-bottom-right-radius: var(--palette-rayon-conversation); box-shadow: 0 8px 20px -12px var(--palette-ombre-conversation-3); }

/* Liens dans les bulles — style Wikipedia : souligné + petite icône
   "lien externe" en ::after pour rendre le lien évident. Couleur
   contrastée selon la variante (bulle blanche ou bulle accent). */
.bubble a {
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
  word-break: break-all;     /* évite qu'une URL longue casse la mise en page */
}
.bubble a:hover { text-decoration-thickness: 2px; }
.bubble a[target="_blank"]::after {
  content: "";
  display: inline-block;
  width: 0.85em;
  height: 0.85em;
  margin-left: var(--palette-espacement-components-4);
  vertical-align: -0.05em;
  background-color: currentColor;
  /* SVG "external link" inline embedded via mask — couleur héritée du
     texte du lien donc cohérent quel que soit le thème de la bulle. */
  -webkit-mask: var(--palette-masque-4) no-repeat center / contain;
          mask: var(--palette-masque-4) no-repeat center / contain;
  opacity: .85;
}

/* Bulle "them" (psy, fond blanc) : liens en accent violet pour rester lisibles. */
.msg-row.them .bubble a { color: var(--accent); }
.msg-row.them .bubble a:hover { color: var(--accent-hover, var(--accent)); }

/* Bulle "me" (patient, fond violet) : liens en blanc avec léger contraste. */
.msg-row.me .bubble a { color: var(--palette-texte-inverse-components); opacity: .98; }
.msg-row.me .bubble a:hover { opacity: 1; }
.msg-meta { font-size: var(--palette-typographie-composants-3); color: var(--muted); padding: var(--palette-espacement-cabinet-2) var(--palette-espacement-cabinet-conversation-11) 0; }
.msg-meta .msg-edited { font-style: italic; }
.msg-row.me .msg-meta { text-align: right; }

/* Composer (zone d'envoi) */
.composer-wrap {
  position: relative;
  border-top: 1px solid var(--line);
  padding: var(--palette-espacement-cabinet-conversation-10) var(--palette-espacement-invitation-4) var(--palette-espacement-composants-3);
  background: linear-gradient(180deg, var(--bg) 0%, var(--surface) 50px);
}
.composer-preamble { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--palette-espacement-cabinet-conversation-5); margin-bottom: var(--palette-espacement-cabinet-conversation-6); padding: 0 var(--palette-espacement-composants-5); }
.composer-preamble .label { font-size: var(--palette-typographie-composants-14); letter-spacing: var(--palette-typographie-conversation); color: var(--ink); font-weight: 600; }
.composer-preamble .label .to { color: var(--accent); margin-left: var(--palette-espacement-cabinet-2); }
.composer-preamble .draft-status { font-size: var(--palette-typographie-components-4); color: var(--muted-2); display: inline-flex; align-items: center; gap: var(--palette-espacement-cabinet-conversation-3); }
.composer-preamble .draft-status .dot { width: 6px; height: 6px; border-radius: var(--palette-rayon-composants-2); background: var(--accent-soft); display: inline-block; }
/* Brouillon persistant (16/10/2026) : discret, sous le composer, visible aussi sur mobile. */
.draft-saved { font-size: var(--palette-typographie-components-4); color: var(--muted-2); margin-top: var(--palette-espacement-cabinet-conversation-3); padding: 0 var(--palette-espacement-cabinet-2); text-align: right; }
.draft-saved[hidden] { display: none; }
.composer {
  border: 1px solid var(--line-strong);
  border-radius: var(--palette-rayon-cabinet-conversation);
  background: var(--surface);
  padding: var(--palette-espacement-cabinet-conversation-6) var(--palette-espacement-cabinet-conversation-10) var(--palette-espacement-cabinet-conversation-5);
  transition: border-color .2s, box-shadow .2s;
}
/* Focus ring discret : juste une bordure accent, sans halo géant (l'ancien
   box-shadow 5px donnait l'impression d'un 2e encadré autour du textarea). */
.composer:focus-within { border-color: var(--adaptation-bordure-action-accent, var(--accent)); }
.composer-wrap--locked .composer .composer-input { min-height: 48px; max-height: 48px; }

/* Mini toolbar de mise en forme — discret, n'apparaît qu'au-dessus du
   contenteditable. Boutons texte (B / I) + un bouton lien qui demande
   l'URL au clic via prompt() — léger, pas besoin de modal custom. */
.composer-toolbar { display: flex; align-items: center; gap: var(--palette-espacement-composants-5); padding: var(--palette-espacement-composants-5) 0 var(--palette-espacement-cabinet-conversation-3); }
.composer-toolbar button {
  width: 28px; height: 28px; border: 0; background: transparent; border-radius: var(--palette-rayon-conversation);
  color: var(--muted); cursor: pointer; font-size: var(--palette-typographie-components); line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
}
.composer-toolbar button:hover { background: var(--surface-2); color: var(--ink-2); }
.composer-toolbar button[data-cmd="bold"]   { font-weight: 800; }
.composer-toolbar button[data-cmd="italic"] { font-style: italic; font-family: var(--palette-police-editoriale-espace-patient); }
.composer-toolbar .sep { width: 1px; height: 16px; background: var(--line); margin: 0 var(--palette-espacement-cabinet-2); }

/* contenteditable : on lui colle exactement le look-feel du textarea
   précédent. Le placeholder est géré via [data-placeholder] + :empty:before. */
.composer-input {
  width: 100%;
  border: 0;
  outline: 0;
  background: transparent;
  font-size: var(--palette-typographie-composants-10);
  line-height: 1.7;
  padding: var(--palette-espacement-composants-5) 0;
  min-height: 92px;
  max-height: 320px;
  overflow-y: auto;
  color: var(--ink);
  font-family: inherit;
  word-break: break-word;
  white-space: pre-wrap;
  transition: min-height .18s ease, max-height .18s ease;
}
/* 3 tailles via les boutons réduire/agrandir de la toolbar (cf. démarrer.html) :
   compact / normal / expanded. */
.composer.is-compact .composer-input {
  /* Hauteur FIXE en mode compact : ne grandit plus avec le contenu,
     le texte scrolle dedans (cf. overflow-y: auto du base). */
  height: 48px;
  min-height: 48px;
  max-height: 48px;
}
.composer.is-expanded .composer-input {
  min-height: 400px;
  max-height: 65vh;
}
.composer-input:empty:before {
  content: attr(data-placeholder);
  color: var(--muted-2);
  pointer-events: none;
  /* Le navigateur place le caret avant le ::before via display block (sinon
     le pseudo-element pousse le caret à droite du placeholder à la frappe). */
  display: block;
}
.composer-input a { color: var(--accent); text-decoration: underline; }
.composer-input b, .composer-input strong { font-weight: 700; }
.composer-input i, .composer-input em { font-style: italic; }

.composer-bar { display: flex; align-items: center; gap: var(--palette-espacement-cabinet-2); padding: var(--palette-espacement-cabinet-conversation-6) 0 var(--palette-espacement-composants-5); margin-top: var(--palette-espacement-cabinet-conversation-11); border-top: 1px dashed var(--line); }
.composer-bar .spacer { flex: 1; }
.composer-bar .count { font-size: var(--palette-typographie-components-4); color: var(--muted-2); margin-right: var(--palette-espacement-cabinet-conversation-3); }
.send-btn { border: 0; background: var(--adaptation-fond-action-accent, var(--accent)); color: var(--palette-texte-inverse-components); display: inline-flex; align-items: center; gap: var(--palette-espacement-cabinet-conversation-11); padding: var(--palette-espacement-cabinet-conversation-4) var(--palette-espacement-cabinet-conversation-2); border-radius: var(--palette-rayon-components); font-size: var(--palette-typographie-components); font-weight: 600; cursor: pointer; transition: background .15s, transform .15s; }
.send-btn:hover { background: var(--adaptation-fond-action-accent-hover, var(--accent-hover)); transform: translateY(-1px); }
.send-btn:disabled { background: var(--surface-2); color: var(--muted-2); cursor: not-allowed; transform: none; border: 1px solid var(--line); }
.tool-btn { width: 32px; height: 32px; border-radius: var(--palette-rayon-cabinet-2); border: 0; background: transparent; color: var(--muted); display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
.tool-btn:hover { background: var(--surface-2); color: var(--ink-2); }
.hint { font-size: var(--palette-typographie-components-4); color: var(--muted-2); margin-top: var(--palette-espacement-cabinet-conversation-6); padding-left: var(--palette-espacement-cabinet-2); display: flex; align-items: center; gap: var(--palette-espacement-cabinet-conversation-11); flex-wrap: wrap; }
.hint .sep { width: 3px; height: 3px; border-radius: var(--palette-rayon-composants-2); background: var(--adaptation-surface-secondaire-muted-2, var(--muted-2)); opacity: .5; }
.hint kbd { font-family: inherit; font-size: var(--palette-typographie-composants-17); border: 1px solid var(--line); border-radius: var(--palette-rayon-invitation-4); padding: var(--palette-espacement-conversation) var(--palette-espacement-cabinet-conversation-9); background: var(--surface-2); color: var(--muted); }

/* ── Aside droite + carte RDV + fiche psy : CSS RETIRÉ ──────────────────────
   L'aside de droite (340px) a été supprimée pour la continuité avec le funnel
   conversation.html (conversation centrée 1 colonne). Les styles .aside /
   .appt-card / .therapist + @keyframes pulse, qui n'avaient plus aucun DOM
   correspondant, ont été retirés. Conservés ci-dessous : voicePulse +
   .tool-btn.is-recording (enregistrement vocal, toujours utilisés). */
@keyframes voicePulse { 0%,100% { opacity: 1; transform: scale(1); } 50% { opacity: .55; transform: scale(.9); } }
.tool-btn.is-recording { background: var(--palette-fond-danger-espace-patient); color: var(--palette-texte-danger-consultation); }

/* Waveform animée pendant l'enregistrement vocal (style WhatsApp).
   15 barres verticales qui s'étirent/contractent en boucle avec des
   phases décalées via animation-delay → effet "égaliseur audio" simulé.
   Pas de Web Audio API (analyse du vrai signal) → trop de friction pour
   peu de valeur, l'effet visuel suffit. */
.voice-wave { display: inline-flex; align-items: center; gap: var(--palette-espacement-composants-5); height: 22px; padding: 0 var(--palette-espacement-composants-5); }
.voice-wave span { display: block; width: 2.5px; height: 100%; background: var(--palette-fond-danger-espace-patient-2); border-radius: var(--palette-rayon-consultation); transform: scaleY(.25); transform-origin: center; animation: voiceBar 1s ease-in-out infinite; }
.voice-wave span:nth-child(1)  { animation-delay: 0.00s; }
.voice-wave span:nth-child(2)  { animation-delay: 0.07s; }
.voice-wave span:nth-child(3)  { animation-delay: 0.14s; }
.voice-wave span:nth-child(4)  { animation-delay: 0.21s; }
.voice-wave span:nth-child(5)  { animation-delay: 0.28s; }
.voice-wave span:nth-child(6)  { animation-delay: 0.35s; }
.voice-wave span:nth-child(7)  { animation-delay: 0.42s; }
.voice-wave span:nth-child(8)  { animation-delay: 0.49s; }
.voice-wave span:nth-child(9)  { animation-delay: 0.56s; }
.voice-wave span:nth-child(10) { animation-delay: 0.63s; }
.voice-wave span:nth-child(11) { animation-delay: 0.55s; }
.voice-wave span:nth-child(12) { animation-delay: 0.42s; }
.voice-wave span:nth-child(13) { animation-delay: 0.30s; }
.voice-wave span:nth-child(14) { animation-delay: 0.18s; }
.voice-wave span:nth-child(15) { animation-delay: 0.06s; }
/* scaleY (compositor) au lieu de height (layout) — Vague 1 UX 03/07 :
   l'égaliseur n'impose plus un layout par frame pendant l'enregistrement. */
@keyframes voiceBar {
  0%, 100% { transform: scaleY(.25); }
  20%      { transform: scaleY(.70); }
  40%      { transform: scaleY(1); }
  60%      { transform: scaleY(.55); }
  80%      { transform: scaleY(.85); }
}
/* Bandeau d'enregistrement vocal — uniquement visible quand on retire l'attribut [hidden].
   Sans `:not([hidden])`, le `display:flex` inline du HTML overriderait l'attribut hidden
   du navigateur (qui ne pose que `display:none` via la UA stylesheet → priorité basse). */
#voiceRecording:not([hidden]) { display: flex; align-items: center; gap: var(--palette-espacement-conversation-16); padding: var(--palette-espacement-conversation-13) var(--palette-espacement-conversation-12); background: var(--palette-fond-danger-espace-patient-3); border: 1px solid var(--palette-bordure-danger-espace-patient); border-radius: var(--palette-rayon-cabinet-conversation-4); margin-bottom: var(--palette-espacement-espace-patient); font-size: var(--palette-typographie-conversation-13); color: var(--palette-texte-danger-consultation); }
/* Mobile : le bandeau débordait (bouton « Arrêter » poussé hors écran). On masque
   le label « Enregistrement… » (redondant avec la waveform animée + le timer), on
   resserre l'espacement et on laisse la waveform rétrécir → Annuler + Arrêter
   restent toujours visibles dans l'écran. */
@media (max-width: 640px) {
  #voiceRecording:not([hidden]) { gap: var(--palette-espacement-espace-patient); padding: var(--palette-espacement-conversation-17) var(--palette-espacement-conversation-16); }
  #voiceRecording .voice-rec-label { display: none; }
  #voiceRecording .voice-wave { flex: 0 1 auto; min-width: 0; overflow: hidden; }
}
/* Lien « Transcrire » en TEXTE seul (ni icône ni pastille) + transcription affichée
   sous une simple barre de séparation. Couleur héritée de la bulle (.me/.them). */
/* Bouton « Transcrire » : pastille douce avec icône IA (sparkle), SANS soulignement.
   Couleur héritée de la bulle (.me = blanc translucide / .them = accent violet). */
.vmsg-tr-btn { margin-top: var(--palette-espacement-cabinet-conversation-4); display: inline-flex; align-items: center; gap: var(--palette-espacement-cabinet-conversation-9); font-size: var(--palette-typographie-cabinet-conversation-4); font-weight: 600; font-family: inherit; color: inherit; background: var(--palette-surface-espace-patient); border: 0; border-radius: var(--palette-rayon-components-2); padding: var(--palette-espacement-cabinet-conversation-9) var(--palette-espacement-components-2); cursor: pointer; transition: background .15s ease, transform .08s ease; }
.vmsg-tr-btn svg { width: 13px; height: 13px; }
.vmsg-tr-btn:hover { background: var(--palette-surface-espace-patient-2); }
.vmsg-tr-btn:active { transform: scale(.96); }
.vmsg-tr-btn:disabled { opacity: .65; cursor: default; }
/* ── Vague 1 UX (03/07) : arrivée des messages + envoi optimiste + chip ── */
@keyframes msgIn { from { opacity: 0; transform: translateY(8px) scale(.98); } to { opacity: 1; transform: none; } }
.msg-row.msg-new { animation: msgIn .38s cubic-bezier(.22, 1, .36, 1) both; }
@media (prefers-reduced-motion: reduce) { .msg-row.msg-new { animation: none; } }
/* Envoi OPTIMISTE : la bulle apparaît avant la réponse API, légèrement voilée. */
.msg-row.is-sending .bubble { opacity: .65; }
/* Chip « ↓ nouveau message » : verre flottant au-dessus du composer quand un
   message arrive alors qu'on lit plus haut dans le fil. */
.main { position: relative; }
.new-msg-chip {
  position: absolute; left: 50%; transform: translateX(-50%) translateY(6px);
  bottom: calc(112px + max(env(safe-area-inset-bottom, 0px), var(--safe-bottom-fix, var(--native-safe-bottom, 0px))));
  z-index: 30; display: flex; align-items: center; gap: var(--palette-espacement-cabinet-conversation-3);
  padding: var(--palette-espacement-cabinet-conversation-11) var(--palette-espacement-cabinet-conversation-5); border: 1px solid var(--palette-bordure-action-espace-patient); border-radius: var(--palette-rayon-components-2);
  background: var(--palette-surface-layout-3);
  -webkit-backdrop-filter: blur(14px) saturate(1.8); backdrop-filter: blur(14px) saturate(1.8);
  box-shadow: 0 10px 26px -10px var(--palette-ombre-espace-patient);
  color: var(--accent); font-family: inherit; font-size: var(--palette-typographie-components-2); font-weight: 700; cursor: pointer;
  opacity: 0; pointer-events: none; transition: opacity .2s ease, transform .2s ease;
}
.new-msg-chip.is-visible { opacity: 1; pointer-events: auto; transform: translateX(-50%); }
/* ✈️ « décolle » à l'envoi (classe posée 400 ms par le handler d'envoi). */
@keyframes sendFly { 30% { transform: translate(3px, -5px) rotate(-14deg) scale(1.06); } }
.send-btn.did-send { animation: sendFly .38s cubic-bezier(.22, 1, .36, 1); }

.msg-row.them .vmsg-tr-btn { background: var(--accent-softer, var(--palette-surface-action-composants-2)); color: var(--accent, var(--palette-texte-action-composants)); }
.msg-row.them .vmsg-tr-btn:hover { background: var(--accent-soft, var(--palette-surface-action-composants-3)); }
.vmsg-tr { margin-top: var(--palette-espacement-cabinet-conversation-4); padding-top: var(--palette-espacement-components); font-size: var(--palette-typographie-cabinet-conversation-3); line-height: 1.45; opacity: .96; }
.vmsg-tr-txt { white-space: pre-wrap; word-break: break-word; }
/* 24/09/2026 : erreur de transcription lisible sous le bouton (plus d'alert()). */
.vmsg-tr-err { margin-top: var(--palette-espacement-cabinet-conversation-3); font-size: var(--palette-typographie-cabinet-conversation-4); line-height: 1.4; opacity: .9; }
.msg-row.me   .vmsg-tr { border-top: 1px solid var(--palette-bordure-espace-patient); }
.msg-row.them .vmsg-tr { border-top: 1px solid var(--line); }
/* La bulle d'un vocal reste à la dimension du player (≤ ~340px) même avec la
   transcription dessous → plus d'étalement pleine largeur. */
.bubble-audio { max-width: 340px; }

/* ── Pièces jointes (image vignette / carte PDF) ─────────────────────────── */
.bubble-file { padding: var(--palette-espacement-cabinet-conversation-3); max-width: 300px; }
.msg-file-img { display: block; border-radius: var(--palette-rayon-cabinet-conversation-3); overflow: hidden; line-height: 0; border: 0; padding: 0; background: transparent; cursor: zoom-in; width: 100%; }
/* Lightbox IN-APP : la preview d'une image reste DANS l'app (jamais ouvrir
   l'URL signée dans Safari externe — elle atterrirait dans l'historique). */
.img-lightbox { position: fixed; inset: 0; z-index: 9999; background: var(--palette-fond-inverse-espace-patient);
  display: flex; align-items: center; justify-content: center; padding: var(--palette-espacement-cabinet-conversation-2);
  padding-top: calc(var(--palette-espacement-cabinet-conversation-2) + env(safe-area-inset-top)); padding-bottom: calc(var(--palette-espacement-cabinet-conversation-2) + env(safe-area-inset-bottom));
  -webkit-tap-highlight-color: transparent; }
.img-lightbox img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: var(--palette-rayon-cabinet-2); }
.img-lightbox .ilb-close { position: absolute; top: calc(10px + env(safe-area-inset-top)); right: 14px;
  width: 40px; height: 40px; border-radius: var(--palette-rayon-composants-2); border: 0; background: var(--palette-surface-espace-patient-3);
  color: var(--palette-texte-inverse-components); font-size: var(--palette-typographie-cabinet-conversation); line-height: var(--palette-typographie-espace-patient); cursor: pointer; }
.msg-file-img img { display: block; width: 100%; max-height: 280px; object-fit: cover; border-radius: var(--palette-rayon-cabinet-conversation-3); }
.msg-file-card { display: flex; align-items: center; gap: var(--palette-espacement-cabinet-conversation-6); padding: var(--palette-espacement-cabinet-conversation-6) var(--palette-espacement-cabinet-conversation-7); border-radius: var(--palette-rayon-cabinet-conversation-3); text-decoration: none; }
.msg-row.them .msg-file-card { background: var(--surface-2, var(--palette-surface-action-composants)); color: var(--ink); }
.msg-row.me .msg-file-card { background: var(--palette-surface-espace-patient-4); color: var(--palette-texte-inverse-components); }
.msg-file-card .mfc-ic { flex: 0 0 auto; display: inline-flex; }
.msg-file-card .mfc-ic svg { width: 18px; height: 18px; }
.msg-file-card .mfc-meta { min-width: 0; display: flex; flex-direction: column; }
.msg-file-card .mfc-name { font-size: var(--palette-typographie-cabinet-conversation-2); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-file-card .mfc-size { font-size: var(--palette-typographie-cabinet-conversation-4); opacity: .7; margin-top: var(--palette-espacement-conversation); }

/* ============================================================
   Lecteur vocal custom dans les bulles de messagerie
   ============================================================
   Remplace l'audio controls natif (UI laide, taille variable selon les
   navigateurs) par un player horizontal façon WhatsApp/Messenger :
   bouton play/pause rond + barre de progression cliquable + durée.
   Couleurs inversées selon que le message vient de moi (.me, fond accent
   blanc/translucide) ou du psy (.them, accent violet sur fond clair). */
.vmsg { display: flex; align-items: center; gap: var(--palette-espacement-cabinet-conversation-6); min-width: 240px; max-width: 320px; padding: var(--palette-espacement-cabinet-2) 0; }
/* Cercle STRICT (iOS écrasait le bouton : appearance native + padding UA) */
.vmsg-play { width: 36px; height: 36px; min-width: 36px; min-height: 36px; flex: 0 0 36px; aspect-ratio: 1 / 1; padding: 0; box-sizing: border-box; -webkit-appearance: none; appearance: none; border-radius: var(--palette-rayon-composants-2); border: 0; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; transition: transform .12s; }
.vmsg-play:hover { transform: scale(1.06); }
.vmsg-play:active { transform: scale(0.95); }
.vmsg-play svg { width: 14px; height: 14px; }
.vmsg-time { font-size: var(--palette-typographie-components-4); font-variant-numeric: tabular-nums; opacity: .8; flex-shrink: 0; min-width: 36px; text-align: right; }

/* Waveform statique du lecteur audio (style WhatsApp).
   N barres verticales avec hauteurs préfixées (pattern fixe pour
   tous les vocaux — éviter une vraie analyse Web Audio API).
   Les barres "passées" prennent la couleur active, les barres
   "à venir" restent en couleur muted. Le tout via classe .is-active
   posée dynamiquement par mountAudioPlayer au timeupdate. */
/* touch-action pan-y : un glissé HORIZONTAL sur la waveform déplace la lecture (doigt), un glissé vertical fait défiler le fil (14/09/2026). */
.vmsg-wave { flex: 1; display: flex; align-items: center; gap: var(--palette-espacement-composants-5); cursor: pointer; height: 26px; min-width: 120px; touch-action: pan-y; }
.vmsg-wave .b { display: block; width: 2.5px; border-radius: var(--palette-rayon-consultation); transition: background-color 0.1s linear; }

/* Hauteurs fixes pour les 30 barres (pseudo-waveform reproductible) */
.vmsg-wave .b:nth-child(1)  { height: 30%; }
.vmsg-wave .b:nth-child(2)  { height: 55%; }
.vmsg-wave .b:nth-child(3)  { height: 75%; }
.vmsg-wave .b:nth-child(4)  { height: 50%; }
.vmsg-wave .b:nth-child(5)  { height: 90%; }
.vmsg-wave .b:nth-child(6)  { height: 70%; }
.vmsg-wave .b:nth-child(7)  { height: 45%; }
.vmsg-wave .b:nth-child(8)  { height: 65%; }
.vmsg-wave .b:nth-child(9)  { height: 85%; }
.vmsg-wave .b:nth-child(10) { height: 55%; }
.vmsg-wave .b:nth-child(11) { height: 100%; }
.vmsg-wave .b:nth-child(12) { height: 60%; }
.vmsg-wave .b:nth-child(13) { height: 80%; }
.vmsg-wave .b:nth-child(14) { height: 40%; }
.vmsg-wave .b:nth-child(15) { height: 70%; }
.vmsg-wave .b:nth-child(16) { height: 95%; }
.vmsg-wave .b:nth-child(17) { height: 50%; }
.vmsg-wave .b:nth-child(18) { height: 75%; }
.vmsg-wave .b:nth-child(19) { height: 65%; }
.vmsg-wave .b:nth-child(20) { height: 85%; }
.vmsg-wave .b:nth-child(21) { height: 45%; }
.vmsg-wave .b:nth-child(22) { height: 70%; }
.vmsg-wave .b:nth-child(23) { height: 90%; }
.vmsg-wave .b:nth-child(24) { height: 55%; }
.vmsg-wave .b:nth-child(25) { height: 80%; }
.vmsg-wave .b:nth-child(26) { height: 60%; }
.vmsg-wave .b:nth-child(27) { height: 75%; }
.vmsg-wave .b:nth-child(28) { height: 40%; }
.vmsg-wave .b:nth-child(29) { height: 65%; }
.vmsg-wave .b:nth-child(30) { height: 50%; }

/* Variante "them" : bulle blanche/grise, accent violet pour le player. */
.msg-row.them .vmsg-play { background: var(--adaptation-fond-action-accent, var(--accent, var(--palette-fond-action-composants))); color: var(--palette-texte-inverse-components); }
.msg-row.them .vmsg-wave .b { background: var(--palette-fond-action-espace-patient); }
.msg-row.them .vmsg-wave .b.is-active { background: var(--adaptation-fond-action-accent, var(--accent, var(--palette-fond-action-composants))); }
.msg-row.them .vmsg-time { color: var(--muted, var(--palette-texte-secondaire-composants-3)); }

/* Variante "me" : bulle violette de l'utilisateur, accent blanc inversé. */
.msg-row.me .vmsg-play { background: var(--palette-surface-espace-patient-5); color: var(--accent, var(--palette-texte-action-composants)); }
.msg-row.me .vmsg-wave .b { background: var(--palette-surface-espace-patient-6); }
.msg-row.me .vmsg-wave .b.is-active { background: var(--palette-surface-cabinet-conversation); }
.msg-row.me .vmsg-time { color: var(--palette-texte-inverse-espace-patient); }

.vmsg-play.is-playing { animation: vmsgPlayPulse 2s ease-in-out infinite; }
@keyframes vmsgPlayPulse { 0%,100% { box-shadow: 0 0 0 0 currentColor; opacity: 1; } 50% { box-shadow: 0 0 0 4px transparent; opacity: .85; } }
/* (CSS .appt-card .when/.date/.actions/.btn-primary-card + .therapist .* RETIRÉ
   avec l'aside droite — voir note plus haut. Continuité avec le funnel.) */

/* ════════════════════════════════════════════════════════════════════════
   RESTYLE DESKTOP DE LA COLONNE CONVERSATION — alignée sur conversation.html
   ------------------------------------------------------------------------
   Objectif : donner à la VRAIE messagerie (colonne du milieu) le LOOK du
   funnel « conversation.html » UNIQUEMENT EN DESKTOP, sans toucher au layout
   3 colonnes (sidebar | conversation | aside RDV/psy) ni à AUCUNE règle mobile.

   ⚠️ SCOPE STRICT : tout ce bloc est sous `@media (min-width: 769px)`. En
   dessous de 769px (mobile single-col + restyle chat WhatsApp @640px), RIEN
   ici ne s'applique → le rendu mobile reste byte-identique à avant.

   Choix du breakpoint : le restyle chat mobile vit dans `@media
   (max-width: 640px)` et le passage en 1 colonne dans `@media (max-width:
   768px)`. `min-width: 769px` couvre donc TOUT le desktop/tablette où les
   colonnes sont affichées, sans jamais recouvrir une règle mobile.

   On ne change QUE le visuel (couleurs/fonds/rayons/ombres) de la colonne
   conversation : en-tête, bulles, composer, fond lavande. Zéro changement de
   structure, de logique ou de fonctionnalité (audio/PJ/transcription/réactions
   /RDV restent intacts). L'aside 340px et la sidebar gauche sont conservés.
   ════════════════════════════════════════════════════════════════════════ */
@media (min-width: 769px) {

  /* ── FOND DE LA COLONNE CONVERSATION → lavande (comme .conv-main du funnel).
     Le `.main` était blanc (var(--surface)) ; on le passe en var(--bg). */
  .main { background: var(--bg); }

  /* ── EN-TÊTE (.thread-head) → look .conv-head du funnel ───────────────── */
  .thread-head {
    padding: var(--palette-espacement-cabinet-conversation-2) var(--palette-espacement-components-5);             /* aligné sur .conv-head (16px 28px) */
    background: var(--surface);     /* bandeau en-tête blanc sur fond lavande */
  }
  /* Avatar : carré arrondi 44px (au lieu du rond 46px), comme .conv-head .av. */
  .thread-head .av {
    width: 44px;
    height: 44px;
    flex: 0 0 44px;
    border-radius: var(--palette-rayon-cabinet-conversation-3);
    border: 0;                      /* retire la bordure blanche + ring du look rond */
    box-shadow: none;
  }
  /* L'<img> avatar a un border-radius:50% inline (posé par le JS) : on le
     re-arrondit en carré pour épouser le conteneur. Pas de JS touché. */
  .thread-head .av img { border-radius: var(--palette-rayon-cabinet-conversation-3) !important; }
  /* Point « en ligne » : vert sémantique + liseré surface (comme le funnel). */
  .thread-head .av::after {
    background: var(--adaptation-fond-succes-success, var(--success));
    border: 2px solid var(--adaptation-bordure-surface, var(--surface));
  }
  /* Nom du psy : Newsreader, plus grand ; le nom de famille (déjà wrappé en
     <em> par le JS) en italique accent — exactement comme .conv-head-name. */
  .thread-head h1 {
    font-family: var(--palette-police-editoriale-conversation);
    font-weight: 500;
    font-size: var(--palette-typographie-components-6);
    letter-spacing: var(--palette-typographie-conversation);
    line-height: 1.1;
    color: var(--ink);
  }
  /* Pas de margin-left ici : l'espace avant le nom de famille vient du caractère
     espace que le JS insère (« Prénom <em>Nom</em> »), exactement comme
     .conv-head-name em du funnel (qui n'a pas de margin non plus). Un margin
     en plus créerait un écart d'espacement visible vs le funnel. */
  .thread-head h1 em {
    font-family: var(--palette-police-editoriale-conversation);
    font-style: italic;
    font-weight: 500;
    color: var(--accent-ink);
  }
  .thread-head .sub { font-size: var(--palette-typographie-cabinet-conversation-3); color: var(--muted); margin-top: var(--palette-espacement-conversation); }
  /* « Répond habituellement sous … » → vert (comme .conv-head-resp), sans la
     pilule grise encadrée : juste un point vert + texte vert. */
  .thread-head .head-pill {
    border: 0;
    background: transparent;
    padding: 0;
    font-size: var(--palette-typographie-components-2);
    color: var(--success);
    gap: var(--palette-espacement-cabinet-conversation-3);
  }
  .thread-head .head-pill .dot-sm {
    width: 7px;
    height: 7px;
    background: var(--adaptation-fond-succes-success, var(--success));
  }

  /* ── FIL DE MESSAGES (.thread) → fond lavande + COLONNE CENTRÉE 760px ────
     Le funnel conversation.html scrolle sur toute la largeur (.conv-thread)
     mais CENTRE son contenu dans .conv-inner { max-width: 760px; margin: 0
     auto }. Ici, les bulles sont des enfants DIRECTS de .thread (pas de
     wrapper) → on reproduit le même résultat sans toucher au DOM/JS via un
     padding-inline « band » : le contenu reste dans une bande de 760px
     centrée (padding latéral = (100% - 760px)/2, plancher 28px comme le
     funnel) pendant que la zone de scroll, elle, occupe toute la largeur.
     Visuellement IDENTIQUE à .conv-inner. */
  .thread {
    background: var(--bg);
    padding-top: var(--palette-espacement-composants-2);
    padding-bottom: var(--palette-espacement-cabinet-conversation-2);
    padding-left: max(var(--palette-espacement-components-5), calc((var(--palette-espacement-espace-patient-2) - var(--palette-espacement-espace-patient-3)) / 2));
    padding-right: max(var(--palette-espacement-components-5), calc((var(--palette-espacement-espace-patient-2) - var(--palette-espacement-espace-patient-3)) / 2));
    gap: var(--palette-espacement-cabinet-conversation-8);
  }
  .day-divider { color: var(--muted); }

  /* ── BULLES (.msg-row / .bubble) → look funnel ────────────────────────── */
  /* Bulle psy (gauche) : blanche, bordure douce, ombre légère, coin bas-gauche
     resserré — comme .conv-row.them .conv-bubble. */
  .bubble {
    padding: var(--palette-espacement-cabinet-conversation-5) var(--palette-espacement-conversation-2);
    border-radius: var(--palette-rayon-cabinet-conversation);
    font-size: var(--palette-typographie-composants-14);
    line-height: 1.6;
  }
  .msg-row.them .bubble {
    background: var(--surface);
    color: var(--ink-2);
    border: 1px solid var(--line);
    border-bottom-left-radius: var(--palette-rayon-conversation);
    box-shadow: 0 1px 0 var(--palette-ombre-conversation-5);
  }
  /* Bulle patient (droite) : DÉGRADÉ accent #5B65DE→#8B7EE8 + coin bas-droit
     resserré — EXACTEMENT comme .conv-row.me .conv-bubble du funnel lead.
     (La def de base l.195 est déjà en dégradé ; ce bloc desktop la faisait
     retomber sur un violet PLAT var(--accent) → incohérence corrigée 07/07.
     Le box-shadow coloré de la base l.195 est conservé par cascade.) */
  .msg-row.me .bubble {
    background: linear-gradient(135deg, var(--palette-fond-action-composants), var(--palette-fond-action-conversation));
    color: var(--palette-texte-inverse-components);
    border-bottom-right-radius: var(--palette-rayon-conversation);
  }

  /* ── ALIGNEMENT EXACT SUR .conv-row DU FUNNEL ──────────────────────────
     Le funnel n'affiche AUCUN avatar à côté de la bulle : la bulle est
     collée au bord (flush) et l'avatar du psy n'apparaît QUE dans la méta
     (mini-avatar 16px sous la bulle, cf. .conv-meta .mini-av). Ici, chaque
     bulle psy avait un avatar 28px à gauche (.msg-av) → écart visuel net
     vs le funnel. On masque cet avatar latéral et on annule le gap : la
     bulle s'aligne pile comme .conv-row.them du funnel. (Le mini-avatar de
     la méta est, lui, injecté par formatMsgMeta → .msg-meta .mini-av.) */
  .msg-row .msg-av { display: none; }
  .msg-row { gap: 0; max-width: 78%; }

  /* Méta sous la bulle → look .conv-meta du funnel : 11px muted, mini-avatar
     16px rond du psy + « Prénom · quand ». */
  .msg-meta {
    font-size: var(--palette-typographie-composants-3); color: var(--muted); padding: var(--palette-espacement-cabinet-conversation-9) var(--palette-espacement-cabinet-2) 0;
    display: inline-flex; align-items: center; gap: var(--palette-espacement-components);
  }
  .msg-row.me .msg-meta { padding-right: var(--palette-espacement-cabinet-2); }
  .msg-meta .mini-av {
    width: 16px; height: 16px; border-radius: var(--palette-rayon-composants-2); overflow: hidden; flex: 0 0 16px;
    background: linear-gradient(135deg, var(--accent-soft), var(--adaptation-fond-action-accent, var(--accent)));
    color: var(--palette-texte-inverse-components); display: inline-flex; align-items: center; justify-content: center;
    font-size: var(--palette-typographie-conversation-3); font-weight: 600;
  }
  .msg-meta .mini-av img { width: 100%; height: 100%; object-fit: cover; display: block; }

  /* ── COMPOSER (.composer-wrap / .composer) → look funnel ──────────────── */
  /* La ZONE du composer passe sur fond lavande var(--bg) (pas le dégradé
     blanc précédent) — comme .conv-foot du funnel. Le contenu (carte +
     bandeaux vocaux) est CENTRÉ sur 760px comme .conv-foot-inner via le même
     padding-inline « band » que .thread → la carte composer s'aligne
     PILE sous la colonne des messages (continuité parfaite). */
  .composer-wrap {
    background: var(--bg);
    border-top: 1px solid var(--line);
    padding-top: var(--palette-espacement-cabinet-conversation-2);
    padding-bottom: var(--palette-espacement-cabinet-conversation-8);
    padding-left: max(var(--palette-espacement-components-5), calc((var(--palette-espacement-espace-patient-2) - var(--palette-espacement-espace-patient-3)) / 2));
    padding-right: max(var(--palette-espacement-components-5), calc((var(--palette-espacement-espace-patient-2) - var(--palette-espacement-espace-patient-3)) / 2));
  }
  /* La CARTE textarea reste blanche, bordée, rayon 16px (comme .cmp-box). */
  .composer {
    background: var(--surface);
    border: 1.5px solid var(--line);
    border-radius: var(--palette-rayon-cabinet);
  }
  .composer:focus-within { border-color: var(--adaptation-bordure-action-accent, var(--accent)); }

  /* ── « PRENDRE RENDEZ-VOUS » dans l'en-tête ───────────────────────────
     Relocalisé depuis l'aside (carte RDV supprimée). Bouton outline accent
     posé à droite de l'en-tête, à côté de « Répond habituellement sous … ».
     Discret, n'apparaît qu'en desktop (en mobile, « Mes rendez-vous » reste
     dans le menu). */
  .thread-head .head-rdv {
    display: inline-flex; align-items: center; gap: var(--palette-espacement-components);
    padding: var(--palette-espacement-cabinet-conversation-11) var(--palette-espacement-cabinet-conversation-5); border-radius: var(--palette-rayon-cabinet-conversation-4);
    border: 1px solid var(--line-strong); background: var(--surface);
    color: var(--accent-ink); font-size: var(--palette-typographie-cabinet-conversation-3); font-weight: 600;
    text-decoration: none; white-space: nowrap; cursor: pointer;
    transition: border-color .14s, background .14s, color .14s;
  }
  .thread-head .head-rdv:hover {
    border-color: var(--adaptation-bordure-action-accent, var(--accent)); background: var(--accent-softer); color: var(--accent);
  }
  .thread-head .head-rdv svg { width: 15px; height: 15px; flex: 0 0 15px; }
}

/* (L'ancien @media (max-width: 1180px) qui masquait l'aside + repassait en
   2 colonnes est devenu inutile : le layout de base est déjà 2 colonnes
   sidebar | conversation, sans aside.) */
@media (max-width: 768px) {
  .app {
    grid-template-columns: 1fr;
  }
  .side { display: none; }
}

/* ============================================================
   Mobile messagerie — layout WhatsApp-style (audit 20/05/2026)
   - Header : ← retour + avatar psy + nom + statut (1 ligne sous nom)
   - Pas de toolbar B/I/lien — texte brut
   - Pas de preamble "Votre message pour psy..." — chat direct
   - Composer minimal : input plein + bouton envoyer rond à droite
   - Safe-area-inset-bottom pour home-indicator iPhone
   ============================================================ */

/* Flèche retour (→ home) & vidéo dans le header conv — cachés en desktop,
   visibles en mobile (refonte tab bar 02/07 : la flèche remplace le burger). */
.thread-back-btn { display: none; }
.thread-video-btn { display: none; }
/* « Prendre rendez-vous » dans l'en-tête — caché par défaut (mobile : « Mes
   rendez-vous » reste dans le menu), révélé en desktop par le bloc
   @media (min-width: 769px) ci-dessus. */
.thread-head .head-rdv { display: none; }
/* Welcome banner mobile — caché en desktop */
.thread-welcome { display: none; }
.thread-welcome[hidden] { display: none !important; }

@media (max-width: 640px) {
  /* ── Scroll lock + composer en position:fixed ────────────────────
     Stratégie robuste : on FIXE le composer en bas du viewport via
     `position: fixed`. Le .thread garde un padding-bottom pour qu'on
     puisse scroller les messages au-dessus du composer sans qu'ils
     soient cachés. Élimine les problèmes de chaîne flex/dvh sur iOS. */
  html, body {
    height: 100%;
    overflow: hidden;
    overscroll-behavior: none;
    /* (03/07, retour user) Fond LAVANDE var(--bg), comme la home — PLUS de page
       blanche. Le hook natif (AppDelegate.startBgSync) lit ce fond et peint les
       zones safe-area (encoche + barre home) en lavande → plus aucune « bande
       blanche » en haut/bas de la messagerie, et plus de résidu blanc reporté
       sur la page suivante (« Nos thérapies »). Cohérent avec le reste de l'app. */
    background: var(--bg);
  }
  .app {
    /* Hauteur = hauteur VISIBLE réelle. Valeur AUTORITAIRE posée en px par JS
       (adjustThreadPadding) à partir de window.visualViewport.height.
       ── DONNÉE MESURÉE sur appareil (Chrome iOS, ?debug=1, 16/06) ──
         innerHeight = visualViewport.height = 716px  (= visible CORRECT)
         100svh                              = 824px  (= FAUX : +108px, viewport
                                                        SANS barres → composer
                                                        poussé derrière la toolbar)
       Donc on N'utilise PAS svh/dvh comme source de vérité sur Chrome iOS : ils
       sur-évaluent. visualViewport.height est fiable sur TOUS les écrans
       (Safari/Chrome iOS, Android, desktop) et suit le clavier.
       Les valeurs CSS ci-dessous ne servent que de fallback avant l'exécution du
       JS (ou si visualViewport indisponible) : on prend la plus PETITE dispo. */
    height: -webkit-fill-available;
    height: 100dvh;
    height: 100svh;            /* fallback uniquement — écrasé en px par le JS */
    /* ⚠️ CAUSE RACINE (trouvée via ?debug=1, 16/06) : layout.css pose
       `.app { min-height: 100vh }`. Sur Chrome iOS, 100vh = 824px (viewport SANS
       barres) → ce PLANCHER remontait .app à 824 même quand le JS posait
       height:716px → composer 108px sous le visible. On annule le plancher ici
       pour que la hauteur visible réelle (posée en px par le JS) soit respectée. */
    min-height: 0;
    overflow: hidden;
    /* FIX 16/06 (composer disparu Chrome iOS) : en desktop .app est `display:grid`
       (layout.css). Sur WebKit (iOS Safari ET Chrome iOS) la ROW grid `auto` ne
       s'étire PAS jusqu'à la hauteur définie → .main retombait à la hauteur de son
       contenu. Avec le composer DANS LE FLUX, la chaîne .thread{flex:1} n'avait plus
       de parent borné → .thread grandissait au contenu → header+thread+composer
       dépassait la boîte overflow:hidden de 100dvh → le composer (dernier enfant)
       passait sous la ligne visible = INVISIBLE. On force un flex column ici pour
       que .main remplisse EXACTEMENT la hauteur visible (robuste WebKit). */
    display: flex;
    flex-direction: column;
  }
  /* Pas de transition CSS sur .app : on suit visualViewport en TEMPS RÉEL (les
     events vv se déclenchent à chaque frame de l'anim clavier iOS). Une transition
     ferait TRAÎNER le composer derrière le clavier = effet "saute". */
  /* .main remplit toute la hauteur de .app (flex:1) → la chaîne flex interne
     (.msg-state-thread flex:1 → .thread flex:1 scroll + .composer-wrap flex:0 0 auto)
     se résout : composer toujours visible en bas, messages scrollent au-dessus. */
  .main {
    flex: 1;
    min-height: 0;
  }
  .msg-state-thread {
    min-height: 0;
  }
  /* Zone messages = enfant flex qui scrolle (flex:1 + min-height:0). Le
     composer est DANS LE FLUX juste en dessous (flex:0 0 auto) → plus de
     position:fixed ni de padding de compensation. Robuste Safari ET Chrome
     iOS (16/06 : le pattern fixed+padding laissait le texte passer sous le
     composer sur Chrome iOS). */
  .thread {
    flex: 1;
    min-height: 0;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    /* Fil LAVANDE (modèle lead) — sinon retombe sur var(--surface) blanc de la
       def de base (ligne 168) → bloc blanc au milieu de la messagerie. */
    background: var(--bg);
  }
  /* Composer dans le flux, hauteur naturelle, toujours visible sous les messages. */
  .composer-wrap {
    flex: 0 0 auto;
    z-index: 40;
  }

  /* ── HEADER style maquette canvas ────────────────────────────────
     [≡] [avatar+dot] [Prénom <em>Nom</em> + statut vert] [vidéo rond]
     Cf. design canvas "Espace patient — mobile · Messagerie". */
  /* FOND UNIFORME LAVANDE (03/07, retour user — on aligne sur le modèle LEAD
     conversation.html : header + fil + body + html + composer = var(--bg)).
     Le canvas natif iOS lit getComputedStyle(body).backgroundColor pour peindre
     les safe-areas (encoche + barre home) → body lavande = plus de bande blanche
     en bas, la couleur « normale » de l'app défile de bord à bord comme la home. */
  html, body { background: var(--bg); }
  .main { background: var(--bg); }
  .thread-head {
    padding: var(--palette-espacement-cabinet-conversation-6) var(--palette-espacement-cabinet-conversation-7);
    gap: var(--palette-espacement-cabinet-conversation-6);
    align-items: center;
    /* Sticky top : reste visible quand on scroll les messages */
    position: sticky;
    top: 0;
    z-index: 10;
    /* Background opaque LAVANDE (modèle lead : var(--bg)) pour masquer le
       scroll dessous SANS liseré blanc — bordure basse effacée comme le lead. */
    background: var(--bg);
    border-bottom-color: transparent;
  }
  /* Flèche retour → home à gauche (touch 40×40) — refonte tab bar 02/07.
     L'identité du psy (nom + photo) est poussée à DROITE : lien inversé
     (row-reverse → avatar en dernier = tout à droite), texte aligné droite.
     Le bouton visio garde sa place entre la flèche et l'identité. */
  .thread-back-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--palette-rayon-components-2);
    color: var(--ink, var(--palette-texte-principal-composants-3));
    text-decoration: none;
    flex: 0 0 40px;
    padding: 0;
    margin-left: var(--palette-espacement-consultation);
  }
  .thread-back-btn:active { background: var(--surface-2, var(--palette-surface-composants)); }
  .thread-psy-link {
    /* order:10 = l'identité passe EN DERNIER dans la rangée (tout à droite),
       les boutons visio + fiche psy restent à gauche, juste après la flèche. */
    order: 10;
    flex-direction: row-reverse;
    justify-content: flex-start;
    gap: var(--palette-espacement-cabinet-conversation-6) !important;   /* écrase le gap:14px inline (avatar ↔ nom) */
  }
  .thread-psy-id { text-align: right; }

  /* Avatar (40px) + statut "En ligne · répond en X h" vert */
  .thread-head .av { width: 40px; height: 40px; flex: 0 0 40px; }
  /* Nom : "Sarah Houdas" → "Sarah" en sans-serif + "Houdas" en italic serif accent */
  .thread-head h1 {
    font-size: var(--palette-typographie-composants-9);
    font-weight: 600;
    letter-spacing: var(--palette-typographie-layout-4);
  }
  .thread-head h1 em {
    font-family: var(--palette-police-editoriale-espace-patient-2);
    font-style: italic;
    font-weight: 400;
    color: var(--accent, var(--palette-texte-action-composants));
    margin-left: var(--palette-espacement-cabinet-2);
  }
  .thread-head .sub {
    font-size: var(--palette-typographie-cabinet-conversation-4);
    line-height: 1.2;
    color: var(--muted);  /* titre du psy (plus un statut "en ligne" vert) */
    font-weight: 500;
    display: flex;
    align-items: center;
    gap: var(--palette-espacement-cabinet-conversation-9);
  }
  /* Le statut "En ligne" inclut un point vert via .status-dot */
  .thread-head .sub .status-dot {
    width: 7px;
    height: 7px;
    border-radius: var(--palette-rayon-composants-2);
    background: var(--adaptation-fond-succes-success, var(--success, var(--palette-fond-succes-espace-patient-2)));
    flex: 0 0 7px;
  }

  /* Cache éléments superflus en mobile (head-pill desktop + bouton ... dots).
     Le .thread-video-btn (qui a aussi .icon-btn) reste visible — on l'override
     juste après. */
  .thread-head .head-pill,
  .thread-head .icon-btn[data-icon="dots"] {
    display: none;
  }

  /* Bouton vidéo (visioconférence) : rond outline 38×38, accent color */
  .thread-video-btn.icon-btn {
    display: inline-flex;
    width: 38px;
    height: 38px;
    flex: 0 0 38px;
    border-radius: var(--palette-rayon-composants-2);
    border: 1.5px solid var(--line, var(--palette-bordure-conversation-4));
    background: var(--surface, var(--palette-surface-cabinet-conversation));
    color: var(--accent, var(--palette-texte-action-composants));
    padding: 0;
    align-items: center;
    justify-content: center;
  }
  .thread-video-btn.icon-btn:active { background: var(--surface-2, var(--palette-surface-composants)); }

  /* ── WELCOME BANNER (mobile) : "Sarah a reçu votre bilan" ───────
     Affiché entre le header et le thread. Fond accent-soft, icône
     shield violet, texte petit. */
  .thread-welcome {
    display: flex;
    align-items: flex-start;
    gap: var(--palette-espacement-cabinet-conversation-6);
    margin: var(--palette-espacement-cabinet-conversation-6) var(--palette-espacement-cabinet-conversation-7) 0;
    padding: var(--palette-espacement-cabinet-conversation-7) var(--palette-espacement-cabinet-conversation-5);
    background: var(--accent-soft, var(--accent-softer, var(--palette-surface-action-espace-patient)));
    border-radius: var(--palette-rayon-composants-3);
    flex: 0 0 auto;
  }
  .thread-welcome-ico {
    width: 28px;
    height: 28px;
    flex: 0 0 28px;
    border-radius: var(--palette-rayon-composants-2);
    background: var(--adaptation-fond-action-accent, var(--accent, var(--palette-fond-action-composants)));
    color: var(--palette-texte-inverse-components);
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .thread-welcome-text {
    font-size: var(--palette-typographie-components-4);
    color: var(--accent-hover, var(--palette-texte-action-espace-patient));
    line-height: 1.5;
    flex: 1;
  }
  .thread-welcome-text b { font-weight: 700; }
}

/* ═══════════════════════════════════════════════════════════════════════
   CARTE BILAN — SORTIE du @media (max-width:640px) : ces règles doivent
   s'appliquer sur TOUS les écrans (desktop inclus). AVANT, .ep-bilan était
   enfermée dans le bloc mobile → carte SANS STYLE sur desktop (bug). Le
   @media (max-width:640px) est refermé ici et rouvert juste après le bloc.
   ═══════════════════════════════════════════════════════════════════════ */

  /* ── CARTE « PROPOSITION DU BILAN » (metadata.kind='bilan_proposal') ──
     Rendue dans le fil de la messagerie abonné, DISTINCTE d'une bulle psy :
     fond lavande doux, liseré accent, CTA qui déroule le widget SUR PLACE.
     Calquée sur .conv-bilan de conversation.html. La même carte sert d'hôte
     au widget (BilanWidget.mount remplace son contenu interne). */
  .ep-bilan {
    flex-shrink: 0;  /* NE JAMAIS se compresser dans la colonne flex .thread : quand
       le widget se monte il ajoute .bpw{overflow:hidden} → un item flex avec overflow
       masqué a une taille min de 0 → le flex l'écraserait sans ce flex-shrink:0. */
    margin: var(--palette-espacement-cabinet-conversation-7) auto; max-width: 560px; width: calc(100% - 4px);
    padding: var(--palette-espacement-cabinet-conversation-2) var(--palette-espacement-cabinet-conversation-8);
    background: linear-gradient(135deg, var(--accent-softer, var(--palette-surface-action-composants-2)) 0%, var(--palette-surface-action-conversation-2) 100%);
    border: 1px solid var(--adaptation-bordure-accent-soft, var(--accent-soft, var(--palette-bordure-composants-7)));
    border-left: 3px solid var(--adaptation-bordure-action-accent, var(--accent, var(--palette-bordure-action-composants)));
    border-radius: var(--palette-rayon-composants-3);
    box-shadow: 0 4px 14px -10px var(--palette-ombre-conversation-12);
  }
  .ep-bilan-head { display: flex; align-items: center; gap: var(--palette-espacement-cabinet-conversation-11); margin-bottom: var(--palette-espacement-cabinet-conversation-11); }
  .ep-bilan-ic { font-size: var(--palette-typographie-composants); line-height: 1; }
  .ep-bilan-tag {
    font-size: var(--palette-typographie-composants-3); font-weight: 700; letter-spacing: var(--palette-typographie-conversation-6); text-transform: uppercase;
    color: var(--accent, var(--palette-texte-action-composants));
  }
  .ep-bilan-txt {
    margin: 0 0 var(--palette-espacement-cabinet-conversation-5); font-size: var(--palette-typographie-components); line-height: 1.55; color: var(--ink-2, var(--palette-texte-action-composants-2));
    white-space: pre-line; word-wrap: break-word;
  }
  .ep-bilan-cta {
    display: inline-flex; align-items: center; gap: var(--palette-espacement-cabinet-conversation-11);
    padding: var(--palette-espacement-cabinet-conversation-7) var(--palette-espacement-cabinet-conversation-10); border-radius: var(--palette-rayon-components-2); border: 0; cursor: pointer;
    background: var(--adaptation-fond-action-accent, var(--accent, var(--palette-fond-action-composants))); color: var(--palette-texte-inverse-components);
    font-family: inherit; font-size: var(--palette-typographie-cabinet-conversation-2); font-weight: 600; min-height: 46px;
    box-shadow: 0 4px 14px -4px var(--palette-ombre-conversation-9); transition: background .15s, transform .15s;
  }
  .ep-bilan-cta:hover { background: var(--adaptation-fond-action-accent-hover, var(--accent-hover, var(--palette-fond-action-composants-3))); transform: translateY(-1px); }
  .ep-bilan-cta:active { transform: translateY(0); }
  .ep-bilan-arrow { display: inline-flex; }
  .ep-bilan-arrow svg { width: 15px; height: 15px; }

  /* CARTE « bilan transmis » — confirmation VERTE persistante qui REMPLACE
     l'invitation une fois le bilan envoyé (flag local par id de message). Reprend
     le look de la confirmation du widget (.bpw-done) pour la continuité visuelle. */
  .ep-bilan-done {
    align-self: stretch; max-width: 560px; width: 100%;
    margin: var(--palette-espacement-cabinet-conversation-11) auto; padding: var(--palette-espacement-composants-6) var(--palette-espacement-cabinet-conversation-2);
    display: flex; align-items: center; gap: var(--palette-espacement-components-2);
    background: linear-gradient(135deg, var(--success-soft, var(--palette-surface-succes-composants-2)) 0%, var(--palette-surface-succes-composants) 100%);
    border: 1px solid var(--palette-bordure-composants-6);
    border-left: 3px solid var(--adaptation-bordure-succes-success, var(--success, var(--palette-bordure-succes-composants)));
    border-radius: var(--palette-rayon-composants-3);
  }
  .ep-bilan-done-ic {
    flex: 0 0 auto; width: 30px; height: 30px; border-radius: var(--palette-rayon-composants-2);
    background: var(--adaptation-fond-succes-success, var(--success, var(--palette-fond-succes-composants))); color: var(--palette-texte-inverse-components);
    display: flex; align-items: center; justify-content: center;
    font-size: var(--palette-typographie-components-3); font-weight: 700;
  }
  .ep-bilan-done-txt { font-size: var(--palette-typographie-components); color: var(--ink-2, var(--palette-texte-action-composants-2)); line-height: 1.45; }
  .ep-bilan-done-txt b { color: var(--ink, var(--palette-texte-principal-composants-3)); font-weight: 600; }

  /* ── CARTE « RÉSULTAT DE TEST » (metadata.kind='test_result') ──────────────
     Calquée sur .conv-result de conversation.html. Rendue en tête de fil dans la
     messagerie abonné (continuité après le funnel). TOP-LEVEL (comme .ep-bilan)
     pour s'appliquer sur desktop ET mobile — cf. bug v105. */
  .ep-result {
    flex-shrink: 0; margin: var(--palette-espacement-cabinet-conversation-7) auto; max-width: 560px; width: calc(100% - 4px);
    padding: var(--palette-espacement-composants-7) var(--palette-espacement-conversation-2);
    background: linear-gradient(135deg, var(--accent-softer, var(--palette-surface-action-composants-2)) 0%, var(--palette-surface-action-conversation-2) 100%);
    border: 1px solid var(--adaptation-bordure-accent-soft, var(--accent-soft, var(--palette-bordure-conversation-2)));
    border-left: 3px solid var(--adaptation-bordure-action-accent, var(--accent, var(--palette-bordure-action-composants)));
    border-radius: var(--palette-rayon-composants-3);
  }
  .ep-result-head { display: flex; align-items: center; gap: var(--palette-espacement-cabinet-conversation-11); margin-bottom: var(--palette-espacement-cabinet-conversation-4); }
  .ep-result-ic { font-size: var(--palette-typographie-composants-9); line-height: 1; }
  .ep-result-tag { font-size: var(--palette-typographie-composants-3); font-weight: 700; text-transform: uppercase; letter-spacing: var(--palette-typographie-conversation-7); color: var(--accent-ink, var(--palette-texte-action-conversation)); }
  .ep-result-title { font-size: var(--palette-typographie-cabinet-conversation-5); font-weight: 700; color: var(--ink, var(--palette-texte-principal-composants-3)); line-height: 1.3; }
  .ep-result-label {
    display: inline-block; margin-top: var(--palette-espacement-cabinet-conversation-3); padding: var(--palette-espacement-components-4) var(--palette-espacement-cabinet-conversation-6); border-radius: var(--palette-rayon-components-2);
    background: var(--palette-surface-cabinet-conversation); border: 1px solid var(--adaptation-bordure-accent-soft, var(--accent-soft, var(--palette-bordure-conversation-2)));
    font-size: var(--palette-typographie-components-2); font-weight: 600; color: var(--accent-ink, var(--palette-texte-action-conversation));
  }
  .ep-result-reco { margin: var(--palette-espacement-cabinet-conversation-4) 0 0; font-size: var(--palette-typographie-cabinet-conversation-3); color: var(--ink-2, var(--palette-texte-action-composants-2)); line-height: 1.5; }
  .ep-result-state {
    display: flex; align-items: center; gap: var(--palette-espacement-components); margin-top: var(--palette-espacement-cabinet-conversation-7); padding-top: var(--palette-espacement-components-2);
    border-top: 1px dashed var(--adaptation-bordure-accent-soft, var(--accent-soft, var(--palette-bordure-conversation-2))); font-size: var(--palette-typographie-components-2); font-weight: 600;
  }
  .ep-result-state .ep-result-dot {
    flex: 0 0 auto; width: 18px; height: 18px; border-radius: var(--palette-rayon-composants-2);
    display: flex; align-items: center; justify-content: center; font-size: var(--palette-typographie-composants-3); color: var(--palette-texte-inverse-components);
  }
  .ep-result-state.wait { color: var(--palette-texte-avertissement-conversation-2); }
  .ep-result-state.wait .ep-result-dot { background: var(--palette-fond-avertissement-conversation); }
  .ep-result-state.done { color: var(--palette-texte-succes-conversation); }
  .ep-result-state.done .ep-result-dot { background: var(--adaptation-fond-succes-success, var(--success, var(--palette-fond-succes-composants))); font-weight: 700; }

  /* ── ALERTE SYSTÈME CENTRÉE (message_type='system' générique, ex. kind='psy_transfer')
     Notification système discrète, centrée, SANS avatar ni attribution psy. TOP-LEVEL
     (hors @media) pour s'appliquer aussi en desktop (cf. piège v105 des cartes système). */
  .ep-sys {
    flex-shrink: 0; align-self: center; max-width: 460px; width: fit-content;
    margin: var(--palette-espacement-cabinet-conversation-5) auto; padding: var(--palette-espacement-cabinet-conversation-11) var(--palette-espacement-cabinet-conversation-2);
    display: flex; align-items: center; gap: var(--palette-espacement-cabinet-conversation-11);
    background: var(--accent-softer, var(--palette-surface-action-composants-2));
    border: 1px solid var(--adaptation-bordure-accent-soft, var(--accent-soft, var(--palette-bordure-composants-7)));
    border-radius: var(--palette-rayon-components-2);
    font-size: var(--palette-typographie-components-2); line-height: 1.45; color: var(--muted, var(--palette-texte-secondaire-espace-patient));
    text-align: center;
  }
  .ep-sys-ic { flex: 0 0 auto; font-size: var(--palette-typographie-cabinet-conversation-2); line-height: 1; }
  .ep-sys-txt { min-width: 0; }
  .ep-sys-txt b { color: var(--ink-2, var(--palette-texte-action-composants-2)); font-weight: 700; }

  /* ── SÉPARATEUR « HISTORIQUE D'UN TRANSFERT » (24/09/2026) ─────────────────
     Messages recopiés lors d'un changement de psychologue (forwarded_from_message_id) :
     « Historique de votre suivi avec X » avant eux, « Suite de votre suivi avec Y » après.
     Même motif que le séparateur de l'espace psy (.msg-transfer-sep), mais le libellé
     passe à la ligne sur téléphone au lieu de déborder. TOP-LEVEL (desktop + mobile). */
  .ep-histo {
    flex-shrink: 0; align-self: stretch;
    display: flex; align-items: center; gap: var(--palette-espacement-cabinet-conversation-6);
    margin: var(--palette-espacement-cabinet-conversation-7) 0 var(--palette-espacement-cabinet-conversation-3);
  }
  .ep-histo-line { flex: 1 1 16px; min-width: 16px; height: 1px; background: linear-gradient(90deg, transparent 0%, var(--accent-soft, var(--palette-surface-action-composants-3)) 50%, transparent 100%); }
  .ep-histo-label {
    flex: 0 1 auto; min-width: 0; max-width: 80%;
    padding: var(--palette-espacement-cabinet-conversation-9) var(--palette-espacement-cabinet-conversation-7); border-radius: var(--palette-rayon-components-2);
    background: var(--accent-softer, var(--palette-surface-action-composants-2)); color: var(--accent, var(--palette-texte-action-composants));
    font-size: var(--palette-typographie-cabinet-conversation-4); font-weight: 700; line-height: 1.35; text-align: center;
    overflow-wrap: anywhere;
  }

  /* CARTE « proposition de séance » (11/09/2026) — le psy propose un créneau ;
     le patient réserve avec un crédit ou par carte. Même famille visuelle que .ep-bilan. */
  .ep-prop {
    flex-shrink: 0; align-self: stretch; max-width: 560px; width: 100%;
    margin: var(--palette-espacement-cabinet-conversation-11) auto; padding: var(--palette-espacement-cabinet-conversation-5) var(--palette-espacement-cabinet-conversation-2);
    background: linear-gradient(135deg, var(--accent-softer, var(--palette-surface-action-composants-2)) 0%, var(--palette-surface-action-conversation-2) 100%);
    border: 1px solid var(--adaptation-bordure-accent-soft, var(--accent-soft, var(--palette-bordure-composants-7)));
    border-left: 3px solid var(--adaptation-bordure-action-accent, var(--accent, var(--palette-bordure-action-composants)));
    border-radius: var(--palette-rayon-composants-3);
  }
  .ep-prop.is-done { border-left-color: var(--adaptation-bordure-succes-success, var(--success, var(--palette-bordure-succes-composants))); background: linear-gradient(135deg, var(--success-soft, var(--palette-surface-succes-composants-2)) 0%, var(--palette-surface-succes-composants) 100%); border-color: var(--palette-bordure-composants-6); }
  .ep-prop.is-off { opacity: .75; border-left-color: var(--palette-bordure-espace-patient-2); background: var(--palette-surface-espace-patient-7); border-color: var(--palette-bordure-conversation-4); }
  .ep-prop-head { display: flex; align-items: center; justify-content: space-between; gap: var(--palette-espacement-cabinet-conversation-11); margin-bottom: var(--palette-espacement-cabinet-conversation-3); }
  .ep-prop-tag { font-size: var(--palette-typographie-composants-3); font-weight: 700; letter-spacing: var(--palette-typographie-consultation); text-transform: uppercase; color: var(--accent, var(--palette-texte-action-composants)); }
  .ep-prop.is-done .ep-prop-tag { color: var(--success, var(--palette-texte-succes-espace-patient)); }
  .ep-prop-state { font-size: var(--palette-typographie-components-4); font-weight: 600; color: var(--muted, var(--palette-texte-secondaire-espace-patient)); }
  .ep-prop-when { font-size: var(--palette-typographie-cabinet-conversation-5); font-weight: 700; color: var(--ink, var(--palette-texte-principal-composants-3)); }
  .ep-prop-dur { font-size: var(--palette-typographie-components-2); color: var(--muted, var(--palette-texte-secondaire-espace-patient)); margin-top: var(--palette-espacement-composants-5); }
  .ep-prop-note { margin: var(--palette-espacement-cabinet-conversation-11) 0 0; font-size: var(--palette-typographie-components); line-height: 1.5; color: var(--ink-2, var(--palette-texte-action-composants-2)); white-space: pre-line; }
  .ep-prop-actions { display: flex; gap: var(--palette-espacement-cabinet-conversation-11); flex-wrap: wrap; margin-top: var(--palette-espacement-cabinet-conversation-7); }
  .ep-prop-cta {
    display: inline-flex; align-items: center; gap: var(--palette-espacement-cabinet-conversation-3); padding: var(--palette-espacement-cabinet-conversation-6) var(--palette-espacement-cabinet-conversation-8); border-radius: var(--palette-rayon-components-2); border: 0; cursor: pointer;
    background: var(--adaptation-fond-action-accent, var(--accent, var(--palette-fond-action-composants))); color: var(--palette-texte-inverse-components); font-family: inherit; font-size: var(--palette-typographie-cabinet-conversation-2); font-weight: 600;
    box-shadow: 0 4px 14px -4px var(--palette-ombre-components);
  }
  .ep-prop-cta:disabled { opacity: .6; cursor: default; }
  .ep-prop-alt {
    display: inline-flex; align-items: center; padding: var(--palette-espacement-cabinet-conversation-6) var(--palette-espacement-cabinet-conversation-2); border-radius: var(--palette-rayon-components-2); cursor: pointer;
    background: transparent; color: var(--muted, var(--palette-texte-secondaire-espace-patient)); border: 1px solid var(--adaptation-bordure-accent-soft, var(--accent-soft, var(--palette-bordure-composants-7))); font-family: inherit; font-size: var(--palette-typographie-components); font-weight: 600;
  }
  .ep-prop-hint { font-size: var(--palette-typographie-cabinet-conversation-4); color: var(--muted, var(--palette-texte-secondaire-espace-patient)); margin-top: var(--palette-espacement-cabinet-conversation-11); }
  .ep-prop-confirm { margin-top: var(--palette-espacement-cabinet-conversation-7); padding: var(--palette-espacement-cabinet-conversation-7) var(--palette-espacement-cabinet-conversation-5); border-radius: var(--palette-rayon-cabinet-conversation-3); background: var(--palette-surface-cabinet-conversation); border: 1px solid var(--adaptation-bordure-accent-soft, var(--accent-soft, var(--palette-bordure-composants-7))); }
  .ep-prop-confirm p { margin: 0 0 var(--palette-espacement-cabinet-conversation-6); font-size: var(--palette-typographie-cabinet-conversation-2); line-height: 1.5; color: var(--ink, var(--palette-texte-principal-composants-3)); font-weight: 600; }
  .ep-prop-confirm .ep-prop-actions { margin-top: 0; }
  .ep-prop-flash { position: fixed; left: 50%; bottom: 84px; transform: translateX(-50%); z-index: 9000; padding: var(--palette-espacement-cabinet-conversation-6) var(--palette-espacement-cabinet-conversation-2); border-radius: var(--palette-rayon-components-2); background: var(--palette-fond-inverse-conversation); color: var(--palette-texte-inverse-components); font-size: var(--palette-typographie-components); font-weight: 600; box-shadow: 0 8px 24px -8px var(--palette-ombre-espace-patient-2); }

@media (max-width: 640px) {
  /* ── THREAD MESSAGES ──────────────────────────────────────────── */
  .thread { padding: var(--palette-espacement-cabinet-conversation-7) var(--palette-espacement-cabinet-conversation-5); }
  .msg-row { max-width: 88%; gap: var(--palette-espacement-cabinet-conversation-11); }
  .msg-av { width: 26px; height: 26px; flex: 0 0 26px; }

  /* ── COMPOSER WhatsApp-style ──────────────────────────────────── */
  /* Cache le preamble "Votre message pour psy" + status brouillon
     (info inutile, on est dans un chat) */
  .composer-preamble { display: none; }
  /* Cache la toolbar B / I / lien / resize — chat direct, pas
     d'édition riche en mobile (l'user peut quand même utiliser le
     clavier natif iOS qui propose gras/italique en long-press). */
  /* 16/09/2026 (Alexandre) : la barre gras / italique reste visible sur mobile aussi (Android n'a pas
     de mise en forme native, iOS seulement par appui long). */

  /* ── COMPOSER WRAP : NE DOIT JAMAIS SHRINK ──────────────────────
     Bug 20/05/2026 : quand l'user tapait beaucoup, le composer entier
     pouvait perdre de la hauteur (flex: 0 1 auto par défaut) → le
     bouton envoyer disparaissait sous le viewport. Force `flex: 0 0 auto`
     pour que le composer garde toujours sa hauteur naturelle, et la
     zone messages (`.thread`, flex:1, overflow-y:auto) compense en
     scrollant. Layout WhatsApp authentique. */
  /* COMPOSER FLOTTANT (02/07 nuit, retour user) : comme la tab bar, la
     pilule flotte au-dessus du fil (fond transparent, plus de bandeau blanc
     ni de bordure), la conversation défile dessous — réserve basse du fil
     juste après. Absolute dans .main (position:relative ci-dessous). Le
     repli bilan (body.bilan-active, max-height 0) reste compatible. */
  .main { position: relative; }
  .composer-wrap {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    /* max(env, --native-safe-bottom) : env()=0 dans l'app (cf. conversation). */
    padding: var(--palette-espacement-cabinet-conversation-11) var(--palette-espacement-cabinet-conversation-7) calc(var(--palette-espacement-cabinet-conversation-2) + max(env(safe-area-inset-bottom, var(--palette-espacement-composants)), var(--safe-bottom-fix, var(--native-safe-bottom, var(--palette-espacement-composants)))));
    border-top: none;
    background: transparent;
  }
  .thread { padding-bottom: var(--palette-espacement-espace-patient-4); }
  /* Extension canvas app (31/08) : .app est étendue de l'inset bas natif par le
     JS (--app-inset-extend) → on rend l'inset au padding du fil pour que le
     dernier message repose exactement à la même place qu'avant au scroll max. */
  html.in-app .thread { padding-bottom: calc(var(--palette-espacement-espace-patient-4) + var(--app-inset-extend, var(--palette-espacement-composants))); }
  /* COMPOSANT « PILULE » (maquette user 02/07 soir, commune aux 3 messageries) :
     [😊][saisie][📎] dans une pilule BLANCHE + bouton rond accent à droite
     (🎙 quand c'est vide → ✈️ dès qu'il y a du texte, bascule CSS pure sur
     les classes .is-empty/.has-text déjà posées par updateCount). Le
     .composer devient un simple rang transparent ; la pilule = l'input,
     😊 et 📎 sont posés en absolute par-dessus ses paddings. */
  .composer {
    padding: 0;
    border: none;
    background: transparent;
    display: flex;
    align-items: flex-end;
    gap: var(--palette-espacement-cabinet-conversation-6);
    position: relative;
  }
  .composer:focus-within { border-color: transparent; }
  .composer-input {
    flex: 1 1 0;
    min-width: 0;
    box-sizing: border-box;
    /* Verre LACTÉ dense (Vague 3 UX 03/07) : la pilule flotte en absolute
       au-dessus du fil (.thread padding-bottom:130px) → les bulles défilent
       floutées dessous. Dégradé .82→.66 volontairement DENSE (retour user
       « trop fondue » : le verre léger --glass-bg est réservé aux barres
       flottantes, pas aux zones de saisie). Repli opaque @supports ci-dessous. */
    background: linear-gradient(180deg, var(--palette-surface-layout-3), var(--palette-surface-layout-2));
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    /* contraste renforcé (retour user : « trop fondue »). */
    border: 1px solid var(--palette-bordure-action-conversation-2);
    border-radius: var(--palette-rayon-conversation-4);
    box-shadow: 0 10px 26px -10px var(--palette-ombre-conversation-17), 0 2px 6px -2px var(--palette-ombre-conversation-16);
    min-height: 48px;
    max-height: 120px;
    /* padding droit = place du 📎 (absolute par-dessus) */
    padding: var(--palette-espacement-composants-6) var(--palette-espacement-conversation-6) var(--palette-espacement-composants-6) var(--palette-espacement-cabinet-conversation-8);
    /* 16px MINIMUM en mobile : iOS Safari zoome auto sur les inputs avec
       font-size < 16px → casse la mise en page. 16px = seuil pas-zoom. */
    font-size: var(--palette-typographie-components-3);
    outline: none;
    overflow-y: auto;
  }
  /* Repli sans backdrop-filter (vieux Android / WebView) : pilule opaque,
     bordure + ombres de contraste inchangées. */
  @supports not ((backdrop-filter: blur(14px)) or (-webkit-backdrop-filter: blur(14px))) {
    .composer-input { background: var(--surface, var(--palette-surface-cabinet-conversation)); }
  }
  .composer.is-compact .composer-input,
  .composer.is-expanded .composer-input {
    min-height: 48px;
    max-height: 120px;
  }
  /* ── COMPOSER BAR : la barre est aplatie (display:contents) → ses enfants
     deviennent des items du rang .composer, sans toucher au HTML desktop. */
  .composer-bar {
    display: contents;
  }
  /* 📎 (attach) : DANS la pilule, à droite — absolute par-dessus le padding
     droit de l'input (trombone d'origine, l'icône « + » du look précédent
     est abandonnée — maquette 02/07 soir). */
  .composer-bar .tool-btn[data-icon="attach"] {
    position: absolute;
    /* 48px (bouton rond) + 10px (gap) + 13px (marge interne pilule) */
    right: 71px;
    bottom: 11px;
    display: flex;
    width: 26px;
    height: 26px;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--muted-2, var(--palette-texte-secondaire-conversation));
    align-items: center;
    justify-content: center;
    z-index: 2;
  }
  .composer-bar .tool-btn[data-icon="attach"]:active { color: var(--accent, var(--palette-texte-action-composants)); }
  /* Spacer et count : cachés (pas pertinents en mobile WhatsApp) */
  .composer-bar .spacer,
  .composer-bar .count {
    display: none;
  }
  /* 🎙 et ✈️ : LE bouton rond accent à droite de la pilule (48px, blanc sur
     accent). Un seul visible à la fois : micro quand c'est vide, envoi dès
     qu'il y a du texte (comme WhatsApp — bascule CSS sur .is-empty/.has-text,
     remplace la rangée précédente aux 2 boutons permanents, maquette 02/07 soir). */
  .composer-bar #voiceBtn,
  .composer-bar .send-btn {
    order: 3;
    display: flex;
    width: 48px;
    height: 48px;
    flex: 0 0 48px;
    padding: 0;
    border: none;
    border-radius: var(--palette-rayon-composants-2);
    background: var(--adaptation-fond-action-accent, var(--accent, var(--palette-fond-action-composants)));
    color: var(--palette-texte-inverse-components);
    align-items: center;
    justify-content: center;
    box-shadow: 0 10px 22px -10px var(--palette-ombre-conversation-18);
    font-size: 0; /* cache le label « Envoyer » */
  }
  .composer-bar #voiceBtn:active,
  .composer-bar .send-btn:active { background: var(--adaptation-fond-inverse-accent-ink, var(--accent-ink, var(--palette-fond-action-espace-patient-2))); }
  /* Bascule 🎙⟷✈️ en CROSS-FADE (Vague 1 UX — plus de display:none sec) :
     le bouton sortant s'écrase et se replie (width 0), l'entrant se déploie. */
  .composer-bar #voiceBtn,
  .composer-bar .send-btn {
    transition: opacity .18s ease, transform .18s ease, width .18s ease,
                flex-basis .18s ease, margin .18s ease, padding .18s ease;
  }
  .composer.has-text #voiceBtn,
  .composer.is-empty .send-btn {
    display: flex;
    width: 0; flex-basis: 0; min-width: 0; padding: 0; border: none;
    margin-left: var(--palette-espacement-conversation-5);   /* compense le gap du rang */
    opacity: 0; transform: scale(.55); overflow: hidden;
    box-shadow: none; pointer-events: none;
  }
  /* SVG paper-plane via CSS mask (icône blanche sur fond accent) */
  .composer-bar .send-btn::before {
    content: "";
    width: 19px;
    height: 19px;
    background: currentColor;
    display: block;
    -webkit-mask: var(--palette-masque-5) center/contain no-repeat;
            mask: var(--palette-masque-5) center/contain no-repeat;
  }
  /* Le SVG inline éventuel (du data-icon="send") est masqué */
  .composer-bar .send-btn svg,
  .composer-bar .send-btn > span:not(.icon-mask) { display: none; }

  /* Ombres COURTES dans l'app (même recette que la tab bar v242) : le canvas
     web s'arrête 34pt au-dessus du bas physique de l'écran ; l'ombre longue
     de la pilule (26px) et du bouton rond (22px) dépasse cette frontière et
     se fait COUPER net par la zone peinte par le natif → « bande » en bas.
     On éteint l'ombre ~10px sous les éléments, avant la frontière. */
  html.in-app .composer-input {
    box-shadow: 0 5px 12px -8px var(--palette-ombre-conversation-17), 0 2px 6px -2px var(--palette-ombre-conversation-16);
  }
  html.in-app .composer-bar #voiceBtn,
  html.in-app .composer-bar .send-btn {
    box-shadow: 0 5px 11px -7px var(--palette-ombre-conversation-3);
  }
  /* …sans réveiller l'ombre du bouton REPLIÉ (cross-fade 🎙⟷✈️) : la règle
     .has-text/.is-empty (box-shadow:none) est MOINS spécifique que l'override
     in-app ci-dessus → on la ré-affirme. */
  html.in-app .composer.has-text #voiceBtn,
  html.in-app .composer.is-empty .send-btn { box-shadow: none; }
  /* Le ✈️ n'apparaît que s'il y a du texte → l'état disabled grisé n'a plus
     de rendu dédié (le bouton est simplement absent quand il n'y a rien). */
  .composer-bar .send-btn:disabled {
    background: var(--surface-2, var(--palette-surface-composants));
    color: var(--muted-2, var(--palette-texte-secondaire-espace-patient-2));
    border: 1.5px solid var(--line, var(--palette-bordure-conversation-4));
  }
}

/* 100vh → 100dvh (iOS Safari barre URL) */
@supports (height: 100dvh) {
  .app { height: 100dvh; }
}

.conversation-choice { padding:var(--palette-espacement-cabinet-conversation-6) var(--palette-espacement-cabinet-conversation-10); border-bottom:1px solid var(--line); background:var(--paper,var(--palette-surface-cabinet-conversation)); }
.conversation-choice[hidden] { display:none; }
.conversation-buttons { display:flex; gap:var(--palette-espacement-cabinet-conversation-11); overflow-x:auto; padding:var(--palette-espacement-composants-5); scrollbar-width:thin; }
.conversation-tab { display:flex; align-items:center; gap:var(--palette-espacement-cabinet-conversation-6); flex:0 0 auto; max-width:260px; padding:var(--palette-espacement-cabinet-conversation-4) var(--palette-espacement-cabinet-conversation-5); border:1px solid var(--line); border-radius:var(--palette-rayon-composants-3); background:transparent; color:var(--ink); text-align:left; cursor:pointer; font:inherit; }
.conversation-tab[aria-current="true"] { background:var(--palette-surface-action-espace-patient-2); border-color:var(--palette-bordure-espace-patient-3); }
.conversation-tab:focus-visible { outline:2px solid var(--palette-bordure-action-composants); outline-offset:2px; }
.conversation-tab-avatar { display:grid; place-items:center; width:34px; height:34px; border-radius:var(--palette-rayon-composants-2); overflow:hidden; background:var(--palette-surface-action-espace-patient-3); color:var(--palette-texte-action-espace-patient-2); flex-shrink:0; font-weight:700; }
.conversation-tab-avatar img { width:100%; height:100%; object-fit:cover; }
.conversation-tab-text { min-width:0; display:grid; gap:var(--palette-espacement-components-4); }
.conversation-tab-name { font-weight:600; font-size:var(--palette-typographie-cabinet-conversation-2); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.conversation-tab-kind { font-size:var(--palette-typographie-cabinet-conversation-4); color:var(--palette-texte-secondaire-espace-patient-3); }
.conversation-tab-unread { min-width:18px; padding:var(--palette-espacement-composants-5) var(--palette-espacement-cabinet-conversation-9); border-radius:var(--palette-rayon-composants); color:var(--palette-texte-inverse-components); background:var(--palette-fond-action-composants); font-size:var(--palette-typographie-composants-3); text-align:center; }
@media(max-width:640px) { .conversation-choice { padding:var(--palette-espacement-cabinet-conversation-11) var(--palette-espacement-cabinet-conversation-6); } .conversation-tab { padding:var(--palette-espacement-cabinet-conversation-11) var(--palette-espacement-cabinet-conversation-6); max-width:220px; } }

/* Questionnaire d'accueil (.ob), ex-\3c style> inséré par shared/pages/espace-patient.js avec le formulaire (17/10/2026, CSP sans style en ligne).
   En fin de feuille : il venait après les styles de la page. */
.ob{max-width:560px;margin:0 auto;padding:var(--palette-espacement-conversation-7) var(--palette-espacement-conversation-8) var(--palette-espacement-conversation-9)}
.ob h2{font-size:var(--palette-typographie-conversation-10);margin:0 0 var(--palette-espacement-conversation-10);color:var(--palette-texte-principal-composants-3)}
.ob p.lead{color:var(--palette-texte-secondaire-conversation-2);font-size:var(--palette-typographie-conversation-11);margin:0 0 var(--palette-espacement-conversation-11);line-height:1.5}
.ob .q{background:var(--palette-surface-cabinet-conversation);border:1px solid var(--palette-bordure-conversation-4);border-radius:var(--palette-rayon-composants-3);padding:var(--palette-espacement-conversation-8);margin-bottom:var(--palette-espacement-conversation-12)}
.ob .q h3{font-size:var(--palette-typographie-composants-5);margin:0 0 var(--palette-espacement-conversation-13);color:var(--palette-texte-principal-composants-3)}
.ob label.f{display:block;font-size:var(--palette-typographie-conversation-12);font-weight:600;color:var(--palette-texte-secondaire-conversation-2);margin:var(--palette-espacement-conversation-14) 0 var(--palette-espacement-conversation-15)}
.ob input[type=text],.ob input[type=date]{width:100%;box-sizing:border-box;padding:var(--palette-espacement-conversation-13) var(--palette-espacement-conversation-16);border:1px solid var(--palette-bordure-conversation-5);border-radius:var(--palette-rayon-cabinet-conversation-4);font:inherit;font-size:var(--palette-typographie-composants-5);background:var(--palette-surface-cabinet-conversation)}
.ob .row{display:grid;grid-template-columns:1fr 1fr;gap:var(--palette-espacement-conversation-13)}
.ob .opt{display:flex;align-items:center;gap:var(--palette-espacement-conversation-17);padding:var(--palette-espacement-conversation-17) var(--palette-espacement-conversation-18);border:1px solid var(--palette-bordure-conversation-4);border-radius:var(--palette-rayon-cabinet-conversation-4);margin-bottom:var(--palette-espacement-conversation-14);cursor:pointer;font-size:var(--palette-typographie-conversation-11);background:var(--palette-surface-cabinet-conversation)}
.ob .opt:has(input:checked){border-color:var(--palette-bordure-action-composants);background:var(--palette-surface-action-conversation-4)}
.ob .opts2{display:grid;grid-template-columns:1fr 1fr;gap:var(--palette-espacement-conversation-14)}
.ob .err{color:var(--palette-texte-danger-conversation);font-size:var(--palette-typographie-conversation-13);min-height:1.2em;margin:var(--palette-espacement-conversation-19) 0}
.ob button.go{width:100%;padding:var(--palette-espacement-conversation-12);border:0;border-radius:var(--palette-rayon-cabinet-conversation-3);background:var(--palette-fond-action-composants);color:var(--palette-texte-inverse-components);font:inherit;font-size:var(--palette-typographie-composants-6);font-weight:700;cursor:pointer}
.ob button.go:disabled{opacity:.6;cursor:wait}
@media (max-width:480px){.ob .row,.ob .opts2{grid-template-columns:1fr}}
