/* ===== Chantier 130 — les seules règles qui ne viennent pas des démos =====
   Recopié tel quel à la fin de v3.css par scripts/build-onboarding-v3.mjs.
   Chaque bloc dit POURQUOI il existe. Rien d'autre n'a le droit d'être ici. */

/* Le champ de fichier, hors écran. La démo le posait dans l'habillage iPhone
   (`#fin`) ; dans l'app il sert sur les trois tailles d'écran. */
#whv-v3-file{position:fixed;left:-999px;top:0;opacity:0}

/* L'attente pendant la lecture d'une fiche : même règle que la démo iPhone,
   mais valable aussi sur tablette et Mac. */
.sf b.wait{color:#B5B5B0!important;animation:shim 1s ease-in-out infinite}
@keyframes shim{50%{opacity:.35}}

/* « Pas d'avis » (consigne du chantier) : les trois cartes d'avis de la démo
   portaient « Vrai avis, même profil · à brancher ». Un faux avis ne s'affiche
   pas — règle données du projet. Le CSS Mac de la démo les cachait déjà. */
.oav{display:none!important}

/* La fausse fenêtre iOS « souhaite vous envoyer des notifications » n'a aucune
   raison d'exister dans l'app : sur iOS c'est le vrai système qui demande, sur
   le site on ne demande rien. Déclaration reprise du CSS Mac de la démo. */
.palert{display:none!important}

/* APP iOS : aucune notification dessinée à l'écran, c'est une vraie notification
   locale qui arrive. */
html[data-whv="app"] .nban{display:none!important}

/* SITE WEB : la notification d'exemple glisse du haut après 1 s, sans fenêtre
   d'autorisation. Déclarations reprises mot pour mot du CSS de la démo pc. */
html[data-whv="site"] .nban{opacity:1!important;transform:none!important;margin:18px auto 0!important;max-width:420px;animation:webban .6s cubic-bezier(.2,.8,.2,1) 1s both}
@keyframes webban{from{translate:0 -60px;filter:opacity(0)}to{translate:0 0;filter:opacity(1)}}
html[data-whv="site"] .nban{margin-bottom:28px!important}

/* L'app n'a pas la barre de commande de la démo (choix du parcours, flèches) :
   le parcours vient des réponses. Si un reste de balise traînait, il ne
   s'afficherait pas. */
#bar{display:none!important}

/* Le CSS de l'app (Tailwind) pose line-height:1.5 sur <html>. Les démos sont
   des pages nues : partout où elles ne donnent pas d'interligne, leurs écrans
   comptent sur la valeur par défaut du navigateur. Sans ce rappel, chaque bloc
   de texte grandit de quelques pixels et toute la page glisse vers le bas
   (mesuré sur l'offre : .opl haute de 100 px au lieu de 90, « Annulable à tout
   moment » poussé hors de l'écran). L'interligne est hérité : le poser sur la
   racine du funnel suffit, et les règles des démos qui en fixent un gagnent.
   C'est la seule différence de rendu trouvée entre l'app et les démos. */
#app{line-height:normal}

/* ===== Chantier 130d — un vrai téléphone, pas une maquette de téléphone =====

   LA FAUSSE BARRE D'ÉTAT N'EXISTE PLUS.
   `.sb` est le décor de la maquette : « 9:41 » à gauche, la pile et le réseau à
   droite. Dans une démo on dessine un téléphone, donc on dessine sa barre ; dans
   l'app on EST sur le téléphone, qui a déjà la sienne — Joshua en voyait deux
   l'une sous l'autre sur son iPhone. Elle est retirée des trois endroits qui
   l'émettent d'un seul coup : les écrans mobiles (`WHV_MOB_SCREEN`), la page de
   fin et l'offre (`CG.html` / `CG.offerHtml`, sélecteur `.cgx .sb`).
   Même façon de faire que `.oav` (faux avis) et `.palert` (fausse fenêtre iOS)
   ci-dessous : on ne touche ni aux démos ni au code généré.
   La barre de PROGRESSION, elle, reste — mais une seule, posée par glue.js :
   voir le bloc 130f plus bas. */
.sb{display:none!important}

/* L'ONBOARDING OCCUPE L'ÉCRAN, ZONE SÛRE COMPRISE.
   `#app` est le cadre réel : on lui donne les encoches en padding, et la boîte
   de l'onboarding se pose dans ce qui reste (les enfants en `position:absolute`
   se placent sur la boîte de padding). `WHV.fit` lit `clientWidth/clientHeight`,
   donc il n'a jamais à interpréter `env()` lui-même. */
#app{padding:env(safe-area-inset-top) 0 env(safe-area-inset-bottom);background:var(--bg)}

/* Chantier 136 — AUCUNE COUTURE EN HAUT DE L'ÉCRAN.
   La bande de la zone sûre laissait voir le fond de `#app` (#F4F4F2) par-dessus
   un écran sombre ou derrière la lueur verte : une ligne horizontale nette sous
   la Dynamic Island. Le fond de la racine suit donc l'écran affiché, et
   `body.dk` est déjà posé par le moteur sur les écrans sombres. */
html, body{background:var(--bg)}
body.dk, body.dk #app{background:#000}
#app:has(ag-screen[data-t=agfinal]){background:#000}

/* ===== Chantier 134 — LA RANGÉE DU HAUT : une seule, toujours au même endroit

   Sur l'iPhone 17 Pro, Joshua voyait deux barres, dont une sous la Dynamic
   Island, et qui bougeait d'un écran à l'autre. Les deux barres des démos sont
   donc RETIRÉES du DOM (`display:none`, pas `visibility`), et glue.js pose à
   leur place une rangée unique, `position:fixed`, calée sur la zone sûre.

   La rangée fait 44 px : une case de 44 × 44 px réservée au bouton retour, puis
   la barre jusqu'à 20 px du bord droit. La case existe même quand la flèche est
   masquée — c'est ce qui empêche la barre de bouger d'un écran à l'autre.

   `env(safe-area-inset-top)` ne vaut quelque chose que parce que
   `viewportFit:'cover'` est posé dans app/layout.tsx. Sur le web, il vaut 0 et
   la rangée se place à 14 px du haut, comme demandé. */
#whv-top{
  position:fixed;
  top:calc(env(safe-area-inset-top, 0px) + 14px);
  left:0; right:0;
  height:44px;
  z-index:9;
  display:flex;
  align-items:center;
  pointer-events:none;
}

