/* =========================================================================
 *  LA VITRINE — O.R.I.O.N
 * =========================================================================
 *
 *  LE PRINCIPE : DES PANNEAUX QUI SE RECOUVRENT
 *  --------------------------------------------
 *  La page n'est pas une suite de sections posées bout à bout. C'est une pile
 *  de grandes plaques arrondies : chacune reste collée en haut pendant qu'on
 *  la lit, puis la suivante MONTE PAR-DESSUS et la recouvre. La plaque
 *  recouverte recule légèrement et s'assombrit, comme si elle passait sous la
 *  suivante.
 *
 *  Ce n'est pas un effet gratuit : c'est ce qui donne à une page longue la
 *  sensation d'un objet manipulé plutôt que d'un document déroulé. On sait à
 *  tout moment combien de plaques restent, et chaque changement de sujet est
 *  marqué par un geste physique au lieu d'un simple titre.
 *
 *  Tout repose sur `position: sticky` et une variable `--couvert` posée par le
 *  JavaScript. Aucune bibliothèque.
 *
 *  LA MATIÈRE
 *  ----------
 *  Fond de page très sombre, plaques en papier blanc, rayons généreux, ombres
 *  profondes. Le contraste entre la plaque et le fond est ce qui la fait
 *  exister comme objet. L'accent reste le cyan d'O.R.I.O.N — nulle part
 *  ailleurs.
 *
 *  `prefers-reduced-motion` désactive l'empilement : les plaques se suivent
 *  alors normalement, et rien ne bouge.
 * ========================================================================= */

:root {
  --nuit:    #0b0b0e;     /* le fond, derrière les plaques */
  --papier:  #ffffff;
  --papier2: #f5f5f7;     /* plaque secondaire, pour alterner */
  --encre:   #08080a;
  --gris:    #6c6c78;
  --gris-c:  #6f6f7d;     /* gris secondaire SUR PAPIER - 4,9:1 */
  --cyan-t:  #046b7c;     /* le cyan, version texte sur papier - 5,6:1 */
  --trait:   #e6e6ea;
  --cyan:    #00c2dd;
  --cyan-v:  #00e5ff;
  --cyan-d:  #eafbff;

  --r-plaque: 34px;
  --elan:      cubic-bezier(.22, .61, .36, 1);
  --elan-doux: cubic-bezier(.4, 0, .2, 1);
}

html.vitrine, html.vitrine body {
  height: auto; min-height: 100%; overflow: visible;
  background: var(--nuit);
}
html.vitrine body { overflow-x: hidden; }
html.vitrine { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html.vitrine { scroll-behavior: auto; } }

.vitrine-corps {
  position: relative; z-index: 1;
  padding-bottom: env(safe-area-inset-bottom);
}

/* ======================================================================= */
/*  LA PILE                                                                */
/* ======================================================================= */
.pile { position: relative; }

/*
 *  LES CONTRÔLES NE SUIVENT PAS LA COULEUR DU TEXTE — IL FAUT LE LEUR DIRE.
 *  ========================================================================
 *
 *  Un `<button>`, un `<input>`, un `<select>` n'héritent PAS de `color` : le
 *  navigateur leur applique sa propre couleur système, `buttontext`. Et cette
 *  couleur suit le thème du système d'exploitation.
 *
 *  Conséquence observée en production : les cartes de forfaits sont des
 *  `<button>` au fond blanc. Chez un visiteur en thème sombre, le prix
 *  s'affichait en BLANC SUR BLANC — invisible. Rien dans la feuille de style
 *  ne le laissait deviner, et l'écran d'un développeur en thème clair ne le
 *  montre jamais.
 *
 *  Une seule ligne referme la catégorie entière, pour tous les contrôles
 *  présents et à venir. Les variantes qui veulent une autre couleur la
 *  déclarent après, et gagnent par spécificité.
 */
button, input, select, textarea { color: inherit; }

.plaque {
  position: sticky; top: 0;
  min-height: 100svh;
  border-radius: var(--r-plaque);
  background: var(--papier);
  color: var(--encre);
  /*  `clip` ET PAS `hidden` — la différence n'est pas cosmétique.
   *
   *  `overflow: hidden` fait de l'élément un CONTENEUR DE DÉFILEMENT, et
   *  `position: sticky` ne fonctionne plus pour aucun de ses descendants. Le
   *  schéma des moteurs était censé rester à l'écran pendant qu'on parcourt
   *  les quatre paliers : il défilait comme un bloc ordinaire, sans que rien
   *  ne le signale — ni erreur, ni avertissement, juste un geste qui
   *  n'existait pas.
   *
   *  `clip` coupe exactement pareil sans créer de conteneur de défilement. */
  overflow: clip;
  /*  L'ombre PORTÉE VERS LE HAUT : c'est elle qui fait qu'une plaque a l'air
   *  de venir se poser sur celle d'en dessous, et pas de simplement défiler
   *  devant. Sans elle, l'empilement se lit comme un bug d'affichage. */
  box-shadow: 0 -24px 70px rgba(0, 0, 0, .5);
  /*  Le recul de la plaque recouverte. `--couvert` va de 0 à 1 et vient du
   *  JavaScript ; la plaque perd 4 % de taille et 22 % de lumière. */
  transform: scale(calc(1 - var(--couvert, 0) * .04));
  filter: brightness(calc(1 - var(--couvert, 0) * .22));
  transform-origin: 50% 0%;
  will-change: transform, filter;
}
/*  UN MEME ROLE, DEUX CONTEXTES.
 *  `--gris-c` designe le texte secondaire. Sur papier il doit etre sombre,
 *  sur plaque sombre il doit etre clair. Un seul jeton pour les deux, et
 *  l'un des deux devient illisible : on le redefinit donc ici. */
.plaque.sombre { background: var(--nuit); color: #fff;
                 --gris-c: rgba(255,255,255,.66); }
.plaque.creme  { background: var(--papier2); }

/*  La dernière plaque n'est jamais recouverte : elle n'a donc pas besoin
 *  d'être collée, et le pied de page peut passer dessous normalement. */
.plaque.derniere { position: relative; }

.plaque-in {
  width: min(1220px, calc(100% - 64px));
  margin: 0 auto;
  padding: clamp(72px, 9vw, 120px) 0;
}
@media (max-width: 720px) {
  .plaque-in { width: calc(100% - 40px); }
  :root { --r-plaque: 26px; }
}

@media (prefers-reduced-motion: reduce) {
  .plaque { position: relative; transform: none; filter: none; min-height: 0; }
}