/* La case du retour : 44 × 44, zone de tap entière, chevron centré. */
#whv-back{
  width:44px; height:44px;
  flex:none;
  border:0; padding:0; margin:0;
  background:none;
  color:#111;
  display:grid; place-items:center;
  cursor:pointer;
  pointer-events:auto;
}
#whv-back svg{width:10px;height:17px}
/* Sur les écrans sombres, le chevron doit rester lisible. */
#app.dk #whv-back, body.dk #whv-back{color:#fff}

/* La barre : plus épaisse et arrondie qu'avant (3 px plats), avec les couleurs
   déjà présentes dans les démos — piste #E1E1DD, remplissage #111. */
#whv-pg{
  flex:1;
  height:6px;
  margin-right:20px;
  background:#E1E1DD;
  border-radius:999px;
  overflow:hidden;
}
#whv-pg>i{
  display:block;
  height:100%;
  width:0;
  background:#111;
  border-radius:999px;
  transition:width .45s cubic-bezier(.22,1,.36,1);
}

/* LES DEUX BARRES DES DÉMOS N'EXISTENT PLUS.
   Celle du gabarit des questions n'est même plus ÉMISE : le ternaire `.pg` est
   retiré du gabarit par scripts/build-onboarding-v3.mjs, parce que le point 1
   demande qu'il n'en reste qu'une dans tout le DOM — et `display:none` en
   laissait une deuxième, comptée par le script de mesure.
   Celle du module Argent (`.bar`) est masquée par sa propre feuille, dans sa
   racine fantôme, et la rangée du haut remplace les deux. */

/* La feuille de l'onboarding 103b (`components/onboarding/web/funnel.css`)
   n'est PAS neutralisée ici. Elle déclare des sélecteurs non scopés — `.cta`,
   `.row`, `.pg3`… 19 de ses noms de classe existent aussi dans les démos — et
   Next la livre sur /onboarding même quand c'est le v3 qui s'affiche. Rapiécer
   classe par classe ne tenait pas : la rustine neutralisait `position`, `left`,
   `right`, `bottom`, et la recette tablette a trouvé le coup suivant,
   `translate:-50%`, qui poussait le bouton de l'offre à moitié hors de l'écran.
   C'est `components/onboarding/v3/FunnelV3.tsx` qui met cette feuille en
   sommeil tant que le v3 est à l'écran (`endormirFunnel103b`). Aucune valeur
   des démos n'a donc à être recopiée ici. */

/* Chantier 134 — le diagnostic de l'offre de sortie, ADMINS DU STUDIO SEULEMENT.
   Après le test de Joshua, `exit_offer_seen_at` était resté NULL et rien ne
   disait laquelle des conditions avait bloqué la croix. Cette ligne le dit, en
   discret, sous le bouton de l'offre. Elle n'est jamais rendue pour un client :
   `FunnelV3` ne la monte que si `admin` est vrai (lu de `is_studio_admin`). */
.whv-diag-sortie{
  position:fixed;
  left:0;right:0;
  bottom:calc(env(safe-area-inset-bottom, 0px) + 8px);
  /* Chantier 135 — au-dessus de TOUT, y compris la rangée du haut et les
     couches du moteur. Montée dans `document.body` par un portail : un z-index
     ne sort pas du contexte d'empilement de son parent. */
  z-index:2147483000;
  text-align:center;
  pointer-events:none;
}
.whv-diag-sortie > span{
  display:inline-block;
  background:#fff;
  border-radius:999px;
  padding:4px 10px;
  font-size:12px;
  color:#8A8A84;
}

/* Chantier 136 — le bouton d'achat pendant que la feuille Apple s'ouvre.
   Sans ce retour, elle met une seconde à venir, on croit que rien ne s'est
   passé et on rappuie : c'est ce qui a produit trois achats enchaînés le
   9 octobre. Le verrou de FunnelV3 empêche le double appel ; ceci empêche
   l'envie de rappuyer. Pas de nouvelle couleur : juste l'opacité. */
#app .whv-attente{opacity:.55;pointer-events:none}

/* Chantier 136, revu au 139 — rien ne se voit avant que ce soit prêt.
   Le moteur se charge, pose son CSS et rend son premier écran : trois étapes
   qui se voyaient l'une après l'autre au premier lancement.
   Le fondu passe de 200 à 150 ms : c'est la durée imposée au point 2 du 139
   pour l'apparition de la première question après la connexion Apple, et elle
   tient aussi la limite du point 1 (200 ms au plus au lancement). Une seule
   durée pour les deux, donc une seule chose à régler. */
#app.whv-pas-pret{opacity:0}
#app.whv-pret{opacity:1;transition:opacity .15s ease}

/* Chantier 136, revu au 139 — UNE SEULE fenêtre sur l'écran des notifications.
   APP SEULEMENT (`glue.js` ne pose la classe que si APP). La fausse fenêtre de
   la démo s'ouvrait au milieu de l'écran, la vraie arrivait 400 ms plus tard
   pile dessus : deux fenêtres superposées, et le texte de l'écran caché
   derrière. On retire la fausse — la vraie EST la fenêtre.

   CE QUI A ÉTÉ RETIRÉ AU 139, et pourquoi : le contour, la flèche et le
   libellé « Touche « Autoriser » ». Un `UIAlertController` est présenté dans
   sa propre fenêtre, AU-DESSUS de la WKWebView : la page ne peut rien dessiner
   devant lui. La règle de Joshua est nette — si on ne peut pas le mettre
   au-dessus, on ne le met pas. Ne pas réessayer. */
#app .whv-notif-app .palert{display:none}
/* Le 139 remontait le texte en haut de l'écran. Le 143 le descend dans la
   bande basse : c'est en haut que tombe la bannière de la notification. Voir
   la règle complète plus bas. */

/* ===========================================================================
   CHANTIER 137 — UNE VRAIE MISE EN PAGE BUREAU ET TABLETTE

   LE DÉFAUT. Sur un Mac, l'onboarding v3 s'affichait en colonne de téléphone
   posée au milieu de l'écran : 412 px pour l'écran de connexion sur une
   fenêtre de 1440, 495 px pour les questions, et 229 px de vide entre le titre
   « Organise ton PVT. » et le premier bouton. Ce n'était pas une régression :
   personne n'avait jamais dessiné de mise en page bureau.

   TROIS PALIERS, PAR LARGEUR DE FENÊTRE ET JAMAIS PAR APPAREIL. Les bornes
   sont écrites à deux autres endroits, et les trois doivent rester d'accord :
   `readMode()` dans glue.js (l'habillage et `data-mode`) et le découpage des
   `@media` dans scripts/build-onboarding-v3.mjs (le CSS des trois démos).

     < 768 px      téléphone — RIEN NE CHANGE ICI, c'est le contrôle n° 1
     768 → 1023    tablette  — une colonne de 620 px, centrée
     ≥ 1024 px     bureau    — deux colonnes sur la connexion, 620 px ailleurs

   AUCUNE COULEUR NOUVELLE. La carte reprend `rgba(255,255,255,.06)`, déjà
   utilisée cinq fois plus haut dans v3.css pour les cartes sur fond sombre ;
   le gris atténué des promesses est `--mute`, soit `rgba(255,255,255,.55)`,
   présent sept fois dans v3.css. Le fond (#111113) et la lueur sont ceux que
   l'écran de connexion porte déjà.

   POURQUOI CES RÈGLES SONT ICI, ET PAS DANS components/auth/entree.css.
   `.whv-a2` est servi à quatre endroits : /auth/login, /auth/sign-up, le
   funnel 103b — tous les trois EN PRODUCTION — et l'onboarding v3. Sa feuille
   est donc intouchable pendant ce chantier. v3.css, elle, n'est chargée que
   par `components/onboarding/v3/FunnelV3.tsx`. Le sélecteur `#app ~ .whv-a2`
   le redit une seconde fois : `#app` est la racine du moteur, et l'écran de
   connexion n'en est le frère que dans le v3.
   =========================================================================== */

/* Les quatre promesses n'existent que dans le v3 (prop `bureau` de
   `EntreeScreen`) et ne s'affichent qu'au palier bureau. En dessous, elles
   sont dans le DOM sans rien peindre : le téléphone ne bouge pas d'un pixel. */
#app ~ .whv-a2 .promesses{display:none}