/* ======================================================================= */
/*  TYPOGRAPHIE                                                            */
/* ======================================================================= */
.vitrine-corps h1, .vitrine-corps h2, .vitrine-corps h3 {
  font-weight: 700; letter-spacing: -.038em; line-height: 1.02;
}
.geant  { font-size: clamp(2.7rem, 7.4vw, 5.6rem); }
.grand  { font-size: clamp(2.1rem, 5vw, 3.6rem); }
.moyen  { font-size: clamp(1.35rem, 2.4vw, 1.7rem); letter-spacing: -.028em; }

.sous {
  font-size: clamp(1.05rem, 1.7vw, 1.32rem); line-height: 1.5;
  color: var(--gris); letter-spacing: -.012em; max-width: 46ch;
}
.plaque.sombre .sous { color: var(--gris-c); }

.marque-sec {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 14px; border-radius: 99px;
  font-size: .76rem; font-weight: 560; letter-spacing: .02em;
  background: var(--cyan-d); color: #046b7c;
}
.plaque.sombre .marque-sec {
  background: rgba(0, 226, 255, .12); color: var(--cyan-v);
}
.marque-sec i {
  width: 6px; height: 6px; border-radius: 50%; background: currentColor;
}

/* ======================================================================= */
/*  LES BOUTONS                                                            */
/* ======================================================================= */
.b {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  padding: 15px 26px; border-radius: 99px; border: 0;
  font: inherit; font-size: .97rem; font-weight: 600; letter-spacing: -.01em;
  text-decoration: none; cursor: pointer;
  transition: transform .4s var(--elan), background .25s var(--elan),
              color .25s var(--elan), box-shadow .3s var(--elan);
}
.b svg { width: 15px; height: 15px; transition: transform .3s var(--elan); }
.b:hover svg { transform: translateX(3px); }