/* ── 768 px et au-delà : la colonne de 620 px ───────────────────────────── */
@media (min-width:768px){

  /* LE MOTEUR.
     `.stage.one` porte tout ce que le v3 met en page lui-même : les questions,
     les chargements, l'écran IA, les faits, la to-do, le pack, l'écran des
     notifications, l'insight et l'écran sombre du rêve. La démo le posait en
     haut (`align-items:flex-start`) avec 110 px de marge basse : d'où les
     380 px de vide sous le bouton « Continuer » sur une fenêtre de 900. */
  /* 26/10 — LA MOLETTE FAIT DÉFILER LA PAGE.
     La démo pose `overflow:auto` sur `html` ET sur `body`, et le `body` porte
     `overscroll-behavior:none`. La molette visait le `body`, qui n'avait rien
     à faire défiler, et ne pouvait pas passer la main à `html` : un écran plus
     haut que la fenêtre (le pack à 1024×768, la page de fin) restait coupé.
     Un seul défilement, celui de la page. */
  html[data-whv="site"]:not([data-mode="mob"]) body{overflow:visible !important}

  #app .stage.one{
    padding:24px 48px;
    align-items:center;
    /* `safe` évite de couper le haut du contenu quand il dépasse la fenêtre ;
       les moteurs qui ne le connaissent pas gardent la ligne précédente. */
    align-items:safe center;
  }

  /* 620 ÷ 300. La boîte des démos fait 300 px de large ; la démo Mac la zoomait
     à 1,65 (495 px peints) et la démo tablette à 1,9 (570 px). Le zoom la porte
     à la colonne du chantier. Aucune valeur des démos n'est réécrite : tailles
     de texte, boutons, cartes et rayons sont vus à leur taille réelle, comme
     sur téléphone où l'échelle vaut largeur ÷ 270. */
  #app .ph.mob.solo{zoom:2.0667}

  /* 25/10 — LE HALO VERT CENTRÉ (IA, chargements, lecture d'une fiche).
     Ses bulles sont placées à gauche en pixels (`left:20…75px`) pour une
     maquette de 270 px. Ici la scène prend toute la fenêtre : elles restaient
     à gauche, jusqu'à 230 px du milieu à 1440. Même dessin, ancré au milieu :
     chaque bulle garde son écart au centre de la maquette (135 px). */
  #app .v5s .mesh.ctr .m1{left:calc(50% - 115px)}
  #app .v5s .mesh.ctr .m2{left:calc(50% - 95px)}
  #app .v5s .mesh.ctr .m3{left:calc(50% - 86px)}
  #app .v5s .mesh.ctr .m4{left:calc(50% - 55px)}

  /* 25/10 — L'ÉCRAN DE L'IA : LE BOUTON NE PASSE PLUS SUR LES LIGNES.
     Les démos tablette et Mac collent « Continuer » à 110 px du bas et le
     bloc à 120 px du bas, sans rien qui les sépare : une fois le tout zoomé
     (×2,07), le bouton tombait dans la carte des trois lignes (mesuré à
     1440×900 : carte 488→768, bouton 584→675). Ici le bouton suit le bloc,
     24 px dessous, et l'ensemble est centré dans la hauteur. Seul l'écran qui
     a un bouton est concerné : les chargements n'en ont pas. */
  #app .v5s:has(> .cta){display:flex !important;flex-direction:column;align-items:center;justify-content:center}
  #app .v5s:has(> .cta) > .in{position:relative !important;inset:auto !important;margin-left:0 !important;flex:none;padding-top:0 !important;padding-bottom:0 !important}
  #app .v5s:has(> .cta) > .cta{position:relative !important;inset:auto !important;margin:24px 0 0 !important;flex:none}
  /* La même colonne que les questions : 620 px réels de large (300 × 2,0667),
     bouton compris. Les démos lui donnaient 440 × 2,0667 = 909 px — plus
     large que l'écran sur tablette, et 818 px de bouton sur un portable. */
  #app .v5s:has(> .cta) > .in, #app .v5s:has(> .cta) > .cta{zoom:.682}
  /* Le centrage se fait SOUS la rangée du haut (retour + progression),
     76 px réels : la scène est zoomée ×2,0667, d'où 37 px ici. */
  #app .v5s:has(> .cta){padding-top:37px !important;box-sizing:border-box}

  /* LES ÉCRANS ARGENT OCCUPENT TOUTE LA FENÊTRE.
     `#app:has(.agst)>.top` retire déjà l'en-tête sur ces écrans : les 140 px
     que `.agbox` réservait pour lui sont du vide — et ils RÉDUISENT la toile,
     puisque `<ag-screen>` la met à l'échelle avec min(largeur/1440, hauteur/900).
     Mesuré à 1440 × 900 : colonne peinte 506 px au lieu des 600 px que la démo
     Mac dessine. On rend donc la hauteur à la toile. */
  #app .stage.agst{padding:0}
  #app .agbox{min-height:0}
  /* 25/10 — `min-height:0` laissait `.agbox` à 0 px sur ordinateur : là,
     `#app` n'a qu'une hauteur minimale, donc le `height:100%` de la démo ne
     se résout pas. Tous les écrans Argent étaient vides à partir de 1024 px.
     L'étirement de `.stage` (flex, `align-items:stretch`) donne la hauteur. */
  @media (min-width:1024px){ #app .agbox{height:auto} }

  /* L'ÉCRAN DE CONNEXION — une colonne de 620 px au lieu des 460 d'entree.css.
     Les écrans secondaires (« Ton e-mail. », « Se connecter », le code, le
     succès) gardent cette colonne jusqu'au plus large : ils n'ont pas de
     discours à gauche, donc pas de deuxième colonne. */
  /* `width:100%` n'est pas décoratif : `.wrap` est un élément flex de `.scr`
     et `entree.css` lui pose `margin:0 auto`. Des marges automatiques sur
     l'axe transversal annulent l'étirement et font tomber la largeur sur le
     contenu — mesuré 485 px (le titre « Organise ton PVT. » en `nowrap` plus
     ses marges) au lieu des 620 demandés. */
  #app ~ .whv-a2 .wrap{width:100%}

  /* v3.css PORTE UNE RÈGLE `.brand` NON SCOPÉE, et elle atteint l'écran de
     connexion. C'est le logotype de l'en-tête des démos tablette et Mac :
     `gap:9px`, `font-weight:800`, `font-size:16px`, `letter-spacing:-.02em`,
     `flex:none`. `entree.css` ne déclare AUCUNE de ces cinq propriétés sur
     `.whv-a2 .brand` — elles passaient donc telles quelles : 9 px de plus
     entre le logotype et le titre (ce qui décalait le titre par rapport au
     premier bouton) et les quatre promesses rendues en gras.
     On les remet à ce qu'elles valaient sans la fuite. Rien sous 768 px : la
     règle fautive vit dans les blocs tablette et ordinateur, le téléphone ne
     l'a jamais vue. */
  #app ~ .whv-a2 .brand{
    gap:0;
    font-weight:inherit;
    font-size:inherit;
    letter-spacing:inherit;
    flex:0 1 auto;
  }

  #app ~ .whv-a2 .wrap,
  #app ~ .whv-a2 .top,
  #app ~ .whv-a2 .ttl,
  #app ~ .whv-a2 .sub,
  #app ~ .whv-a2 .form,
  #app ~ .whv-a2 .cells,
  #app ~ .whv-a2 .resend,
  #app ~ .whv-a2 .spam,
  #app ~ .whv-a2 .okbas{max-width:620px}
}

/* ── CHANTIER 182 — le format ordinateur du chemin « j'ai déjà un compte » ─
   La règle, son pourquoi et ses mesures sont dans `components/auth/entree.css`
   (bloc 182). Elle est REDITE ici parce que le bloc ci-dessus donne 620 px à
   ces écrans avec un sélecteur d'identifiant (`#app ~ …`, spécificité 1,2,0),
   qui bat celui d'entree.css (0,3,0) : sans ces trois lignes, le v3 garderait
   sa colonne de 620 px et sa flèche au bord de cette colonne.
   Ne concerne que « Ton e-mail. », le code et « Se connecter » : l'entrée
   (deux colonnes du chantier 137) et le succès gardent leurs 620 px. */
@media (min-width:768px){
  #app ~ .whv-a2[data-etape="login"] .top,
  #app ~ .whv-a2[data-etape="mail"] .top,
  #app ~ .whv-a2[data-etape="code"] .top{
    max-width:none;
    margin-left:0;
    margin-right:0;
    padding:calc(24px + env(safe-area-inset-top,0px)) 24px 0 calc(24px + env(safe-area-inset-left,0px));
  }

  #app ~ .whv-a2[data-etape="login"] .ttl,
  #app ~ .whv-a2[data-etape="login"] .sub,
  #app ~ .whv-a2[data-etape="login"] .form,
  #app ~ .whv-a2[data-etape="mail"] .ttl,
  #app ~ .whv-a2[data-etape="mail"] .sub,
  #app ~ .whv-a2[data-etape="mail"] .form,
  #app ~ .whv-a2[data-etape="code"] .ttl,
  #app ~ .whv-a2[data-etape="code"] .sub,
  #app ~ .whv-a2[data-etape="code"] .cells,
  #app ~ .whv-a2[data-etape="code"] .resend,
  #app ~ .whv-a2[data-etape="code"] .spam{max-width:480px}

  #app ~ .whv-a2[data-etape="login"] .glow,
  #app ~ .whv-a2[data-etape="mail"] .glow,
  #app ~ .whv-a2[data-etape="code"] .glow{position:fixed}
}

/* ── 768 → 1023 px : l'entrée en UNE colonne, sans trou au milieu ───────── */
/* `.brand` porte `margin:auto 0` : le bloc marque occupe tout l'espace libre
   au-dessus des boutons, qui restent collés au bas de l'écran. Sur un
   téléphone c'est juste — le pouce atteint les boutons. Sur un iPad de
   1194 px de haut, ça fait 376 px de vide entre le titre et le premier bouton
   (mesuré avant le chantier), soit bien au-delà des 200 px de la consigne.
   On centre donc les deux blocs ensemble. Le palier bureau, lui, a sa propre
   mise en page en grille juste en dessous, et le téléphone ne voit rien de
   tout ça. */
@media (min-width:768px) and (max-width:1023px){
  #app ~ .whv-a2[data-etape="entree"] .wrap{justify-content:center;gap:48px}
  #app ~ .whv-a2[data-etape="entree"] .brand{margin:0}
}

/* ── Les deux boutons de connexion, à toutes les largeurs ───────────────── */
/* Même largeur, même hauteur, même rayon : ils sortent de la même règle
   `.pill` d'entree.css et rien ici ne les sépare. Ce qui les séparait, c'est
   le libellé : « Continuer avec Google » passait sur deux lignes quand
   « Continuer avec un e-mail » tenait sur une, et le bouton grandissait.
   Désormais le texte ne revient jamais à la ligne — c'est la taille de police
   qui baisse, mesurée par `EntreeScreen` (effet `ajuster`, v3 seulement). */
#app ~ .whv-a2 .pill{white-space:nowrap}

/* ── 1024 px et au-delà : la connexion en deux colonnes ─────────────────── */
@media (min-width:1024px){

  /* À gauche le discours, aligné en haut : le logotype, « Organise ton PVT. »
     en grand, puis les quatre promesses. À droite les boutons, dans une carte
     de 400 px. Le bloc entier est centré dans la hauteur de la fenêtre. */
  #app ~ .whv-a2[data-etape="entree"] .wrap{
    max-width:1120px;
    display:grid;
    /* 458 = les 400 px de la colonne de boutons, les 28 px de rembourrage de
       la carte de chaque côté et son filet d'un pixel (`box-sizing:border-box`
       est posé par entree.css sur tout l'écran). Ce sont donc bien LES BOUTONS
       qui font 400 — vérifié par scripts/mesure-137.mjs. */
    grid-template-columns:minmax(0,1fr) 458px;
    column-gap:64px;
    align-content:center;
    align-items:start;
    padding:40px;
  }

  /* `margin:auto 0` centrait le bloc marque dans tout l'espace libre : en
     grille, il doit partir du haut de la rangée. */
  #app ~ .whv-a2[data-etape="entree"] .brand{
    margin:0;
    align-items:flex-start;
    text-align:left;
  }

  #app ~ .whv-a2 .promesses{
    display:flex;
    flex-direction:column;
    gap:10px;
    margin-top:28px;
    list-style:none;
    color:var(--mute);
    font-size:16px;
    line-height:1.45;
  }

  /* LA CARTE. Fond `rgba(255,255,255,.06)` — la valeur des cartes sombres de
     v3.css, donc « légèrement distincte » du fond de page sans être une
     couleur nouvelle. Les mentions légales et « Tu as déjà un compte ? » sont
     DANS la carte : elles sont déjà dans `.cta`, rien à déplacer.

     Le haut : 34 px de logotype + 18 px de marge du titre = 52 px, moins le
     pixel de la bordure de la carte. Le premier bouton commence donc
     exactement à la hauteur du titre de la colonne de gauche — c'est mesuré
     écran par écran par scripts/mesure-137.mjs (`alignementTitreBouton`). */
  #app ~ .whv-a2[data-etape="entree"] .cta{
    background:rgba(255,255,255,.06);
    border:1px solid var(--line);
    border-radius:28px;
    padding:51px 28px 28px;
  }
}

/* Chantier 138 — pendant qu'on interroge Apple, aucun prix faux à l'écran.
   APP SEULEMENT : la classe n'est posée que si le hook `prices` existe, donc
   jamais sur le site. Elle est posée sur la BOÎTE du moteur et non sur `#app`,
   dont React réécrit le `className` à chaque rendu — voir `prixAppliquer`. Le moteur a déjà écrit SES prix (129,99 AUD…) dans ces
   deux nœuds ; les montrer une seconde puis les corriger, sur un écran de
   vente, serait pire que de ne rien montrer. On garde la place exacte — un
   squelette, pas un trou — pour que rien ne saute quand les vrais arrivent. */
#app .whv-prix-attente .opl .pt,
#app .whv-prix-attente .opl .pp{
  color:transparent;
  background:rgba(0,0,0,.06);
  border-radius:6px;
}