.b-noir { background: var(--encre); color: #fff; }
.b-noir:hover { box-shadow: 0 12px 34px rgba(0, 0, 0, .3); }
.b-blanc { background: #fff; color: var(--encre); }
.b-blanc:hover { box-shadow: 0 12px 34px rgba(0, 0, 0, .45); }
.b-clair {
  background: transparent; color: var(--encre);
  box-shadow: inset 0 0 0 1px var(--trait);
}
.b-clair:hover { background: #fafafb; box-shadow: inset 0 0 0 1px #d4d4da; }

/*  LA BARRE EST SOMBRE, SON BOUTON SECONDAIRE DOIT L'ÊTRE AUSSI.
 *
 *  `.b-clair` est écrit pour une plaque de papier : encre noire, filet gris.
 *  Posé dans la barre — qui est une pastille presque noire — il donnait du
 *  noir sur noir, et « Connexion » était illisible. La règle de plaque sombre
 *  existait déjà mais ne pouvait pas s'appliquer : la barre est en dehors des
 *  plaques, c'est un élément fixe. */
.barre .b-clair {
  color: #fff;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .22);
}
.barre .b-clair:hover {
  background: rgba(255, 255, 255, .08);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .38);
}
.plaque.sombre .b-clair {
  color: #fff; box-shadow: inset 0 0 0 1px rgba(255,255,255,.2);
}
.plaque.sombre .b-clair:hover {
  background: rgba(255,255,255,.06); box-shadow: inset 0 0 0 1px rgba(255,255,255,.34);
}

/* ======================================================================= */
/*  LA BARRE                                                               */
/* ======================================================================= */
.barre {
  position: fixed; top: 14px; left: 50%; transform: translateX(-50%);
  z-index: 50; width: min(1220px, calc(100% - 32px));
  display: flex; align-items: center; gap: 8px;
  padding: 9px 9px 9px 20px; border-radius: 99px;
  background: rgba(12, 12, 15, .93);
  backdrop-filter: blur(26px) saturate(1.5);
  -webkit-backdrop-filter: blur(26px) saturate(1.5);
  border: 1px solid rgba(255, 255, 255, .1);
  transition: transform .45s var(--elan), opacity .3s var(--elan);
}

.sceau { display: flex; align-items: center; gap: 9px; text-decoration: none; color: #fff; }
/*  Le logo : la constellation d'Orion, un trait blanc. Au survol, elle
 *  s'allume comme un ciel qu'on regarde — sans tourner (une constellation
 *  à l'envers n'est plus Orion). */
.sceau svg { width: 28px; height: 28px; overflow: visible;
  transition: filter .5s var(--elan), transform .5s var(--elan); }
.sceau:hover svg { transform: scale(1.08); filter: drop-shadow(0 0 6px rgba(160, 220, 255, .75)); }
.sceau b { font-weight: 680; letter-spacing: .14em; font-size: .84rem; }

.barre nav { display: flex; gap: 2px; margin-left: 22px; }
.barre nav a {
  padding: 8px 14px; border-radius: 99px; text-decoration: none;
  font-size: .87rem; color: rgba(255, 255, 255, .62);
  transition: color .22s var(--elan), background .22s var(--elan);
}
.barre nav a:hover { color: #fff; }
.barre nav a[aria-current="true"] { color: #fff; background: rgba(255, 255, 255, .1); }
.barre .droite { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.barre .b { padding: 10px 19px; font-size: .88rem; }
@media (max-width: 940px) { .barre nav, .barre .cache-tel { display: none; } }

.burger {
  display: none; width: 38px; height: 38px; border-radius: 99px;
  border: 1px solid rgba(255,255,255,.16); background: none; cursor: pointer;
  flex-direction: column; align-items: center; justify-content: center; gap: 5px;
}
.burger i {
  width: 15px; height: 1.5px; border-radius: 2px; background: #fff;
  transition: transform .35s var(--elan);
}
.burger[aria-expanded="true"] i:nth-child(1) { transform: translateY(3.25px) rotate(45deg); }
.burger[aria-expanded="true"] i:nth-child(2) { transform: translateY(-3.25px) rotate(-45deg); }
@media (max-width: 940px) { .burger { display: flex; } }

.voile {
  position: fixed; inset: 0; z-index: 49; display: grid; place-items: center;
  background: rgba(11, 11, 14, .94);
  backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px);
  animation: fondu .3s var(--elan);
}
@keyframes fondu { from { opacity: 0 } to { opacity: 1 } }
.voile nav { display: flex; flex-direction: column; gap: 6px; text-align: center; }
.voile a {
  padding: 15px 34px; border-radius: 16px; text-decoration: none;
  font-size: 1.5rem; font-weight: 640; letter-spacing: -.03em; color: #fff;
  opacity: 0; transform: translateY(14px);
  animation: monterLien .45s var(--elan) forwards;
}
.voile a:nth-child(1) { animation-delay: .05s } .voile a:nth-child(2) { animation-delay: .1s }
.voile a:nth-child(3) { animation-delay: .15s } .voile a:nth-child(4) { animation-delay: .2s }
.voile a:nth-child(5) { animation-delay: .25s }
@keyframes monterLien { to { opacity: 1; transform: none } }
@media (prefers-reduced-motion: reduce) {
  .voile, .voile a { animation: none; opacity: 1; transform: none; }
}

/* ======================================================================= */
/*  LES APPARITIONS                                                        */
/* ======================================================================= */
.mot-boite { display: inline-block; overflow: hidden; vertical-align: bottom; }
.mot {
  display: inline-block; transform: translateY(110%);
  transition: transform .95s var(--elan);
  transition-delay: calc(var(--i, 0) * 46ms + var(--r, 0ms));
}
.vu .mot { transform: none; }

.monte {
  opacity: 0; transform: translateY(26px);
  transition: opacity .85s var(--elan) var(--r, 0ms),
              transform .85s var(--elan) var(--r, 0ms);
}
.monte.vu { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .mot, .monte { transform: none; opacity: 1; transition: none; }
}

/* ======================================================================= */
/*  LE HÉROS                                                               */
/* ======================================================================= */
.heros-haut {
  display: flex; align-items: flex-end; gap: 40px; flex-wrap: wrap;
  margin-bottom: clamp(34px, 5vw, 56px);
}
.heros-haut .txt { flex: 1 1 520px; min-width: 0; }
.heros-haut h1 { margin: 20px 0 22px; max-width: 15ch; }
.heros-boutons { display: flex; gap: 11px; flex-wrap: wrap; margin-top: 30px; }

/* ---------------------------- L'ORBE EN FOND ---------------------------- */
/*
 *  IL EST DERRIÈRE LE TEXTE, ET IL DOIT Y RESTER.
 *
 *  En vignette dans un coin, il ressemblait à un logo posé là. En fond, il
 *  devient la matière sur laquelle le titre est écrit. Trois réglages tiennent
 *  tout l'effet, et aucun n'est décoratif :
 *
 *   · `opacity` et la désaturation le LAVENT vers le blanc. À pleine
 *     intensité il passe devant le titre et le rend illisible — c'est un fond,
 *     il doit se comporter comme un fond.
 *   · `z-index: 0` avec le contenu à 1 : sans cela le canevas recouvre les
 *     boutons, qui cessent d'être cliquables sans que rien ne le montre.
 *   · `pointer-events: none` : l'orbe réagit au curseur, mais il ne l'INTERCEPTE
 *     jamais. Un fond qui avale les clics est un fond cassé.
 */
.orbe-fond {
  position: absolute;
  top: 50%; left: 58%;
  width: min(1180px, 96vw); aspect-ratio: 1;
  transform: translate(-50%, -54%);
  z-index: 0; pointer-events: none;
  opacity: .5;
  /*  La nébuleuse en pastel : ses vraies couleurs, adoucies, posées sur le
   *  blanc. Le texte reste noir sur un fond qui ne descend jamais sous le
   *  rose pâle — la lecture n'y perd rien. */
  filter: saturate(.85) blur(.3px);
  -webkit-mask-image: radial-gradient(closest-side, #000 52%, transparent 86%);
  mask-image: radial-gradient(closest-side, #000 52%, transparent 86%);
  transition: opacity .8s var(--elan);
}
.plaque#haut .plaque-in { position: relative; z-index: 1; }

/*  Sur un téléphone, un fond de cette taille coûte cher à dessiner pour un
 *  effet qu'on ne voit qu'à moitié : on le réduit plutôt que de le retirer. */
@media (max-width: 700px) {
  .orbe-fond { width: 150vw; opacity: .24; top: 32%; }
}
@media (prefers-reduced-motion: reduce) {
  .orbe-fond { opacity: .2 }
}

/* ----------------------- le lecteur de démonstration ------------------- */
/*  La grande surface sombre du héros : c'est l'application, en train de
 *  travailler. Une page qui décrit un logiciel sans jamais le montrer laisse
 *  un doute que trois paragraphes ne lèvent pas. */
.lecteur {
  border-radius: 26px; overflow: hidden; background: #101014;
  border: 1px solid rgba(255, 255, 255, .09);
  box-shadow: 0 34px 90px rgba(0, 0, 0, .45);
  transform: perspective(1600px) rotateX(var(--bx, 0deg)) rotateY(var(--by, 0deg));
  transition: transform .55s var(--elan);
}

/*  HAUTEUR FIXE, ET NON MINIMALE.
 *  La demonstration ajoute ses bulles une a une. Avec `min-height`, le
 *  lecteur grandissait a chaque replique : toute la page glissait sous le
 *  regard, les ancres visaient une position perimee, et le calage des
 *  plaques etait faux des la premiere seconde. La scene garde donc une
 *  taille constante, et c'est son contenu qui defile a l'interieur.
 *  `justify-content: flex-end` colle la conversation en bas, comme dans
 *  l'application : on voit toujours la derniere replique. */

/*  Bulles et tours : définis plus bas, avec la reconstitution de
 *  l'application. Cette version-ci était une approximation — bulle bleutée
 *  translucide, rayons de 18 px — là où l'application pose une bulle cyan
 *  pleine à encre sombre. Montrer l'approximation, c'était promettre une
 *  interface qui n'existe pas. */
@keyframes arriver { from { opacity:0; transform: translateY(10px) } to { opacity:1; transform:none } }
.stylo {
  display: inline-block; width: 2px; height: 1.05em; margin-left: 2px;
  vertical-align: text-bottom; background: var(--cyan-v);
  animation: cligne 1s steps(2) infinite;
}
@keyframes cligne { 0%,49%{opacity:1} 50%,100%{opacity:0} }

/*  Les étapes de la démonstration sont définies plus bas, avec le reste de la
 *  reconstitution de l'application. L'ancienne version les habillait d'une
 *  pastille encadrée : l'application, elle, les écrit en simples lignes. */
@media (prefers-reduced-motion: reduce) { .tour, .geste { animation: none } }

/* ======================================================================= */
/*  LA BANDE DÉFILANTE                                                     */
/* ======================================================================= */
.ruban {
  overflow: hidden; padding: 16px 0; margin-top: clamp(40px, 5vw, 64px);
  border-top: 1px solid var(--trait); border-bottom: 1px solid var(--trait);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.ruban-piste { display: flex; width: max-content; animation: rouler 44s linear infinite; }
.ruban:hover .ruban-piste { animation-play-state: paused; }
.ruban span {
  display: flex; align-items: center; gap: 38px; padding-right: 38px;
  font-size: .82rem; font-weight: 520; letter-spacing: .1em; text-transform: uppercase;
  color: var(--gris-c); white-space: nowrap;
}
.ruban span::after {
  content: ''; width: 4px; height: 4px; border-radius: 50%;
  background: var(--cyan); flex: none;
}
@keyframes rouler { to { transform: translateX(-50%) } }
@media (prefers-reduced-motion: reduce) { .ruban-piste { animation: none } }

/* ======================================================================= */
/*  LA GRILLE BENTO                                                        */
/* ======================================================================= */
/*  Six tuiles de tailles inégales. Chacune montre UN écran du produit en
 *  mouvement : ce ne sont pas des icônes avec une légende, ce sont des
 *  fragments d'interface qui font ce qu'ils annoncent. */
.bento {
  display: grid; gap: 14px; margin-top: clamp(38px, 5vw, 60px);
  grid-template-columns: repeat(6, 1fr);
  grid-auto-rows: 252px;
}
.tuile {
  position: relative; overflow: hidden; border-radius: 22px;
  background: var(--papier2); border: 1px solid var(--trait);
  padding: 22px; display: flex; flex-direction: column;
  transition: transform .5s var(--elan), box-shadow .5s var(--elan);
  isolation: isolate;
}
.tuile:hover { transform: translateY(-5px); box-shadow: 0 20px 46px rgba(0,0,0,.13); }
.tuile::after {
  content: ''; position: absolute; inset: 0; z-index: -1; opacity: 0;
  background: radial-gradient(280px circle at var(--x,50%) var(--y,50%),
              rgba(0,194,221,.16), transparent 66%);
  transition: opacity .4s var(--elan);
}
.tuile:hover::after { opacity: 1; }

.tuile.large { grid-column: span 4; }
.tuile.moyenne { grid-column: span 3; }
.tuile.petite { grid-column: span 2; }
.tuile.haute { grid-row: span 2; }
@media (max-width: 1000px) {
  .bento { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 236px; }
  .tuile.large, .tuile.moyenne, .tuile.petite { grid-column: span 2; }
  .tuile.haute { grid-row: span 1; }
}
@media (max-width: 620px) {
  .bento { grid-template-columns: 1fr; }
  .tuile.large, .tuile.moyenne, .tuile.petite { grid-column: span 1; }
}

.tuile .t-tete { position: relative; z-index: 2; }
.tuile h3 { font-size: 1.02rem; margin-bottom: 5px; letter-spacing: -.022em; }
.tuile p { font-size: .855rem; line-height: 1.45; color: var(--gris); max-width: 34ch; }
.tuile .t-scene {
  position: relative; flex: 1; margin-top: 14px; min-height: 0;
  /*  `overflow: hidden` ET `min-height: 0` : sans le second, un enfant
   *  flexible refuse de rétrécir sous sa hauteur de contenu et déborde par
   *  le bas quoi qu'on écrive sur le parent. */
  overflow: hidden;
}

/*  LA TUILE SOMBRE — celle qui porte la démonstration du coût. Le contraste
 *  avec les autres n'est pas décoratif : c'est l'argument central du produit,
 *  il a droit à un traitement différent. */
.tuile.nuit {
  background: var(--encre); border-color: rgba(255,255,255,.08); color: #fff;
}
.tuile.nuit p { color: rgba(255,255,255,.56); }
.tuile.cyan {
  background: linear-gradient(145deg, #001f28, #003d4d 55%, #00566b);
  border-color: rgba(0,226,255,.2); color: #fff;
}
.tuile.cyan p { color: rgba(210,247,255,.72); }

/* --- micro-scènes des tuiles --- */
.mini-msg {
  padding: 8px 12px; border-radius: 12px; font-size: .76rem; line-height: 1.4;
  background: #fff; border: 1px solid var(--trait); max-width: 90%;
  animation: glisser .6s var(--elan) both;
}
.mini-msg.moi { margin-left: auto; background: var(--cyan-d); border-color: #bfeef7; }
.mini-msg:nth-child(2) { animation-delay: .5s }
.mini-msg:nth-child(3) { animation-delay: 1s }
@keyframes glisser { from { opacity: 0; transform: translateY(8px) } to { opacity: 1; transform: none } }

.mini-code {
  font-family: var(--mono); font-size: .68rem; line-height: 1.75; white-space: pre;
  color: rgba(255,255,255,.82); overflow: hidden;
}
.mini-code .k { color: var(--cyan-v) } .mini-code .s { color: #9fe6a0 }
.mini-code .c { color: rgba(255,255,255,.32) }

.mini-ligne {
  display: flex; align-items: center; gap: 9px; padding: 7px 11px;
  border-radius: 10px; background: #fff; border: 1px solid var(--trait);
  font-size: .75rem; margin-bottom: 7px;
}
.tuile.nuit .mini-ligne, .tuile.cyan .mini-ligne {
  background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.09);
}
.mini-ligne .ok { margin-left: auto; color: var(--cyan-t); font-weight: 600; }
.tuile.nuit .mini-ligne .ok { color: var(--cyan-v); }

/*  L'ONDE VOCALE — un signal, pas un décor : elle dit que la voix est
 *  réellement écoutée. */
.onde { display: flex; align-items: center; gap: 3px; height: 100%; }
.onde i {
  flex: 1; border-radius: 99px; background: var(--cyan-v); height: 14%;
  animation: onduler 1.05s var(--elan-doux) infinite alternate;
  animation-delay: calc(var(--i) * -66ms);
}
@keyframes onduler { from { height: 12% } to { height: 100% } }
@media (prefers-reduced-motion: reduce) { .onde i { animation: none; height: 48% } }

.chiffre-geant {
  font-size: clamp(2rem, 4vw, 3rem); font-weight: 700; letter-spacing: -.04em;
  font-variant-numeric: tabular-nums; line-height: 1;
}
.tuile .lgd { font-size: .74rem; letter-spacing: .1em; text-transform: uppercase;
              color: var(--gris); margin-top: 6px; }
.tuile.nuit .lgd, .tuile.cyan .lgd { color: rgba(255,255,255,.66); }

/*  LA VIDÉO JOINTE — la pastille du fichier, comme dans l'application :
 *  une vignette, le nom, la durée. */
.mini-fichier {
  display: flex; align-items: center; gap: 9px; width: max-content; max-width: 90%;
  margin: 0 0 7px auto; padding: 6px 11px 6px 6px; border-radius: 12px;
  background: #fff; border: 1px solid var(--trait); font-size: .74rem;
  animation: glisser .6s var(--elan) both;
}
.mini-fichier b { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mini-fichier i { font-style: normal; color: var(--gris); }
.mf-vignette {
  position: relative; width: 40px; height: 26px; border-radius: 6px; flex: none;
  background: linear-gradient(135deg, #0b2a36, #134b5c 55%, #3e2a5a);
}
.mf-vignette::after {
  content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-35%, -50%);
  border-style: solid; border-width: 5px 0 5px 8px; border-color: transparent transparent transparent #fff;
}
.mini-fichier + .mini-msg { animation-delay: .4s }
.mini-fichier ~ .mini-msg:last-child { animation-delay: .9s }

/*  LES SOURCES — la pastille qui suit chaque réponse sourcée : les icônes
 *  des sites, empilées, puis leur nombre. */
.mini-sources {
  display: inline-flex; align-items: center; gap: 8px; width: max-content;
  padding: 6px 13px 6px 6px; border-radius: 99px; font-size: .86rem; font-weight: 560;
  background: rgba(255,255,255,.07); border: 1px solid rgba(0,229,255,.35);
  box-shadow: 0 0 0 3px rgba(0,229,255,.08); color: #fff;
}
.mini-sources b { font-weight: 640; color: var(--cyan-v); }
.ms-visages { display: inline-flex; }
.ms-visages i {
  display: grid; place-items: center; width: 24px; height: 24px; border-radius: 50%;
  font-style: normal; font-size: .64rem; font-weight: 700; color: #fff;
  border: 2px solid var(--encre); margin-left: -7px;
}
.ms-visages i:first-child { margin-left: 0; background: #3f5bd8; }
.ms-visages i:nth-child(2) { background: #8a4fd6; }
.ms-visages i:nth-child(3) { background: #157a55; }

/*  CONNECTÉ OU NON. La classe `connecte` est posée sur <html> avant le
 *  premier affichage (voir index.html) : aucun bouton ne clignote. */
html:not(.connecte) .si-connecte { display: none !important; }
html.connecte .si-visiteur { display: none !important; }

/* ======================================================================= */
/*  LES MOTEURS                                                            */
/* ======================================================================= */
.moteurs-grille {
  display: grid; gap: clamp(26px, 4vw, 58px); align-items: start;
  grid-template-columns: minmax(0, 430px) minmax(0, 1fr);
  margin-top: clamp(36px, 5vw, 56px);
}
@media (max-width: 960px) {
  .moteurs-grille { grid-template-columns: 1fr; }
  .schema-colle { position: static !important; }
}
.schema-colle { position: sticky; top: 116px; }
.schema {
  padding: 26px 24px; border-radius: 24px;
  background: rgba(255,255,255,.045); border: 1px solid rgba(255,255,255,.1);
}
.schema-titre {
  font-size: .72rem; letter-spacing: .16em; text-transform: uppercase;
  color: rgba(255,255,255,.62); margin-bottom: 20px;
}
.branche {
  display: flex; align-items: center; gap: 13px; padding: 12px 14px;
  border-radius: 14px; border: 1px solid transparent;
  transition: background .45s var(--elan), border-color .45s var(--elan),
              transform .45s var(--elan);
}
.branche + .branche { margin-top: 6px; }
.branche .nm {
  font-weight: 680; font-size: .92rem; letter-spacing: .04em; min-width: 70px;
  color: rgba(255,255,255,.74); transition: color .45s var(--elan);
}
.branche .br { flex: 1; height: 3px; border-radius: 99px; background: rgba(255,255,255,.12); overflow: hidden; }
.branche .br i {
  display: block; height: 100%; width: 0; min-width: 5px; border-radius: 99px;
  background: var(--cyan-v); transition: width .95s var(--elan);
}
.branche .ct {
  font-family: var(--mono); font-size: .73rem; min-width: 88px; text-align: right;
  color: rgba(255,255,255,.64); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.branche.on {
  background: rgba(0,226,255,.1); border-color: rgba(0,226,255,.28);
  transform: translateX(5px);
}
.branche.on .nm { color: var(--cyan-v); }
.branche.on .ct { color: #fff; }

.moteur-bloc { padding: clamp(30px, 5vw, 62px) 0; border-top: 1px solid rgba(255,255,255,.09); }
.moteur-bloc:first-child { border-top: 0; padding-top: 0; }
.moteur-bloc .rg { font-family: var(--mono); font-size: .76rem; color: var(--cyan-v); }
.moteur-bloc h3 { margin: 9px 0 11px; font-size: clamp(1.4rem, 2.8vw, 2rem); }
.moteur-bloc p { color: rgba(255,255,255,.6); line-height: 1.62; max-width: 52ch; }
.moteur-bloc .ex {
  margin-top: 14px; padding: 11px 15px; border-radius: 12px; font-style: italic;
  border-left: 2px solid rgba(0,226,255,.42); background: rgba(255,255,255,.035);
  font-size: .86rem; color: rgba(255,255,255,.55);
}

/* ======================================================================= */
/*  LES PLANS                                                              */
/* ======================================================================= */
.plans {
  display: grid; gap: 14px; margin-top: clamp(34px, 4vw, 52px);
  grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
  align-items: stretch;
}
.plan {
  --pc: var(--cyan);
  position: relative; display: flex; flex-direction: column; text-align: left;
  padding: 26px 22px; border-radius: 22px; cursor: pointer;
  /*  La carte est en papier : son texte est en encre, quel que soit le thème
   *  du système. Voir la note sur les contrôles, plus haut. */
  background: #fff; color: var(--encre); border: 1px solid var(--trait);
  transition: transform .45s var(--elan), box-shadow .45s var(--elan),
              border-color .45s var(--elan);
}
.plan:hover { transform: translateY(-6px); box-shadow: 0 22px 50px rgba(0,0,0,.12); }
.plan[aria-pressed="true"] {
  border-color: var(--encre); box-shadow: 0 0 0 1.5px var(--encre), 0 22px 50px rgba(0,0,0,.14);
}
/*  LA COULEUR DU PLAN NE PEUT PAS PORTER LE TEXTE.
 *  Elle vient du serveur : n'importe quelle valeur peut arriver ici, et les
 *  cyans clairs tombaient a 1,33:1 sur blanc. Aucune formule ne met un nom a
 *  l'abri d'une couleur future. Le nom passe donc en encre, et la couleur
 *  garde son role de repere sous forme de pastille. */
.plan .nm { font-weight: 700; letter-spacing: .06em; font-size: .88rem;
            color: var(--encre); display: inline-flex; align-items: center; gap: 8px; }
.plan .nm::before { content: ''; width: 9px; height: 9px; flex: none;
                    border-radius: 50%; background: var(--pc); }
.plan .ac { font-size: .82rem; margin-top: 3px; color: var(--gris); }
.plan .px {
  margin: 18px 0 3px; font-size: 1.95rem; font-weight: 700; letter-spacing: -.04em;
  font-variant-numeric: tabular-nums;
}
.plan .px small { font-size: .86rem; font-weight: 500; color: var(--gris); }
.plan .dt {
  font-size: .77rem; color: var(--gris); padding-bottom: 18px;
  border-bottom: 1px solid var(--trait);
}
.plan ul { list-style: none; margin-top: 18px; display: grid; gap: 11px; flex: 1; }
.plan li { display: flex; gap: 10px; font-size: .84rem; line-height: 1.42; }
.plan li svg { width: 14px; height: 14px; flex: none; margin-top: 3px; }
.plan li.oui svg { color: var(--pc); }
.plan li.non { color: var(--gris-c); }
.plan li b { display: block; font-weight: 600; color: var(--encre); }
.plan li span { color: var(--gris); font-size: .79rem; }
.plan .top {
  position: absolute; top: -9px; left: 22px; padding: 3px 11px; border-radius: 99px;
  background: var(--encre); color: #fff;
  font-size: .66rem; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
}

.budget {
  margin-top: 16px; padding: clamp(22px, 3vw, 34px); border-radius: 24px;
  background: var(--papier2); border: 1px solid var(--trait);
}
.budget-haut {
  display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-start;
  padding-bottom: 20px; border-bottom: 1px solid var(--trait);
}
.budget-haut .d { margin-left: auto; text-align: right; }
.montant { font-size: 1.6rem; font-weight: 700; letter-spacing: -.035em;
           font-variant-numeric: tabular-nums; }
.curseur-boite { margin: 22px 0 8px; }
.curseur-boite input[type="range"] {
  -webkit-appearance: none; appearance: none; width: 100%; height: 4px;
  border-radius: 99px; background: #dedee4; outline: none;
}
.curseur-boite input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 22px; height: 22px;
  border-radius: 50%; background: var(--encre); cursor: pointer;
  box-shadow: 0 0 0 5px rgba(8,8,10,.08); transition: box-shadow .2s var(--elan);
}
.curseur-boite input[type="range"]::-webkit-slider-thumb:hover { box-shadow: 0 0 0 9px rgba(8,8,10,.1); }
.curseur-boite input[type="range"]::-moz-range-thumb {
  width: 22px; height: 22px; border: 0; border-radius: 50%; background: var(--encre); cursor: pointer;
}
.bornes { display: flex; justify-content: space-between; font-size: .73rem; color: var(--gris-c); }
.estim { display: grid; gap: 11px; margin-top: 22px;
         grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); }
.estim > div {
  padding: 13px 12px; border-radius: 14px; text-align: center;
  background: #fff; border: 1px solid var(--trait);
}
.estim b { display: block; font-size: 1.08rem; font-weight: 700; color: var(--encre);
           font-variant-numeric: tabular-nums; }
.estim span { font-size: .69rem; letter-spacing: .08em; text-transform: uppercase; color: var(--gris); }
.budget-bas {
  display: flex; align-items: center; gap: 15px; flex-wrap: wrap;
  margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--trait);
}
.budget-bas .b { margin-left: auto; }
@media (max-width: 560px) { .budget-bas .b { margin-left: 0; width: 100%; } }

/* ======================================================================= */
/*  LES PREUVES                                                            */
/* ======================================================================= */
.preuves {
  display: grid; gap: 14px; margin-top: clamp(34px, 4vw, 52px);
  grid-template-columns: repeat(auto-fit, minmax(244px, 1fr));
}
/*  La carte est en PAPIER, posee sur une plaque sombre. Sans cette ligne,
 *  ses titres heritaient du blanc de la plaque : blanc sur blanc casse,
 *  1,09:1 - le titre existait, il ne se voyait pas. */
.preuves > div { padding: 24px 22px; border-radius: 20px; color: var(--encre);
                 background: var(--papier2); border: 1px solid var(--trait); }
.preuves h3 { font-size: .98rem; margin-bottom: 8px; }
.preuves p { font-size: .86rem; line-height: 1.55; color: var(--gris); }

/* ======================================================================= */
/*  LA FIN                                                                 */
/* ======================================================================= */
.fin { text-align: center; display: grid; place-items: center; gap: 0; }
.fin .orbe-fin { width: 116px; aspect-ratio: 1; margin-bottom: 26px; }
.fin h2 { max-width: 16ch; }
.fin .sous { margin: 18px auto 30px; text-align: center; }
.fin .heros-boutons { justify-content: center; }

.pied {
  margin-top: clamp(56px, 7vw, 92px); padding-top: 28px;
  border-top: 1px solid rgba(255,255,255,.09);
  display: flex; gap: 18px; flex-wrap: wrap; align-items: center;
  font-size: .83rem; color: rgba(255,255,255,.45); text-align: left;
}
.pied nav { margin-left: auto; display: flex; gap: 18px; flex-wrap: wrap; }
.pied a { color: inherit; text-decoration: none; transition: color .2s var(--elan); }
.pied a:hover { color: #fff; }
@media (max-width: 620px) { .pied nav { margin-left: 0 } }

/* ======================================================================= */
/*  LE FOCUS CLAVIER                                                       */
/* ======================================================================= */
/*  Sans cette règle, qui navigue au clavier ne sait pas où il est : l'anneau
 *  par défaut de Chrome est mesuré à rgb(16,16,16) sur 1 px — invisible sur
 *  fond sombre comme sur fond blanc. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--cyan); outline-offset: 3px; border-radius: 8px;
}
.plaque.sombre :where(a, button, input, [tabindex]):focus-visible,
.barre :where(a, button):focus-visible { outline-color: var(--cyan-v); }
.plan:focus-visible, .tuile:focus-visible, .b:focus-visible { border-radius: inherit; }

/*  L'ambiance : une nappe de lumière qui suit le curseur derrière les
 *  plaques. On ne remplace pas le pointeur — le natif est composé par le
 *  système et ne traîne jamais ; c'est l'environnement qui l'accueille. */
.ambiance {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  opacity: 0; transition: opacity .9s var(--elan);
  background: radial-gradient(600px circle at var(--cx,50%) var(--cy,40%),
              rgba(0,194,221,.16), transparent 70%);
}
.ambiance.on { opacity: 1; }
@media (prefers-reduced-motion: reduce), (pointer: coarse) { .ambiance { display: none } }
.aimant { transition: transform .4s var(--elan); }

/*  LE BOUTON DE LA BARRE — UNE ANIMATION QUI NE COURT APRES PERSONNE.
 *  Le suivi du curseur convient a un bouton pose au milieu d'une section,
 *  qu'on vise. Dans la barre, il deplace une cible fixe au moment meme ou
 *  la main s'en approche : on clique a cote. Il garde donc sa presence,
 *  mais par ses propres moyens — un reflet qui passe, et une respiration.
 */
.brille { position: relative; overflow: hidden; animation: respirer 3.6s var(--elan-doux) infinite; }
.brille::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 38%, rgba(255,255,255,.62) 50%, transparent 62%);
  transform: translateX(-130%);
  animation: reflet 3.6s var(--elan) infinite;
  pointer-events: none;
}
.barre .brille:hover { animation-play-state: paused; }
@keyframes respirer {
  0%, 72%, 100% { transform: translateY(0) }
  78%           { transform: translateY(-3px) }
  84%           { transform: translateY(0) }
  88%           { transform: translateY(-1.5px) }
  92%           { transform: translateY(0) }
}
@keyframes reflet {
  0%, 44% { transform: translateX(-130%) }
  62%     { transform: translateX(130%) }
  100%    { transform: translateX(130%) }
}
@media (prefers-reduced-motion: reduce) {
  .brille, .brille::after { animation: none }
}


/* ======================================================================= */
/*  LA PLAQUE DU CIEL — une animation pilotée par le défilement            */
/* ======================================================================= */
/*  CE N'EST PAS UNE ANIMATION QUI SE DÉCLENCHE : C'EN EST UNE QU'ON PILOTE.
 *
 *  La plaque fait trois hauteurs d'écran. Son contenu, lui, est COLLÉ et n'en
 *  fait qu'une : pendant les deux hauteurs restantes, on ne défile donc pas —
 *  on avance dans le temps. La molette déplace le soleil, la lune se lève,
 *  les couleurs changent, la météo se dépose. L'écran entier est l'animation.
 *
 *  C'est la seule façon honnête de montrer cette fonction : elle dépend de
 *  l'heure, et personne ne va rester douze heures sur une page d'accueil.
 */
.plaque.longue { min-height: 320vh; }
@media (prefers-reduced-motion: reduce) { .plaque.longue { min-height: 0; } }

.ciel-colle {
  position: sticky; top: 0; height: 100vh;
  display: flex; flex-direction: column; justify-content: flex-end;
  overflow: clip;
}
@media (prefers-reduced-motion: reduce) {
  .ciel-colle { position: static; height: auto; min-height: 72vh; }
}
.ciel-toile { position: absolute; inset: 0; z-index: 0; }
.ciel-toile canvas { position: absolute; inset: 0; width: 100%; height: 100%; }

/*  Un voile en bas : le texte doit tenir sur un ciel de plein midi comme sur
 *  un ciel de minuit. Sans lui, la même phrase blanche disparaît à 13 h. */
.ciel-voile {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to top, rgba(4,5,9,.94) 0%, rgba(4,5,9,.5) 27%, transparent 54%);
}
.ciel-texte {
  position: relative; z-index: 2; color: #fff;
  width: min(1220px, calc(100% - 64px)); margin: 0 auto;
  padding: 0 0 clamp(48px, 8vw, 96px);
}
@media (max-width: 720px) { .ciel-texte { width: calc(100% - 40px); } }
.ciel-texte h2 { max-width: 18ch; }
.ciel-texte .sous { color: rgba(255,255,255,.72); }

/*  L'HORLOGE — elle ne décore pas : elle dit que ce qu'on voit est calculé,
 *  pas joué en boucle. Le chiffre change avec la molette. */
.horloge {
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  margin-bottom: 18px;
}
.horloge b {
  font-size: clamp(2.6rem, 7vw, 4.6rem); font-weight: 700; letter-spacing: -.045em;
  font-variant-numeric: tabular-nums; line-height: 1;
}
.horloge span {
  font-size: .78rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--cyan-v);
}
.ciel-jauge {
  margin-top: 26px; height: 3px; border-radius: 99px;
  background: rgba(255,255,255,.16); overflow: hidden; max-width: 420px;
}
.ciel-jauge i {
  display: block; height: 100%; width: var(--avance, 0%);
  background: var(--cyan-v); border-radius: 99px;
}
.ciel-note {
  margin-top: 12px; font-size: .78rem; color: rgba(255,255,255,.5);
  max-width: 420px;
}

/*  LE CIEL DE LA DERNIÈRE PLAQUE — en fond, immobile, à l'heure qu'il est
 *  vraiment. Il n'est pas là pour bouger mais pour que la page se termine sur
 *  l'objet dont elle parle. */
.ciel-fond { position: absolute; inset: 0; z-index: 0; opacity: .55; }
.ciel-fond canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.plaque.sombre .plaque-in { position: relative; z-index: 2; }

/*  LA PREMIÈRE PLAQUE PASSE SOUS LA BARRE.
 *
 *  La barre est flottante (14 px du haut, ~56 px de haut) : avec le rembourrage
 *  commun de 72 px, la pastille « Assistant · par Leonvar » venait se poser
 *  exactement dessous, au contact. Elle a donc besoin de sa propre marge —
 *  les plaques suivantes, elles, arrivent en défilant et n'ont pas ce
 *  problème. */
.plaque:first-child .plaque-in { padding-top: clamp(112px, 13vw, 156px); }

/*  La note sous le schéma portait ses couleurs en attribut `style` : hors de
 *  portée de la feuille, donc hors de portée de toute correction globale.
 *  Elle rentre dans le rang — 6,6:1 au lieu de 4,09:1. */
.schema-note {
  margin-top: 18px; font-size: .78rem; line-height: 1.55;
  color: rgba(255, 255, 255, .66);
}

/* ======================================================================= */
/*  LE LECTEUR — RECONSTITUTION DE L'APPLICATION                           */
/* ======================================================================= */
/*
 *  Les valeurs ci-dessous ne sont pas choisies pour la vitrine : elles sont
 *  reprises de `app.css`, jeton par jeton. L'accent #6ee7ff, l'encre #04070c,
 *  la surface #0a0e15, les bords à 11 % de blanc, les rayons 22/10 px, la
 *  courbe d'élan. Une vitrine qui invente ses couleurs promet une interface
 *  qui n'existe pas, et la déception arrive au premier écran après
 *  l'inscription.
 */
.lecteur {
  --a: #6ee7ff;                    /* accent */
  --a-doux: rgba(110,231,255,.13);
  --a-trait: rgba(110,231,255,.38);
  --encre-app: #04070c;
  --txt: #eaf1f7;
  --txt-doux: rgba(234,241,247,.72);
  --bord: rgba(255,255,255,.11);
  --verre: rgba(8,11,18,.62);
  --verre-cote: rgba(8,11,18,.72);

  position: relative;
  display: block;
  background: #05070c;
}

/*  LE CIEL. Dans l'application, le fond n'est pas une image : il suit l'heure
 *  et la météo réelles. La vitrine en montre une instance, en sourdine —
 *  `muet` l'empêche de publier ses couleurs sur toute la page, sans quoi deux
 *  ciels se disputeraient les mêmes variables. */
.app-ciel {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(178deg, #0a1220 0%, #070c16 55%, #05070c 100%);
}
.app-ciel canvas { width: 100%; height: 100%; display: block; }
.lecteur > .app-scene { position: relative; z-index: 1; }

/* ------------------------------- la scène ------------------------------- */
.app-scene { display: flex; flex-direction: column; min-width: 0; }



/* ------------------------------ l'en-tête ------------------------------ */
.app-entete {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 20px;
  border-bottom: 1px solid var(--bord);
  background: rgba(8, 11, 18, .55);
}
.app-titre {
  font-size: .95rem; font-weight: 600; letter-spacing: .01em; color: var(--txt);
  margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.app-meteo {
  margin-left: auto; display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 12px; border-radius: 99px;
  border: 1px solid var(--bord); background: rgba(8, 11, 18, .4);
  font-size: .74rem; color: var(--txt-doux); white-space: nowrap;
}
.app-pastille {
  width: 7px; height: 7px; border-radius: 50%; background: var(--a);
  box-shadow: 0 0 0 4px rgba(110, 231, 255, .12);
}

/* -------------------------------- le fil -------------------------------- */
/*  HAUTEUR FIXE. La conversation s'allonge pendant la démonstration ; sans
 *  cette contrainte, toute la page glissait sous le regard à chaque réplique. */
.app-fil {
  height: 318px; overflow: hidden;
  /*  Le fil garde une hauteur fixe : ce qui remonte au-dessus sort du cadre.
   *  Sans ce fondu, la bulle du haut se termine par une coupe nette qui se
   *  lit comme un défaut d'affichage. Avec, elle se lit comme une
   *  conversation qu'on a fait défiler — ce qu'elle est. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 42px);
  mask-image: linear-gradient(to bottom, transparent 0, #000 42px);
  padding: 20px clamp(16px, 2.2vw, 26px);
  display: flex; flex-direction: column; justify-content: flex-end; gap: 12px;
  color: var(--txt);
}
.tour { display: flex; flex-direction: column; gap: 10px; }
.tour.moi { align-items: flex-end; }

.bulle {
  position: relative; max-width: 100%;
  padding: 13px 17px; border-radius: 22px;
  font-size: .89rem; line-height: 1.55; white-space: pre-wrap;
  border: 1px solid var(--bord); background: var(--verre);
  box-shadow: 0 18px 48px -28px rgba(0, 0, 0, .82);
  animation: surgir-app .38s var(--elan) both;
}
/*  Le filet de lumière en haut de la bulle de verre, comme dans l'application. */
.bulle::before {
  content: ''; position: absolute; left: 14%; right: 14%; top: 0; height: 1px;
  background: linear-gradient(to right, transparent, rgba(255,255,255,.26), transparent);
  pointer-events: none;
}
.tour.moi .bulle {
  max-width: min(74%, 520px);
  background: var(--a); color: var(--encre-app);
  border-color: transparent; border-bottom-right-radius: 10px;
}
.tour.moi .bulle::before { display: none; }
.tour.lui .bulle { border-bottom-left-radius: 10px; }
@keyframes surgir-app {
  from { opacity: 0; transform: translateY(10px) scale(.985) }
  to   { opacity: 1; transform: none }
}

/*  Les étapes : la même ligne discrète que dans l'application, avec sa marque
 *  à gauche et le coût réel à droite. */
.geste {
  display: flex; align-items: center; gap: 9px;
  font-size: .8rem; color: var(--txt-doux);
  animation: glisser-app .3s var(--elan) both;
}
@keyframes glisser-app { from { opacity: 0; transform: translateX(-6px) } to { opacity: 1; transform: none } }
.geste .pt {
  width: 6px; height: 6px; flex: none; border-radius: 50%;
  background: var(--txt-doux); transition: background .35s var(--elan), box-shadow .35s var(--elan);
}
.geste.fait .pt { background: var(--a); box-shadow: 0 0 0 3px rgba(110,231,255,.14); }
.geste .eur {
  margin-left: auto; padding-left: 14px; white-space: nowrap;
  font-family: var(--mono); font-size: .74rem; color: rgba(234,241,247,.62);
}

/* ----------------------------- le composeur ----------------------------- */
.app-composeur {
  padding: 10px 20px 16px;
  border-top: 1px solid rgba(255, 255, 255, .06);
  background: linear-gradient(to top, rgba(5,7,12,.9), rgba(5,7,12,0));
}
.app-rang {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 9px 9px 18px; border-radius: 22px;
  border: 1px solid var(--bord); background: rgba(8, 11, 18, .62);
}
.app-champ {
  flex: 1; min-width: 0; font-size: .86rem; color: rgba(234,241,247,.45);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.app-champ.ecrit { color: var(--txt); }
.app-rond {
  width: 32px; height: 32px; flex: none; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--txt-doux); border: 1px solid transparent;
  transition: background .3s var(--elan), color .3s var(--elan);
}
.app-rond svg { width: 16px; height: 16px; }
.app-envoi { background: rgba(255,255,255,.07); }
/*  Le bouton d'envoi s'allume quand il y a quelque chose à envoyer — c'est
 *  exactement ce que fait l'application, et c'est ce qui rend la scène
 *  crédible. */
.app-envoi.actif { background: var(--a); color: var(--encre-app); }
.app-sous-champ {
  display: flex; gap: 12px; align-items: center;
  margin-top: 9px; padding: 0 4px;
  font-size: .71rem; color: rgba(234,241,247,.52);
}
.app-cout { margin-left: auto; font-family: var(--mono); white-space: nowrap; }

@media (max-width: 560px) {
  .app-fil { height: 268px; }
  .app-meteo { display: none; }
  .app-sous-champ span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

/*  Les sources dans la démonstration du haut de page. */
.demo-sources { margin: 2px 0 4px; font-size: .78rem; padding: 4px 11px 4px 5px; }
.demo-sources .ms-visages i { width: 20px; height: 20px; font-size: .58rem; border-color: #0b1019; }