/* Chantier 142, point 2 — LES ÉCRANS DE RÉSULTAT NE DÉFILENT PAS, ET N'ONT
   PAS DE RETOUR.
   Ce sont des aboutissements : « Ton objectif réalisable », la page de fin,
   l'écran de l'IA. Tout y tient dans l'écran, il n'y a rien à faire défiler,
   et revenir en arrière n'a pas de sens — le seul retour du v3 est celui de
   la rangée du haut, qui n'existe que sur les questions (chantier 139).
   Le rebond élastique est coupé avec le défilement : sur iOS il donne
   l'impression que l'écran bouge alors qu'il n'a nulle part où aller. */
#app .whv-resultat,
#app .whv-resultat * {
  overscroll-behavior: none;
}
#app .whv-resultat .ph,
#app .whv-resultat .pc,
#app .whv-resultat .osc,
#app .whv-resultat .in,
#app .whv-resultat .in2 {
  overflow: hidden;
}
#app .whv-resultat .pbk,
#app .whv-resultat .bk2,
#app .whv-resultat .back,
#app .whv-resultat .top {
  display: none !important;
}

/* Chantier 143, point 2 — L'ÉCRAN DES NOTIFICATIONS : RIEN SOUS UNE FENÊTRE iOS.
   APP SEULEMENT (`glue.js` ne pose la classe que si APP).

   Trois zones, et une seule est à nous :
     · de 0 à la zone sûre + 110 pt : la BANNIÈRE de la vraie notification y
       tombe. On n'y écrit rien — c'est pour ça que la fausse bannière de la
       démo (`.nban`) disparaît ici : la vraie prend sa place ;
     · le centre, à ±140 pt : les FENÊTRES d'iOS (autorisation, puis avis).
       La fausse fenêtre de la démo (`.palert`) disparaît pour la même raison ;
     · la bande BASSE : le texte de l'écran, et lui seul.

   Le texte ne bouge plus de tout l'écran : `justify-content:flex-end` le pose
   en bas une fois pour toutes, au lieu des ressorts flex de la démo qui le
   déplaçaient au fil des animations. C'est le « rabaisser le texte » de
   Joshua.

   Pourquoi `55%` et pas une valeur calculée : la bande basse commence au
   centre + 140 pt, soit 50 % + 140. Sur les écrans visés (667 à 874 pt de
   haut), une marge haute de 55 % de la hauteur place le bloc sous cette
   limite dans tous les cas, et le `flex-end` le colle au bas. La mesure, elle,
   est faite par `scripts/mesure-notif-143.mjs`, qui refuse tout chevauchement. */
#app .whv-notif-app .nban { display: none }
#app .whv-notif-app .pc {
  justify-content: flex-end;
  padding-top: 55%;
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 28px);
}
#app .whv-notif-app .pfl,
#app .whv-notif-app .pfl.s { flex: 0 0 0 }
#app .whv-notif-app .pht,
#app .whv-notif-app .phb { animation: none !important; transform: none !important }

/* ===== CHANTIER 144, point 4 — UNE SEULE LIGNE DE TITRE, SUR TOUS LES ÉCRANS

   Joshua, sur l'appareil : « L'argent que l'Australie te doit » trop haut, le
   titre du pack des 88 jours ailleurs, et d'autres. Le 143 avait mesuré
   « 0 écart » — sous Chromium, et sur une liste blanche de onze types d'écran
   qui ne contenait justement pas ceux-là.

   LA LIGNE est celle du 134 : zone sûre + 14 + 44 + 12, c'est-à-dire juste
   sous la rangée du haut. `fit()` (glue.js) la publie en unités LOCALES dans
   `--whv-titre`, parce que tout ce qui est dans la boîte est remis à
   l'échelle : un `calc(env(safe-area-inset-top) + 70px)` écrit ici vaudrait
   70 × k pixels réels, soit 104 px sur un iPhone 17 Pro.

   Ici, donc : plus de centrage vertical, plus de ressort au-dessus du titre,
   et la même ligne de départ pour chaque famille d'écran. Aucune correction à
   la mesure, aucun réessai — le code d'ancrage JavaScript du 143 est
   supprimé.

   L'ÉCRAN DES NOTIFICATIONS EST LA SEULE EXCEPTION, et elle est voulue
   (chantier 143, point 2) : son texte vit dans la bande basse, sous la
   bannière et sous la fenêtre du système. Il porte `.whv-notif-app`, et toutes
   les règles ci-dessous l'écartent.

   APP ET TÉLÉPHONE SEULEMENT. Sur le site, et sur tablette et Mac, les trois
   démos restent la référence au pixel : `--whv-titre` n'y est pas publiée et
   ces règles ne s'appliquent pas. */

/* 1. les écrans du gabarit v3 : questions, aha, to-do, guide, pack, offre… */
html[data-whv="app"][data-mode="mob"] #app .box:not(.whv-notif-app):not(.whv-load) > .ph > .pc {
  justify-content: flex-start !important;
  padding-top: var(--whv-titre, 0px) !important;
}
/* Le ressort qui précédait le titre poussait l'écran vers le bas d'une valeur
   qui dépendait du contenu : c'est lui qui faisait sauter les titres d'un
   écran à l'autre. Ceux du BAS restent, ils tiennent le bouton en place. */
html[data-whv="app"][data-mode="mob"] #app .box:not(.whv-notif-app):not(.whv-load) > .ph > .pc > .pfl:first-child,
html[data-whv="app"][data-mode="mob"] #app .box:not(.whv-notif-app):not(.whv-load) > .ph > .pc > .pbk:first-child + .pfl {
  display: none !important;
}

/* 2. l'IA, les chargements et la lecture d'une fiche. Le bloc était centré
      dans la hauteur ; il part maintenant de la même ligne. L'animation du
      logo reste AU-DESSUS de la phrase, comme la démo la compose : c'est le
      bloc qui commence à la ligne commune. */
html[data-whv="app"][data-mode="mob"] #app .box:not(.whv-load) .v5s > .in {
  justify-content: flex-start !important;
  padding-top: var(--whv-titre, 0px) !important;
}

/* 2 bis. CHANTIER 144, SECOND PASSAGE — la phrase AVANT l'animation.

   Au premier passage, j'avais laissé ces deux familles en dehors de la ligne
   commune et je l'avais écrit : sur les chargements et sur l'écran de l'IA,
   l'animation du logo précède la phrase, donc la phrase tombait 183 px plus
   bas (312 px au lieu de 129). Pour qu'elle soit à la ligne il n'y a qu'une
   solution : la faire passer DEVANT l'animation.

   C'est fait ici, et uniquement en ordre d'affichage (`order`) : le document
   n'est pas touché, le texte n'est pas touché, la taille n'est pas touchée,
   et ce qui suit la phrase reste dans le même ordre relatif. L'animation
   descend sous elle.

   Les marges inlines de la démo (22 px au-dessus de la phrase, 10 px au-dessus
   du nom) étaient là pour séparer la phrase de l'animation AU-DESSUS : elles
   passent sur l'animation, qui est maintenant en dessous. L'écart visuel est
   le même, de l'autre côté.

   Si tu préfères l'ancienne composition — l'animation en haut, la phrase
   dessous — c'est ce bloc, et lui seul, qu'il faut retirer. */
/* `.gl + .ti` : seulement les CHARGEMENTS, où la phrase suit l'animation.
   Sur l'écran de l'IA, `.ti` est « Ton analyse est prête. » — une phrase qui
   n'arrive qu'à la fin, invisible au départ. La faire passer devant y plaçait
   une boîte vide de 46 px en haut, et c'est le nom de l'IA qui tombait à
   175 px. Son titre, c'est `.nm` et `.st`, traités juste en dessous. */
html[data-whv="app"][data-mode="mob"] #app .box:not(.whv-load) .v5s > .in > .gl + .ti {
  order: -1;
  margin-top: 0 !important;
}
html[data-whv="app"][data-mode="mob"] #app .box:not(.whv-load) .v5s > .in > .gl {
  margin-top: 22px;
}
/* L'écran de l'IA : le nom et la ligne d'état passent devant, dans le bloc
   centré qui les porte avec l'animation. */
html[data-whv="app"][data-mode="mob"] #app .box .v5s > .in > div:first-child > .nm {
  order: -2;
  margin-top: 0 !important;
}
html[data-whv="app"][data-mode="mob"] #app .box .v5s > .in > div:first-child > .st {
  order: -1;
}
html[data-whv="app"][data-mode="mob"] #app .box .v5s > .in > div:first-child > .gl {
  margin-top: 14px;
}

/* 3. la page de fin et « Ton objectif réalisable » : `.in` y est posée en
      absolu (`inset:42px 18px 20px`), donc c'est son `top` qu'on aligne. */
html[data-whv="app"][data-mode="mob"] #app .box .cgp > .in {
  top: var(--whv-titre, 42px) !important;
}

/* 4. l'offre : elle défile (1 300 px de haut pour un écran de 874), donc on
      aligne le DÉBUT de la page, pas son centre. Son conteneur de défilement
      commence 40 px plus bas (`inset:40px 0 0`, la place de la barre d'état
      de la maquette) : ces 40 px s'ajoutaient à la ligne, soit 52 px réels. */
html[data-whv="app"][data-mode="mob"] #app .box .cgp > .osc {
  top: 0 !important;
}
html[data-whv="app"][data-mode="mob"] #app .box .cgp .in2 {
  padding-top: var(--whv-titre, 6px) !important;
}

/* 5. La marge du premier élément s'ajoutait à la ligne. Celle du titre de
      l'offre (`.ot`, 14 px) le posait 18 px plus bas que partout ailleurs. */
html[data-whv="app"][data-mode="mob"] #app .box .cgp .in2 > .ot,
html[data-whv="app"][data-mode="mob"] #app .box .cgp > .in > .hk {
  margin-top: 0 !important;
}

/* 6. Aucune translation verticale du titre à l'entrée : un fondu, et c'est
      tout. Un titre qui monte de 10 px en arrivant ne « commence » pas à la
      même hauteur pendant les 400 premières millisecondes.
      (Une règle `!important` de la feuille passe DEVANT une animation de
      l'API Web Animations : c'est l'ordre de la cascade. L'opacité, elle,
      continue de s'animer — c'est le fondu qu'on garde.) */
html[data-whv="app"][data-mode="mob"] #app .box .pht,
html[data-whv="app"][data-mode="mob"] #app .box .ti,
html[data-whv="app"][data-mode="mob"] #app .box .fk,
html[data-whv="app"][data-mode="mob"] #app .box .hk,
html[data-whv="app"][data-mode="mob"] #app .box .ot,
html[data-whv="app"][data-mode="mob"] #app .box .okick,
html[data-whv="app"][data-mode="mob"] #app .box .pkick,
html[data-whv="app"][data-mode="mob"] #app .box .phero,
html[data-whv="app"][data-mode="mob"] #app .box .eyeb,
/* L'offre entrait en GLISSANT de 14 px : ce n'est pas le titre qui est animé,
   c'est le panneau entier — et le titre monte avec lui. Mesuré : 135 px à
   100 ms, 129 px à la fin. Il reste le fondu. */
html[data-whv="app"][data-mode="mob"] #app .box .cgp {
  transform: none !important;
}

/* 25/10 — téléphone, site ET app (26/10, accord de Joshua) : une question à
   sept choix (« Tu vises quels jobs ? ») ne tient pas sur un iPhone SE, et
   « Continuer » tombait sous l'écran, inatteignable. L'écran défile alors dans
   sa boîte. Quand tout tient, rien ne bouge : il n'y a rien à faire défiler. */
html[data-mode="mob"] #app .box:not(.whv-resultat) > .ph{overflow-y:auto !important;-webkit-overflow-scrolling:touch}
html[data-mode="mob"] #app .box:not(.whv-resultat) > .ph > .pc{flex:none !important;min-height:100%}

/* 25/10 — iPhone Safari (le SITE, jamais l'app) : le halo vert centré. Les
   bulles 3 et 4 dérivent en moyenne de +6 et −5 px (leurs animations) : on
   les recentre d'autant, en unités de la maquette de 270 px. */
html[data-whv="site"][data-mode="mob"] #app .v5s .mesh.ctr .m3{left:49px}
html[data-whv="site"][data-mode="mob"] #app .v5s .mesh.ctr .m4{left:80px}

/* ===== CHANTIER 149 — LES CHARGEMENTS REVIENNENT AU 053fc67 =================

   Joshua : la boule au milieu de l'écran, à sa taille d'origine, la phrase à
   sa place d'origine. Les chantiers 144 et 145 avaient aligné CES écrans sur
   la ligne commune des titres et fait passer la phrase AU-DESSUS de
   l'animation (`order:-1`). Pour un chargement, ça n'apporte rien : il n'y a
   pas de titre à aligner, et la composition de la démo — animation centrée,
   phrase 22 px dessous — était la bonne.

   Rien n'est réécrit : les quatre règles concernées écartent maintenant
   `.whv-load` (posée par `glue.js`), et l'écran retombe sur la feuille des démos, inchangée depuis le
   053fc67 (`.v5s .in`, `.v5s .gl`, `.v5s .ti`) plus le style inline de
   `V5LOAD` (`justify-content:center`). L'écran de l'IA et la
   lecture d'une fiche gardent la mise en page actuelle.
   ========================================================================= */

html[data-whv="app"][data-mode="mob"] #app .box.whv-load .v5s > .in {
  /* Les 38 px de la démo, plus la zone sûre — le calcul du 143, au pixel. */
  padding-top: calc(38px + var(--whv-sr, 0px)) !important;
}

/* ===== CHANTIER 148, point 2 — « TROP DÉZOOMÉ, LES CHIFFRES PAS ASSEZ GROS »

   La page de fin de l'app est rendue par le module CG, dont la feuille est
   écrite pour la MAQUETTE de téléphone des démos — un téléphone dessiné en
   petit dans une page large. Servie en plein écran, elle garde ses tailles de
   maquette : le titre à 9,5 px, le libellé à 11,5 px, les étapes à 12,5 px.
   C'est ça que Joshua voit, et il a raison.

   Les facteurs sont les siens : libellé et ligne d'épargne +20 %, bloc des
   88 jours et étapes +15 %. Chaque valeur ci-dessous est l'ancienne, relevée
   sur la page, multipliée — rien n'est inventé, et aucune couleur, police ou
   forme ne change.

   Le gros chiffre, lui, n'est plus ici : sa taille est MESURÉE au rendu
   (`WHV_AJUSTE_HERO`), la plus grande qui tient dans sa boîte.

   Les mêmes valeurs sont injectées dans la racine fantôme du module Argent
   (glue.js) : `app.css` ne l'atteint pas, et l'écran de fin d'Australie y
   vit. */
html[data-whv="app"][data-mode="mob"] #app .whv-unite {
  font-size: .5em;
  font-weight: 800;
  letter-spacing: 0;
}
/* Le titre et le libellé du gros chiffre, +20 % (9,5 → 11,4 · 11,5 → 13,8). */
html[data-whv="app"][data-mode="mob"] #app .cgx .hk { font-size: 11.4px }
html[data-whv="app"][data-mode="mob"] #app .cgx .hs { font-size: 13.8px }
/* La ligne « dont X de côté, soit ≈ Y par semaine », sous le libellé. */
html[data-whv="app"][data-mode="mob"] #app .hs.whv-hs2,
html[data-whv="app"][data-mode="mob"] #app .fs.whv-fs2 { margin-top: 4px }
/* Le bloc de récupération et les étapes, +15 % (22 → 25,3 · 10,5 → 12,1 ·
   17 → 19,6 · 9 → 10,4 · 12,5 → 14,4). */
html[data-whv="app"][data-mode="mob"] #app .cgx .blk .rcb b { font-size: 25.3px }
html[data-whv="app"][data-mode="mob"] #app .cgx .blk .rcb span { font-size: 12.1px }
html[data-whv="app"][data-mode="mob"] #app .cgx .pr span { font-size: 19.6px }
html[data-whv="app"][data-mode="mob"] #app .cgx .st .s em { font-size: 10.4px }
html[data-whv="app"][data-mode="mob"] #app .cgx .st .s b { font-size: 14.4px }

/* ===== CHANTIER 157 — la page de fin « Ton objectif », sur iPhone =====
   Joshua, 24/10 : des bords visibles à gauche et à droite du gros chiffre,
   des flashs blancs à l'apparition, un anneau des 88 jours « trop brillant ».
   Cause : le halo du chiffre (`text-shadow` 40 px) est découpé net par le
   calque que crée le flou animé (`filter: blur`) de chaque bloc, d'où un
   rectangle visible ; et l'anneau a une lueur animée (`drop-shadow`).
   On garde le thème à l'identique, sans halo, sans flou, sans lueur.
   (`!important` passe devant les animations de l'API Web Animations.) */
.box.cg .cgp:not(.lt) .hero { text-shadow: none !important }
.box.cg .cgp:not(.lt) [data-s],
.box.cg .cgp:not(.lt) .rgw { filter: none !important }
.box.cg .cgp:not(.lt) .y2b {
  background: rgba(255, 255, 255, .06) !important;
  border-color: rgba(255, 255, 255, .08) !important;
}
.box.cg .cgp:not(.lt) .in > .cta { flex: none }
.box.cg .cgp:not(.lt) .rgf { stroke: rgba(236, 243, 224, .85) !important }
.box.cg .cgp { -webkit-backface-visibility: hidden; backface-visibility: hidden }
