/* =========================================================================
   ROAD TO - feuille de style
   DA SOBRE (V5) : on revient a une direction artistique simple et neutre
   (typo systeme, aplats, coins legerement arrondis, ombres douces).
   La DA "comics" (typo ultra-grasse, ombres dures, banniere inclinee) est
   mise de cote - elle sera retravaillee plus tard.

   Deux familles de couleur :
     --brand*  : ROUGE, pour tout ce qui est GENERAL (titre, HUD, fiche)
     --phase-* : couleur SPECIFIQUE a la phase, definie par phase en JS
     --doom*   : VERT Doomsday, reserve a la mention Doomsday ET a la barre
                 de progression (elle mene a Doomsday)
   ========================================================================= */

:root {
  /* ⚠️ LA RAMPE D ENCRE (V44). Le noir pur n avait jamais ete CHOISI - il
     etait la depuis la V4 - et autour de lui QUATORZE gris ad hoc s etaient
     accumules - 08080C, 101018, 0D0D14, 0B0B11, 16161C, 1C1C23, 23232B,
     2C2C34, 2E2E36, 33333D, 34343E, 3A3A44, 3A3A46, 6A6A76. Ce n etait
     pas une echelle, c etait de l accretion.

     ⚠️ ET ILS DERIVAIENT DEJA TOUS VERS LE BLEU-VIOLET (le bleu depasse le
     rouge de 4 a 8 points, sur les quatorze). La teinte froide n est donc
     pas une lubie de la V44 : elle etait deja la, prise a l oeil, une
     valeur a la fois. La V44 ne fait que l ECRIRE - meme teinte, chroma qui
     monte avec la clarte, HUIT degres au lieu de quatorze accidents.

     ⚠️ LE FOND QUITTE LE NOIR PUR. #000 sur un ecran OLED n est pas un
     fond, c est un TROU : l affiche n a plus de bord, la carte n a plus de
     surface, et il ne reste qu un accent qui pope dans le vide - le cliche
     visuel exact que la V44 vient corriger. --ink-0 est a peine leve,
     assez pour que le noir des affiches se detache du papier. */
  /* ⚠️⚠️ V329 - VISUEL_B (B1). Les CLARTES sont celles de la palette Premium
     (un cran au-dessus du Visuel_A : surfaces plus lisibles, filets plus nets),
     la TEINTE est presque neutre - un souffle froid, jamais le gris-bleu
     ardoise que l auteur a juge « tres IA ». La palette Premium reste dans
     l historique git (commit df99dbb) ; celle du Visuel_A dans le tag `visuel-a`.
     ⚠️ Trois endroits RECOPIENT ces valeurs et bougent avec : FOND_SIGNE
     (js/profile.js), les replis du canvas (js/app.js) et les theme-color. */
  --ink-0:         #0B0B0E;   /* le FOND de page */
  --ink-1:         #101014;   /* un CREUX dans une surface (puits, piste) */
  --ink-2:         #16161B;   /* la surface POSEE (panneau, champ, carte) */
  --ink-3:         #1E1E24;   /* la surface ELEVEE (fiche, popover) */
  --ink-4:         #26262D;   /* filet SOURD */
  --ink-5:         #303038;   /* filet */
  --ink-6:         #404049;   /* filet NET, bord eveille */
  --ink-7:         #75757F;   /* un signe ETEINT (etoile vide, cadenas) */

  /* ⚠️ L ENCRE SUR CLAIR est le fond de page, pas un gris de plus. Une
     pastille blanche ou coloree portait tantot 0B0B0E, tantot 06060A,
     tantot 08080C - trois valeurs pour une seule idee. */
  --on-light:      var(--ink-0);

  --ink:           var(--ink-0);
  --panel:         var(--ink-2);
  --panel-2:       var(--ink-3);
  --rule:          var(--ink-5);
  --paper:         #F6F4EE;

  /* Le texte suit la meme discipline : TROIS degres, la ou six blancs
     casses se disputaient les memes roles - E4E4EA, DCDCE4, D8D8E0, D2D2DC.
     ⚠️ Et PAS de quatrieme. Un "--text-3" pour le texte eteint a ete ecrit
     puis retire dans la meme version : c'etait --ink-7 sous un second nom,
     donc un jeton qui n'aurait servi nulle part. Le degre existe, il
     s'appelle --ink-7, et son commentaire dit deja "un signe ETEINT". */
  --text:          #F4F4F6;   /* ce qu on lit d abord */
  --text-2:        #E0E0E6;   /* la prose, les valeurs */
  --text-dim:      #ABABB5;   /* les intitules, les annees (7,3:1 sur --ink-3) */

  /* Rouge de marque - UN SEUL rouge pour tous les elements generaux */
  --brand:         #E23636;
  --brand-deep:    #7A1418;
  /* ⚠️⚠️ V230 - LE ROUGE SOUS UN TEXTE BLANC. Du blanc sur #E23636 donne
     4,36:1, sous les 4,5 d'un texte en petit corps (S'inscrire, 11 px). Et
     `--brand` ne peut PAS etre fonce pour autant : il sert aussi de TEXTE
     rouge sur le fond sombre, ou il lui faut au moins la meme clarte - les
     deux seuils se croisent, aucun rouge unique ne passe les deux. C'est
     donc un jeton de SURFACE, pas un second rouge d'accent : 4,73:1 sous du
     blanc. Il ne sert qu'aux boutons PLEINS a petit texte ; le grand bouton
     ROAD TO (22-31 px, gras) est un « grand texte » et passe deja a 3:1. */
  --brand-plein:   #D83333;

  /* Vert Doomsday - echantillonne sur le logo officiel (vert pur) */
  --doom:          #00C400;
  --doom-soft:     #6BF06B;
  --doom-deep:     #013A01;
  /* ⚠️ V423 - EXPRESS PREND LA COULEUR DU FILM DE « ROAD TO » (demande de
     l auteur). `--roadto-teinte` est pose par `js/app.js` quand le film choisi
     en a une (Secret Wars) ; sinon, le vert des indispensables. */
  /* ⚠️ V453 - EXPRESS PREND LE VERT DIRECTEMENT, PLUS PAR LE STATUT : il
     disait « le vert de ce qu il garde » quand le statut le plus fort etait
     vert. Le statut devient rouge ; Express, lui, garde le vert de ce qui est
     ACQUIS - c est ce sens-la qu il empruntait, pas le statut. */
  --express:       var(--roadto-teinte, var(--doom));

  /* Etoile de notation et coeur favori */
  --star:          #FFC531;   /* le JAUNE de la notation - c'est le DEFAUT */
  --fav:           #FF4D8D;   /* coeur favori, rose */
  /* V371 - le bouton Progression est DORE, fixe (il ne suit plus le profil).
     ⚠️ V447 - IL RETROUVE UN PORTEUR : « J ai devant moi ». Le bouton
     Progression de la V371 n existe plus, et le jeton etait reste declare sans
     emploi - exactement le code mort que la regle des porteurs interdit. Il
     redit la meme chose : l or, c est le PARCOURS - ce qu on se prepare a
     regarder - la ou `--route-accent` dit AVEC QUI on avance. */
  --or-progression: #FCCF00;

  /* MA NOTE prend la COULEUR DU PROFIL (V63), sur la carte comme dans la
     fiche. C'est la seule note qui appartienne a quelqu'un : les deux autres
     rapportent un chiffre collectif, celle-ci porte une signature - la meme
     que la bulle du HUD.
     ⚠️ ELLE VAUT `--star` TANT QU'IL N'Y A PAS DE PROFIL, et ce repli est ce
     qui evite d'ecrire le jaune une seconde fois en JS : `profile.js` POSE la
     teinte quand un profil existe et RETIRE la propriete quand il n'y en a
     plus. Un litteral recopie dans le script serait le piege nº 6 - deux
     valeurs a tenir d'accord, avec une regle ecrite pour le dire. */
  --star-mine:     var(--star);
  /* ⚠️ V132 (C2b) - LA COULEUR DE L ETOILE DU DUO. Elle vaut le JAUNE par
     defaut, comme celle du profil, et `js/app.js` la POSE sur la racine quand
     un duo existe - puis la RETIRE quand il n y en a plus, au lieu d y
     reecrire le jaune : un litteral recopie serait deux valeurs a tenir
     d accord (piege nº 6). Elle passe par le plancher de contraste (V117). */
  --star-duo:      var(--star);

  /* Echelle d IMPORTANCE des statuts - independante de la phase :
     un "Bonus" reste orange que la fiche soit bleue ou rouge. */
  --tier-must:     var(--doom);   /* Indispensable - vert Doomsday */

  /* ⚠️⚠️ V131 - LES TROIS TEINTES DE LA BARRE HERO PASSENT PAR UN JETON DE
     ROUTE, ET C EST CE QUI EVITE UN CYCLE. La barre est la MEME sur les deux
     routes (decision de l auteur, 2 septembre 2026 : « identique en design,
     sauf la couleur ») : redefinir `--doom` sur la barre l aurait fait se
     referencer elle-meme - une propriete personnalisee circulaire ne rend pas
     du vert, elle rend RIEN, et la barre aurait disparu SANS qu aucune erreur
     ne se produise.

     ⚠️ LA FAMILLE `--doom*` NE BOUGE PAS D UN CHIFFRE : elle continue de
     peindre la mention Doomsday, le statut `--tier-must`, le compte de la
     route et le bouton de portee. Ce qui change de nom, ce sont les TROIS
     endroits qui peignent LA BARRE - et `peindreRoute()` ne pose que
     `--route-accent`, sur la racine ; les deux autres s en derivent en CSS.
     Un litteral recopie en JS aurait ete le piege nº 6. */
  --route-accent:  var(--doom);
  /* ⚠️⚠️ V203 (R1) - LE COULISSANT A SA COULEUR, ET ELLE NE SUIT PLUS LA
     ROUTE. La V192 lui avait donne `--route-accent` avec un argument qui
     paraissait bon - « la couleur de la route qu il synchronise » - et le
     retour le refuse : un interrupteur qui change de teinte quand on repeint
     la barre laisse croire qu il DIT quelque chose de la barre, alors qu il ne
     commande qu un versement.
     ⚠️ C EST UN JETON, PAS DEUX LITTERAUX : la couleur a DEUX porteurs (la
     piste, en `color-mix`, et la bille). Le commentaire de la V192 disait deja
     « un litteral ici aurait ete un second porteur » - il reste vrai, c est sa
     VALEUR qui change (piege nº 6).
     ⚠️ IL VAUT LE VERT DU SITE, qui est deja la teinte de ce qui est ACQUIS
     (`--tier-must`, le compte de la route). Le rouge de marque etait exclu :
     il dit « defaire » sur ce menu meme, deux lignes plus bas (`Rompre le
     duo`). Et `--star-mine` VARIE aussi - on aurait remplace un lien par un
     autre.
     🔴 PRIX NOMME : sur un profil qui n a pas choisi de couleur,
     `--route-accent` VAUT deja `--doom`, donc rien ne change a l oeil pour ces
     comptes-la. Ce n est pas un echec - le defaut etait juste, c est le LIEN
     qui etait faux. */
  --switch-on:     var(--doom);
  --route-soft:    var(--doom-soft);
  --route-deep:    var(--doom-deep);
  /* ⚠️⚠️ V453 - LES TROIS STATUTS ONT DESORMAIS LEURS PROPRES JETONS, ET
     C EST UNE SEPARATION, PAS UN RENOMMAGE (decision de l auteur : rouge,
     ambre, bleu). `--tier-must` disait DEUX choses a la fois depuis la V46 :
     « ce statut est le plus fort » ET « ceci est acquis, fini, vu » - la
     coche d un titre vu, le compte d une route finie, le temps d une route
     bouclee, *Express*. Les deux sens sont restes accordes tant que le statut
     etait vert ; il devient rouge, et ils se separent.
     ⚠️ LA FAMILLE `--tier-*` NE CHANGE PAS D UN CHIFFRE : elle garde le sens
     « acquis » et tous ses porteurs. Ce qui bascule sur les jetons neufs, ce
     sont les SEULS porteurs de STATUT : le fanion, la legende, le mot de la
     fiche, le mot de la carte, les trois pastilles de filtre. La retrouver
     ailleurs, c est le sens « acquis », pas le statut.
     ⚠️ LE ROUGE N EST PAS `--brand`, ET C EST VOULU : le rouge de marque dit
     « tout ce qui est general » (V63) - liens, S inscrire, Rompre le duo. Un
     statut qui l emprunterait ferait dire deux choses au meme rouge. */
  --statut-requis:    #FF5148;   /* le plus fort - rouge, jamais le rouge de marque */
  /* ⚠️⚠️ V459 - L AMBRE DEVIENT UN JAUNE (retour de l auteur : « l ambre
     n est pas assez en contraste avec le rouge »). Et c etait mesurable :
     l ambre etait a 33 deg de teinte, le rouge a 3 - trente degres d ecart
     entre deux oranges, c est ce que l oeil lisait comme « la meme famille ».
     Celui-ci est a 48 deg, et il est moins sature : il s eloigne du rouge sans
     partir dans le fluo (« un jaune pas trop flashi »).
     ⚠️ IL RESTE A DISTANCE DU JAUNE DE LA NOTATION (`--star`, #FFC531, 42 deg
     et pleine saturation) : les etoiles sont franchement plus chaudes et plus
     vives. Les deux se croisent sur une affiche - c est le seul endroit du
     site ou deux jaunes se touchent, et c est pourquoi l ecart est garde. */
  --statut-suggere:   #EFD34C;

  /* ⚠️⚠️ V464 - LE CADRE D UNE AFFICHE N EST PLUS A LA COULEUR DE LA ROUTE
     (retour de l auteur : « ca alourdit le visuel [...] disparaitre ou devenir
     blanc/gris discret »). Il disait « vu » une troisieme fois, apres l image
     qui reprend ses couleurs et la coche verte - et il le disait avec la seule
     couleur que la page emploie deja partout ailleurs, celle de la route.
     ⚠️ DEUX JETONS, PARCE QUE DEUX ETATS : le cadre d une affiche VUE, et
     celui d une affiche SURVOLEE. Le survol est plus clair que l acquis - il
     doit se distinguer de lui sur la meme carte, sinon survoler une affiche
     vue ne produit rien.
     ⚠️ EN PART DE `--paper`, PAS EN BLANC ECRIT : le jour ou le blanc casse du
     site bouge, les cadres suivent. */
  --cadre-vu:      color-mix(in srgb, var(--paper) 45%, transparent);
  --cadre-survol:  color-mix(in srgb, var(--paper) 78%, transparent);

  /* ⚠️⚠️ V480 - `--ruban-l` EST PARTI AVEC LA COLONNE QU IL MESURAIT. Le
     bandeau n est plus une bande verticale POSEE SUR l affiche : c est une
     languette horizontale collee SOUS elle, large comme l image. Il n y a
     donc plus de largeur a tenir d accord entre deux regles - la pile
     d etiquettes a recupere le coin.
     ⚠️ CE QUI RESTE UN JETON, C EST LA HAUTEUR : la languette la declare et
     le voile d angle du « i » la lit pour ne pas deborder dessus. */
  --languette-h:   30px;
  --statut-optionnel: #6FA8FF;   /* bleu : rien d urgent, et aucune parente avec les deux autres */

  /* ⚠️ V305 - le recommande passe de l orange au JAUNE (decision de l auteur :
     vert > jaune > blanc). Un jaune CITRON, pas l or de `--star` (#FFC531) :
     l or dit « ma note » et « meneur », il ne doit pas dire un statut.
     ⚠️ V453 - IL NE DIT PLUS UN STATUT : il garde ses autres porteurs (le
     signalement, « a revoir », le titre qui deborde de la plage horaire). */
  --tier-reco:     #FFE14D;       /* Recommande - jaune */
  --tier-bonus:    #FFFFFF;       /* Bonus - blanc casse. Le bonus ne se
                                     distingue plus par une teinte de plus mais
                                     par l'absence de teinte : il n'appartient
                                     a aucun degre d'urgence. */
  /* ⚠️⚠️ V153 - LES TROIS MEDAILLES DU PODIUM SONT DES JETONS, PLUS DES
     LITTERAUX. Elles etaient ecrites DEUX fois chacune (la marche et le
     numero de rang), et la V153 leur ajoute un troisieme porteur : le carre
     de rang du menu de classement. Trois valeurs x trois porteurs, c etaient
     neuf litteraux avec une regle ecrite « les trois doivent rester
     egales » - le piege nº 6 mot pour mot, sur des couleurs que personne ne
     verrait diverger.
     ⚠️ Ce ne sont PAS un quinzieme, seizieme et dix-septieme gris : elles ne
     sont pas neutres, elles nomment un metal. La rampe d encre n est pas
     touchee. */
  --medal-or:      #E8B923;
  --medal-argent:  #C8CBD4;
  --medal-bronze:  #B0793A;

  /* ⚠️ V115 - « --tier-free » EST PARTI AVEC SON STATUT. Il peignait le mot
     « Hors canon », qui ne designe plus aucune fiche depuis la V40 : un jeton
     sans porteur est du code mort (piege nº 4), et c'est le geste que la V59 a
     fait pour la gelule et la V97 pour « .archive-note ». */

  /* Accent de phase - surcharge par phase via JS */
  --phase-accent:      #1E9BFF;
  --phase-accent-soft: #33C4FF;
  /* ⚠️ V136 - « --phase-tesseract » EST PARTI. Il etait POSE par app.js sur
     la section de route ET sur le panneau de fiche, et LU par personne : les
     liaisons qui s en servaient sont parties en V48. Un jeton pose et jamais
     relu donne au lecteur la preuve d un mecanisme qui n existe plus - piege
     nº 4, meme geste que « --tier-free » en V115. */

  /* Echelle : tout l'espacement derive de --poster-w (proportionnel) */
  --poster-w:      clamp(122px, 10.8vw, 152px);
  --poster-ratio:  2 / 3;
  --title-fs:      clamp(.92rem, 1.15vw, 1.06rem);
  --gap:           calc(var(--poster-w) * 0.12);
  --mf-pad:        13px;                        /* padding du cadre du jalon */

  /* ⚠️⚠️ V296 - L ECHELLE TYPOGRAPHIQUE UNIQUE (point [46] d UPDATEV3).
     Cent quatre-vingt-cinq « font-size » en rem litteral portaient QUARANTE
     valeurs differentes (.5, .55, .58, .6, .62, .64, .66, .67, .68, .7...),
     et VINGT-DEUX etaient SOUS le plancher de 10,5 px de la V230 - la regle
     etait ecrite, le fichier ne la tenait pas. Huit marches, pas une de plus :
     une valeur neuve se choisit ICI, jamais en litteral.
     ⚠️ LES DEUX PLANCHERS SONT DES MARCHES (V230, V279) : « micro » = 10,56 px,
     le plus petit corps d une ETIQUETTE qui ne se clique pas ; « xs » =
     11,04 px (.69rem, celle de `.footer-nav a` en V279), le plus petit corps d un texte FONCTIONNEL (lien, bouton, pli).
     Un plancher se franchit, il ne se frole pas.
     ⚠️ CE QUI RESTE HORS ECHELLE, ET POURQUOI : les titres fluides en clamp()
     (ils suivent la largeur, une marche fixe les figerait), les « em » (ils
     sont RELATIFS a leur parent - le sous-titre de carte suit fitTitles), et
     l impression en pt (une autre surface). */
  --fs-micro:      .66rem;   /* 10,56 px - etiquette, plancher V230 */
  --fs-xs:         .69rem;   /* 11,04 px - fonctionnel, la valeur exacte de la V279 */
  --fs-s:          .78rem;   /* 12,48 px */
  --fs-m:          .86rem;   /* 13,76 px */
  --fs-base:       .95rem;   /* 15,2 px */
  --fs-l:          1.05rem;  /* 16,8 px */
  --fs-xl:         1.3rem;   /* 20,8 px */
  --fs-2xl:        1.5rem;   /* 24 px */
  /* ⚠️⚠️ V497 - LA TAILLE D UNE NOTE POSEE SUR UNE CARTE, ET IL N Y EN A
     QU UNE (retour de l auteur : « les notes communautaires et personnelles
     doivent etre de la meme taille de police que pour les duos ; idem pour le
     bandeau dans groupe du coup »). Elles etaient DEUX : la languette a
     `--fs-micro` depuis la V488, le bandeau du duo et celui du groupe a
     `--fs-s`. Un jeton, trois lecteurs - « idem pour le groupe » devient un
     fait de structure et non une ligne de plus a tenir d accord.
     ⚠️ SA VALEUR EST CELLE DU DUO, parce que c est le duo qui sert de
     reference dans le retour. C est donc un RENVERSEMENT partiel de la V488,
     qui avait descendu la languette « de 1 ou 2 tailles » sur ma proposition :
     l auteur tranche autrement, et l egalite des deux notes - le vrai correctif
     de la V488 - ne bouge pas d un mot. */
  --fs-note-carte: var(--fs-s);
  /* ⚠️⚠️ V497 - LA PART DU CADRE QU OCCUPE LE DESSIN DE L ETOILE. Mesure par
     `getBBox()` sur le trace lui-meme : 18,17 unites de haut dans un cadre de
     24, soit 0,757. Ce n est pas un reglage, c est un FAIT sur le dessin - et
     c est ce qui permet de demander « une etoile aussi haute que les
     chiffres » sans ecrire une taille en pixels. Le jour ou le trace change,
     cette ligne change avec lui, et les trois tailles d etoile suivent. */
  --etoile-cadre:  .757;

  /* ⚠️⚠️ V296 - L ECHELLE D ESPACEMENT, ET CE QU ELLE NE TOUCHE PAS.
     La V44 avait refuse de normaliser l espacement parce que « la moitie des
     valeurs ne sont pas du style mais des MESURES PORTANTES ». Elles sont
     nommees ici pour qu on ne les confonde plus :
       PORTANTES (ne passent JAMAIS par l echelle) - --poster-w, --gap,
       --mf-pad, --mf-off, --enc-off, --title-fs et la boite du titre
       (calc(--title-fs * 2.6)), --pastille, --bar-h, --roadto-drop,
       --head-h / --warn-h / --consent-h (mesurees).
       DE STYLE - tout le reste, et c est a elles que l echelle s adresse.
     ⚠️ Elle est APPLIQUEE aux ecarts de rangee (« gap ») en rem litteral ; les
     marges et remplissages y viendront au fil des retouches, jamais par un
     remplacement de masse sur 8 000 lignes (la faute de la V194). */
  --sp-1:          .25rem;
  --sp-2:          .45rem;
  --sp-3:          .7rem;
  --sp-4:          1rem;
  --sp-5:          1.5rem;
  /* V424 - l embleme d un ruban (duo ET groupe) : 36 px, 46 des 1200 px */
  --ruban-emb:     36px;
  --ruban-ecart:   var(--sp-5);   /* V424 - l ecart entre deux cotes d un ruban */

  /* ⚠️ LA COQUILLE (V42, coupee en DEUX BORNES en V76). Une seule variable
     pour la gouttiere du site : le HUD, la barre d outils, les bandeaux de
     route, les grilles, le pied de page. Elle etait ecrite en dur -
     « min(1320px, 100% - 1.75rem) » - a DEUX endroits (.wrap et .phase-header),
     et une regle disait qu ils devaient rester egaux. Une regle ecrite est le
     symptome d une variable manquante.
     ⚠️ 1680 et non 1320 : le reproche principal etait « trop centre ». La
     rangee, elle, prend toute la fenetre depuis la V11 - c est le PLAFOND de
     cartes par ligne qui la retenait au centre, pas la coquille.

     ⚠️⚠️ V76 - LE PLAFOND DE 1680 px TOMBE, REMPLACE PAR DEUX BORNES.
     Au-dela de 1720 px de fenetre, tout le surplus devenait de la MARGE : deux
     colonnes vides sur les grands ecrans. Monter le plafond (2100 ? 2560 ?)
     n aurait fait que repousser le meme probleme d un cran. Une seule borne ne
     pouvait pas servir les deux besoins : une SCENE (des cartes, des cases,
     des barres - ca s etale) et une COLONNE DE LECTURE (des phrases - ca ne
     s etale pas). Il en faut donc deux, et chaque bloc declare laquelle il
     prend.

     ⚠️ LA GOUTTIERE NE BOUGE PAS (2,5 rem), ET C EST CE QUI REND LE
     CHANGEMENT SUR : en dessous de 1720 px de fenetre, le « min() » retenait
     DEJA « 100% - 2.5rem » - la coquille y vaut donc exactement ce qu elle
     valait, au pixel. Toutes les mesures du projet (1680, 1280, 820, 375,
     320 px) sont inchangees PAR CONSTRUCTION ; seul ce qui est plus large que
     1720 px change. */
  --shell:         calc(100% - 2.5rem);

  /* ⚠️ LA COLONNE DE LECTURE. C est l ANCIENNE valeur de « --shell », gardee
     telle quelle : les bandeaux de prose (consentement, avertissement
     « sans compte », pied de page) ne gagnent rien a s etaler, et une ligne de
     2 400 px ne se lit pas. La resserrer davantage - 72rem, 90ch - serait une
     decision TYPOGRAPHIQUE, donc son propre increment : ici on CESSE de la
     faire grandir, on ne la retrecit pas. */
  --lisible:       min(1680px, 100% - 2.5rem);
  --mf-off:        calc(-2px - var(--mf-pad));  /* recalage vertical du cadre */

  /* Les DEUX bandes figees, mesurees en JS (voir mesureSticky dans app.js).
     Ces valeurs ne sont qu'un repli avant le premier calcul.
     ⚠️ ELLES ETAIENT TROIS JUSQU'A LA V77. Le HUD et la barre d'outils se
     figeaient l'un sous l'autre ; ils ont fusionne en une seule ligne - les
     onglets, "Filtres" et la recherche, le profil - et "--tools-h" a disparu
     avec la bande qu'elle mesurait. ⚠️ ELLE N'A PAS ETE REMPLACEE PAR UN
     LITTERAL : c'est le piege nº 6, et la V43 a passe une version a le
     retirer. Ce qui reste s'empile DANS L'ORDRE DU FLUX - l'en-tete, puis
     l'avertissement "sans compte" - chacune sur la SOMME de la precedente.
     ⚠️ "--hud-h" EST DEVENUE "--head-h" : le HUD n'existe plus, et un nom qui
     designe un mecanisme disparu est une fausse piste laissee au prochain
     lecteur (regle V59). */
  --head-h:        52px;   /* V77 : la ligne fusionnee, une seule rangee */
  --warn-h:        0px;    /* l'avertissement est masque par defaut */
  /* ⚠️ V145 - LA HAUTEUR DE LA BANNIERE DE CONSENTEMENT. Elle ne sert qu a
     l ecran d accueil, qui s ouvre desormais DESSOUS : c est sa reserve de
     bas de page. 0 des qu on a repondu, c est-a-dire presque toujours. */
  --consent-h:     0px;

  /* V407 - la capsule Snapline tient DANS la bande figee (45 px) : plus rien
     ne pend sous elle. `--roadto-drop` et `--roadto-grow` sont partis avec le
     debordement qu ils mesuraient ; `--roadto-air` reste, c est l air. */

  /* ⚠⚠ V205 - L AIR SOUS LA BANDE, ET CE N EST PAS `--roadto-drop`.
     Le debordement dit OU FINIT LE BOUTON ; cette valeur dit COMBIEN DE
     VIDE le suit. Les additionner dans un seul jeton aurait pousse
     l avertissement « sans compte » et le menu Progression de 14 px, pour
     un ecart qui ne les concerne pas : ils se calent sur le BAS DU BOUTON,
     jamais sur la respiration qui vient apres.
     ⚠ ELLE VAUT AUX DEUX REGIMES. Sous 1200 px le bouton est dans le flux
     et la bande le contient, mais l ecart entre la bande et la barre hero
     est la meme question - « ni trop compact, ni trop vide ». */
  /* ⚠️ V458 - 14 px -> 9 px (retour de l auteur : « reduis les espaces vides,
     header | road to doomsday »). C est le seul endroit a changer : la bande
     le pose en marge, et les deux coins s en deduisent. */
  --roadto-air:    9px;

  /* ⚠️⚠️ V458 - L ECART DES DEUX COINS A LA BANDE, ET C EST UN JETON PARCE QUE
     DEUX ELEMENTS LE PARTAGENT : les trois routes a gauche (V455) et
     Synchroniser a droite (V448). Tous deux remontent dans l air de la bande,
     et l ecart qui reste vaut `--coin-air` plus le padding de la zone. Un
     nombre recopie dans les deux regles aurait derive des la premiere retouche
     (piege nº 6). « Un leger espace, mais pas trop » : l effet de coin tient a
     ce que l ecart soit visible sans etre un vide. */
  --coin-air:      4px;

  /* ⚠️ LES RAYONS DEVIENNENT UNE ECHELLE, ET SE RESSERRENT (V44).
     Cinq valeurs cohabitaient - 8, 12, 14, 4, 6 - plus quelques 3 et 5
     glisses au fil des versions. Trois degres suffisent, et ils disent
     quelque chose : plus un objet FLOTTE, plus son rayon est grand.

     ⚠️ ET LE RAYON DE BASE PASSE DE 8 A 5. Huit pixels sur tout ce qui
     traine, c est la signature "rounded-lg" du gabarit. Surtout, la
     grammaire du site est faite de CADRES et d AFFICHES - et une affiche
     est un rectangle net. Un panneau moins arrondi se lit comme un
     imprime, pas comme une application.

     ⚠️ 50% n est PAS dans l echelle, et les 1-2px des barres du signe
     "+/-" non plus : ce ne sont pas des reglages, ce sont des faits
     geometriques - un cercle, et le bout arrondi d un trait de 2px
     d epaisseur. Les tokeniser les rendrait solidaires d une echelle de
     panneaux avec laquelle ils n ont rien a voir. */
  /* ⚠️⚠️ V493 - LES QUATRE RAYONS DE SURFACE TOMBENT A ZERO (retour de
     l auteur : « applique sur de nombreux elements le format rectangle/carre
     plutot que bords arrondis »). C est la suite directe de la V479, qui
     l avait fait pour les seules affiches et ecrivait deja l argument : « la
     grammaire du site est faite de CADRES et d AFFICHES - et une affiche est
     un rectangle net ». Le retour l etend aux surfaces.
     ⚠️⚠️ UNE LIGNE PAR JETON, ET RIEN D AUTRE N EST TOUCHE : 107 regles
     changent de forme sans qu une seule d entre elles soit rouverte. C est
     tout l interet d une echelle - et c est aussi ce qui rend le retour en
     arriere possible en quatre chiffres.
     ⚠️ LES TROIS NOMS RESTENT, BIEN QU ILS VAILLENT LA MEME CHOSE. Ils ne
     disent pas un nombre, ils disent un RANG - ce qui est pose, ce qui
     flotte, ce qui est une vignette. Les fondre maintenant qu ils sont egaux
     rendrait impossible de les redifferencier plus tard sans relire 107
     regles.
     ⚠️⚠️ `--r-pill` NE BOUGE PAS, ET C EST LA BORNE DU RETOUR (« pas sur TOUS
     les elements du site mais sur certains oui », V479). Une pastille, une
     puce de filtre, la barre « Annuler » ne sont pas des surfaces aux coins
     adoucis : ce sont des objets dont la FORME est un batonnet. Les mettre au
     carre ne serait pas retirer un arrondi, ce serait changer l objet.
     ⚠️ `50%` NE BOUGE PAS NON PLUS, pour la meme raison qu a la V331 : un
     cercle est un fait geometrique, pas un reglage. */
  --r-1:           0;      /* V493 - vignettes, focus, petites pastilles */
  --r-2:           0;      /* V493 - ce qui est POSE : cartes, champs, boutons */
  --r-3:           0;      /* V493 - ce qui FLOTTE : fiche, popover, cadre de jalon */
  --r-bande:       0;      /* V493 - le bandeau de route */
  /* ⚠️ V479 - LE RAYON DES AFFICHES, ET IL VAUT ZERO (retour de l auteur :
     « faire un test que les affiches soient dans un rectangle... je crois que
     c est vraiment ca le AI slop »). Une affiche de cinema est un rectangle :
     elle est imprimee, pliee, glissee dans un cadre, et aucun de ces objets
     n a de coin rond. Les trois surfaces qui en portent une empruntaient
     `--r-2`, le rayon des BOUTONS - une commande et une image n ont pourtant
     pas la meme nature.
     ⚠️ UN JETON, PAS TROIS ZEROS RECOPIES : le jour ou l auteur veut 2 px,
     une ligne change, pas trois.
     ⚠️ ET LA PORTEE EST BORNEE PAR LE RETOUR LUI-MEME : « pas sur TOUS les
     elements du site, mais sur certains oui ». Les cartes, les champs et les
     fenetres gardent leur rayon. */
  --r-affiche:     0;      /* V479 - la grille, la fiche, la fenetre de note */
  --r-pill:        999px;

  /* ⚠️ V201 - LA PASTILLE DE COULEUR, SA ROUE, SA BARRE, ET LES DEUX
     RENTREES QUI LES ALIGNENT : cinq lecteurs pour une seule valeur, qui
     etait ecrite cinq fois a la main (piege nº 6). Elle vaut 34 px partout,
     et 42 px dans `.sheet`, qui a grandi.  */
  --pastille:      34px;
  /* V432 - LA LARGEUR DE LA BARRE ET DE SES DEUX LIGNES (demande de l auteur :
     « trop large »). Une valeur, trois lecteurs : la barre, la ligne du dessous
     et la tete de la zone Parcours sous 1200 px. */
  /* V434 - elle suit la fenetre (plus large, demande de l auteur), toujours
     centree : 52rem au moins, 80rem au plus. */
  --barre-l:       clamp(52rem, 66vw, 80rem);
  /* V431 - les jetons du flanc gauche (`--flanc-*`) sont partis avec le menu
     Parcours, leur dernier porteur. */
  /* V408 - `--decompte-h` et `--pend-h` (V364) sont partis : le compte a
     rebours et le menu Parcours sont descendus dans la zone Parcours, dans le
     flux. Plus rien ne pend sous la bande figee. */

  /* ⚠️ V92 - LE RYTHME D'UNE SECTION D'ONGLET, ECRIT UNE SEULE FOIS.
     Cette valeur etait recopiee a l'identique sur `.roadmap`, `.archive`,
     `.favs`, `.amis-tab` et `.account-tab` ; aligner `.soon` dessus en aurait
     fait une SIXIEME copie, avec une regle en commentaire pour dire qu'elles
     doivent rester egales - c'est le piege nº 6 mot pour mot. Un jeton qui
     porte un raccourci entier suit la maison (`--frame-dim`). */
  --sect-pad:      clamp(1.5rem, 3vw, 2.25rem) 0 clamp(2rem, 5vw, 3.5rem);

  /* ⚠️ V97 (D1) - LA RESPIRATION SOUS LA TETE D UNE SECTION D ONGLET.
     Elle etait portee par la DESCRIPTION, qui vient d etre retiree de quatre
     ecrans : sans elle, le titre de `Amis`, de `Duo` et de `Mes favoris`
     tombait a .5rem au-dessus de son contenu. La valeur passe donc sur le
     TITRE - mais `Mon compte` garde sa description, donc la valeur existe
     desormais a DEUX endroits et devait rester egale : c est le piege nº 6
     mot pour mot, et un jeton est sa reponse.
     ⚠️ V98 - CINQ PORTEURS DEPUIS QUE LA SECTION EQUIPES EXISTE. */
  --tete-gap:      clamp(1.4rem, 3vw, 2.4rem);

  --frame:         1px solid var(--ink-6);
  --frame-dim:     1px solid var(--ink-5);

  /* ⚠️ L OMBRE DOUCE GENERIQUE ETAIT INERTE, et c est la vraie trouvaille
     de la V44. "0 2px 6px rgba(0,0,0,.6)" pose du NOIR A 60% sur un fond
     NOIR : elle ne rend litteralement aucun pixel de difference. Le site
     portait donc une elevation qui n existait pas.

     Sur fond sombre, la lumiere ne vient pas d en haut, elle vient de la
     SURFACE : un objet monte en s eclaircissant (la rampe d encre) et en
     attrapant la lumiere sur son arete haute (le filet interieur). Ce
     n est pas un effet, c est la seule elevation qui se VOIE ici.

     ⚠️ L ombre portee ne disparait pas - elle redevient ce qu elle aurait
     toujours du etre : reservee a ce qui flotte VRAIMENT au-dessus du
     reste, et alors profonde et large, jamais 2px/6px. */
  --lift-0:        inset 0 1px 0 rgba(255, 255, 255, .045);
  --lift-1:        0 10px 26px -12px rgba(0, 0, 0, .9),
                   inset 0 1px 0 rgba(255, 255, 255, .055);
  --lift-2:        0 28px 70px -18px rgba(0, 0, 0, .95),
                   inset 0 1px 0 rgba(255, 255, 255, .07);

  /* ======================= L APPARIEMENT (V44) =======================
     ⚠️ --display ET --body ETAIENT LA MEME VALEUR. Une seule grotesque
     faisait tout : les boutons, les intitules, les titres de film ET les
     synopsis. Ce n etait pas un appariement typographique, c etait une
     police unique avec deux noms de variable - et c est la raison
     principale pour laquelle le site "sonnait" gabarit.

     LA REGLE, ET ELLE TIENT EN UNE LIGNE :

       ARCHIVO garde tout ce qui se CLIQUE ou se REPERE.
       LITERATA prend tout ce qui se LIT EN PHRASES.

     Ce n est pas un partage decoratif, c est un partage de FONCTION - et
     c est pourquoi il ne se discute pas au cas par cas : un intitule, une
     valeur, un statut, un titre de carte se REPERENT du coin de l oeil ;
     un synopsis, un resume de spoiler, une page de CGU se LISENT ligne a
     ligne. Deux gestes differents, deux voix.

     ⚠️ ET LES TITRES DE CARTE RESTENT ARCHIVO, PAS PAR GOUT MAIS PAR
     MECANIQUE : fitTitles() resserre la CHASSE (font-stretch) avant de
     reduire le corps, et c est l axe wdth d Archivo qui rend ce recours
     possible. Literata n a pas d axe de chasse - poser un titre de carte
     dessus casserait le seul mecanisme qui empeche les titres de deborder
     sur leurs voisins.

     ⚠️ ORBITRON NE BOUGE PAS D UN ELEMENT. La tentation etait de la
     promouvoir en voix d affichage puisqu elle etait deja la : c est
     exactement ce que CLAUDE.md interdit depuis la V14, et il a raison -
     une geometrique de ce genre est illisible en petit corps et perd tout
     effet des qu elle est repetee. Elle sert UN nombre. Un appariement se
     fait avec une police faite pour LIRE, pas avec celle qu on a sous la
     main. */

  /* Literata (SIL OFL 1.1, TypeTogether), variable 400-600, sous-ensemble
     latin, 86 Ko, SERVIE EN LOCAL comme les deux autres - aucun CDN, le
     hors-ligne reste reel et sw.js la precache.
     ⚠️ Pourquoi celle-la, et le test qui a tranche : sur fond sombre les
     serifs a fort contraste MAIGRISSENT - les deliees disparaissent et le
     texte devient gris. Cinq candidates ont ete rendues cote a cote sur
     --ink-0, au corps REEL du synopsis (.95rem) et non en specimen :
     Petrona et Faustina, plus etroites et de hauteur d x plus basse,
     tombaient d un ton sous Archivo ; Newsreader tenait mais coutait
     132 Ko. Literata a la plus grande hauteur d x et le plus faible
     contraste du lot - elle garde sa couleur sur le noir. */
  /* ⚠️⚠️ V330 - VISUEL_B (B3) : QUATRE POLICES. Ce qui precede decrit le
     Visuel_A (tag `visuel-a`) et reste vrai sur son partage de FONCTION :
     une voix pour ce qui se repere, une pour ce qui se lit. Les voix changent :
       SAIRA (--display, --body) : tout ce qui se clique ou se repere. Elle a
         un axe de chasse (wdth 50-125), donc fitTitles garde son recours.
       SPECTRAL (--read) : ce qui se lit en phrases. Quatre fichiers statiques
         (400, 500, et leurs italiques - un VRAI italique, regle V44).
       ARCHIVO reste pour la BANDE FIGEE seule : Saira a des jambages hauts,
         la bande montait de 45 a 57 px (mesure de l essai).
       ORBITRON reste le pourcentage, et lui seul.
     Encode Sans (essai) et Literata (Visuel_A) partent. */
  --read:    "Spectral", Georgia, "Times New Roman", serif;

  /* Une SEULE exception a Archivo : le pourcentage de progression. Orbitron
     (SIL OFL 1.1, 11,8 Ko) est une geometrique spatiale - elle ne sert QU A
     ce nombre, qui est la promesse de Doomsday. Nulle part ailleurs. */
  --cosmic:  "Orbitron", "Archivo", system-ui, sans-serif;

  --display: "Saira", "Archivo", "Segoe UI", system-ui, -apple-system, Helvetica, Arial, sans-serif;
  --body:    "Saira", "Archivo", "Segoe UI", system-ui, -apple-system, Helvetica, Arial, sans-serif;
  /* La voix de la bande figee (V330) : Archivo, pour tenir --head-h a 45 px. */
  --bande:   "Archivo", "Segoe UI", system-ui, -apple-system, Helvetica, Arial, sans-serif;

  /* ⚠️⚠️ V506 - L ECHELLE DE CHASSE, ET C EST L HISTOIRE DE LA V44 SUR L AUTRE
     AXE. La rampe d encre existe parce que quatorze gris ad hoc s etaient
     accumules ; l axe de CHASSE de Saira en etait au meme point - ONZE valeurs,
     dont cinq tassees entre 108 et 122 pour 99 emplois. Ce n etait pas une
     echelle, c etait de l accretion : `.card-title` portait 112 dans une regle
     et 118 dans une autre, `.route-opt` faisait 110 au repos et 116 a l etat
     courant, sans qu une seule ligne dise que c etaient deux marches.

     ⚠️ SIX MARCHES, ET CHACUNE REPOND A UNE QUESTION - c est ce qui la rend
     relisable. Trois etaient DEJA nommees dans DESIGN.md (route 58, etiquette
     80, affichage 118) sans exister nulle part comme jeton ; les trois autres
     sont des roles qu on lisait dans le code sans pouvoir les nommer.

     ⚠️ DEUX REGROUPEMENTS, ET DEUX SEULEMENT : 108 et 110 rejoignent 112
     (c est la valeur DOMINANTE des trois - 46 emplois contre 19 et 12, donc
     celle qui deplace le moins), 116 rejoint 118. Tout le reste est un
     renommage a l identique. Les 31 emplois qui bougent gagnent au plus 4
     points de chasse, mesures au DOM : aucun debordement horizontal.

     ⚠️ LE `font-stretch: 62% 125%` DES @font-face N EST PAS DE CETTE ECHELLE
     et n y entre pas : c est un DESCRIPTEUR - il declare la plage que le
     fichier sait couvrir, pas un style. */
  --ch-route:     58%;    /* les titres de route - la signature de la page */
  --ch-etiquette: 80%;    /* les etiquettes : annee d une carte, intitule d une stat */
  --ch-titre:     100%;   /* les titres de FILM, casse normale - le contraste
                             avec --ch-route est ce qui hierarchise la page */
  --ch-ui:        112%;   /* ce qui se clique ou se repere : onglets, boutons, menus */
  --ch-affiche:   118%;   /* l affichage : la capsule Snapline, les grands mots */
  --ch-chiffre:   122%;   /* un CHIFFRE qui est le sujet : une note, un rang, un
                             pourcentage de profil. Le role que le code avait
                             deja - neuf porteurs - et qu il ne savait pas dire. */
}

/* ⚠️ V330 - LA BANDE FIGEE PARLE ARCHIVO : elle redefinit les deux voix pour
   tout ce qu elle contient (boutons, champ, menus deroulants compris). */
.topbar { --display: var(--bande); --body: var(--bande); font-family: var(--bande); }

/* Archivo variable, servie EN LOCAL (aucun CDN, le hors-ligne reste reel).
   SIL Open Font License 1.1 - voir assets/fonts/LICENSE.txt */
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 400 800;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url("../assets/fonts/archivo-var.woff2") format("woff2");
}

/* V330 - Saira variable (chasse 50-125 %, graisse 100-900), SIL OFL 1.1,
   sous-ensemble latin, 99 Ko, servie EN LOCAL. */
@font-face {
  font-family: "Saira";
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 50% 125%;
  font-display: swap;
  src: url("../assets/fonts/saira-var.woff2") format("woff2");
}

/* V330 - Spectral, SIL OFL 1.1, sous-ensemble latin : quatre fichiers
   statiques de 22 a 24 Ko (91 Ko en tout, contre 175 pour Literata).
   ⚠️ La regle de l italique ci-dessous (ecrite pour Literata) vaut pour elle :
   L ITALIQUE EST UN VRAI FICHIER, PAS UNE OBLIQUE SYNTHETIQUE. La
   premiere version de la V44 ne chargeait que le romain, au motif que
   89 Ko pour quelques <em> etait cher. Rendue, la page "A propos" montrait
   NEUF italiques - les titres de film s ecrivent en italique, c est une
   convention d edition et ces pages sont de l edition - tous PENCHES PAR
   LE MOTEUR, c est-a-dire un romain incline a la volee. Sur une grotesque
   ca passe inapercu ; sur un serif, dont l italique est un dessin a part
   entiere (le a devient rond, le f descend), l oblique synthetique est
   exactement le defaut "herite par defaut" que la V44 vient corriger.
   ⚠️ Et l argument du poids ne tient pas a l examen : le service worker
   precache DEJA soixante-seize affiches. 89 Ko, c est deux affiches. */
@font-face {
  font-family: "Spectral"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../assets/fonts/spectral-400.woff2") format("woff2");
}
@font-face {
  font-family: "Spectral"; font-style: normal; font-weight: 500 700; font-display: swap;
  src: url("../assets/fonts/spectral-500.woff2") format("woff2");
}
@font-face {
  font-family: "Spectral"; font-style: italic; font-weight: 400; font-display: swap;
  src: url("../assets/fonts/spectral-italic-400.woff2") format("woff2");
}
@font-face {
  font-family: "Spectral"; font-style: italic; font-weight: 500 700; font-display: swap;
  src: url("../assets/fonts/spectral-italic-500.woff2") format("woff2");
}

/* Orbitron variable, SIL OFL 1.1 - servie en local elle aussi (aucun CDN).
   Reservee au POURCENTAGE de progression : la seule exception a Archivo. */
@font-face {
  font-family: "Orbitron";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("../assets/fonts/orbitron-var.woff2") format("woff2");
}

* { box-sizing: border-box; }

/* =========================================================================
   [hidden] BAT TOUTE CLASSE QUI POSE UN display. Regle de reset, unique
   !important du site.

   Le piege : la feuille du navigateur declare [hidden] { display: none },
   mais c'est un selecteur d'ATTRIBUT - n'importe quel selecteur de CLASSE
   le bat. Un element que le JS masque par ".hidden = true" et que le CSS
   met en display: flex / inline-flex / grid / inline-block reste donc
   AFFICHE, avec le contenu du titre precedemment ouvert.

   Il a ete corrige quinze fois, une ligne a la fois, depuis la V13 :
   .meta-line (V13), .phase-chip (V39)... et il en manquait encore une,
   .cast-more, qui laissait le lien "Voir tout le casting sur TMDB"
   pointer vers le film precedent sur les fiches sans champ "tmdb"
   (Avengers: Doomsday). Une regle vaut mieux que seize.

   ⚠️ Pourquoi !important : sans lui la regle ne gagne que par ORDRE DE
   SOURCE, donc elle devrait vivre en BAS du fichier et se casserait au
   premier composant ajoute apres elle. La specificite continue d'arbitrer
   ENTRE declarations !important : ".foo[hidden] { display: flex !important }"
   reste disponible si un cas l'exige un jour. Aucun ne l'exige aujourd'hui.
   ========================================================================= */
[hidden] { display: none !important; }

/* ⚠️ LA RESERVE DE DEFILEMENT COMPTE LES DEUX BANDES (V43, V77). Elles etaient
   trois ; la fusion de l'en-tete en a retire une. Avec la seule hauteur de
   l'en-tete, une ancre atterrirait SOUS l'avertissement fige - donc invisible,
   et sans rien pour le dire. */
html { scroll-padding-top: calc(var(--head-h) + var(--warn-h) + var(--fige-h, 0px) + 12px); }

body {
  margin: 0;
  min-height: 100vh;
  display: flex; flex-direction: column;
  /* V329 (Visuel_B) - une lumiere de salle a peine levee au-dessus de la
     scene, neutre ; le fond reste --ink-0 partout ailleurs. */
  background: radial-gradient(120% 60% at 50% -10%, #18181E 0%, var(--ink-0) 60%) fixed, var(--ink);
  color: var(--text);
  font-family: var(--body);
  -webkit-font-smoothing: antialiased;
}

/* Les elements d'affichage sont ELARGIS (axe wdth), pas seulement grassis :
   c'est ce qui leur donne de la prestance sans les rendre compacts. */
.phase-title, .stat-label, .stat-val, .stat-hours,
.card-title, .modal-title, .fold-head, .status-badge {
  font-family: var(--display);
  font-weight: 800;
  font-stretch: var(--ch-ui);
}
.phase-title, .modal-title { font-stretch: var(--ch-affiche); }

/* ⚠️ LA LISTE DE CE QUI SE LIT EN PHRASES (V44) - et elle est COURTE, ce qui
   est le signe qu'elle est juste. Un site d'interface a peu de vraie prose :
   l'accroche de l'accueil, le synopsis, les deux textes de spoiler, le bandeau
   de consentement, et les quatre pages documentaires (voir pages.css).
   Tout le reste se repere du coin de l'oeil et reste a Archivo.

   ⚠️ LE CORPS NE BOUGE PAS D'UN DIXIEME. Literata a une hauteur d'x plus
   grande qu'Archivo : au MEME corps elle parait deja un demi-point plus
   grosse. Remonter la taille "parce que c'est du serif" ferait deborder les
   blocs de la fiche, dont la hauteur est calee sur l'affiche.

   ⚠️ Et --text-2, pas --paper : sur fond sombre un serif blanc pur
   s'auréole. La couleur de lecture est la meme que celle qu'ils avaient. */
.landing-tagline,
.modal-synopsis,
.spoiler-resume, .spoiler-role,
.consent-text, .consent-note {
  font-family: var(--read);
  font-weight: 400;
  /* Un serif se serre naturellement plus qu'une grotesque : on rend le
     dixieme de chasse que la police prend, on ne "corrige" pas la police. */
  letter-spacing: .004em;
}

/* ⚠️ LE MODIFICATEUR NE POSE PAS UNE SECONDE « width », IL REDEFINIT LA
   BORNE QUE « .wrap » LIT. Deux declarations de meme specificite (0,1,0) ne se
   departagent que par l ORDRE DE SOURCE - et « une regle qui ne gagne que par
   ordre de source ne gagne pas, elle attend » (piege nº 2, V45). Une propriete
   personnalisee posee sur l element lui-meme, elle, est arbitree par la
   specificite du selecteur qui la pose : il n y a plus de course. */
.wrap { width: var(--borne, var(--shell)); margin-inline: auto; }
.wrap--texte { --borne: var(--lisible); }

/* --------------------------- Le nom du site ----------------------------- */
/* ⚠️ LE HEADER EST SUPPRIME (V59) : titre, accroche et leurs cinq regles.
   Ils repetaient sur la page principale ce que la page d'accueil dit deja,
   et ils le repetaient sur la seule bande que la roadmap doit occuper.

   ⚠️ CE QUI RESTE N EST PAS UN RESIDU. C'etait le seul <h1> du document :
   sans lui la page n'a plus de nom pour un lecteur d'ecran ni de racine dans
   son plan. Il est donc masque VISUELLEMENT, pas retire - ce qui part, c'est
   ce qui s'affichait.

   ⚠️ ET `.doom` PART AVEC L ACCROCHE : c'etait son unique porteur du site.
   Les quatre pages documentaires ont leur propre `.doc-callout--doom`, qui
   ne depend pas de cette regle. Une classe sans porteur est du code mort
   (piege nº 4), et une classe utilitaire n'y echappe pas. */
/* ⚠️ V77 (B2) - IL REDEVIENT VISIBLE, ET C'EST LE MEME <h1>. La V59 l'avait
   masque visuellement sans le retirer, precisement pour que la page garde un
   nom : il n'y a donc rien a recreer, seulement une regle a defaire.

   ⚠️ "GRAND, MAIS SANS PRENDRE TROP D'ESPACE" : il vit dans le FLUX, pas dans
   la bande figee - ce qu'il coute, on ne le paie qu'a l'arrivee. Le corps est
   borne par un clamp, la casse est celle du site (capitales, decision de DA),
   et la police est Archivo : un titre se REPERE, il ne se lit pas en phrases
   (appariement V44). */
/* ⚠⚠ V101 (D3) - IL EST DE NOUVEAU MASQUE VISUELLEMENT, COMME EN V59, ET
   POUR LA MEME RAISON EXACTE : « masque sans etre retire, precisement pour
   que la page garde un nom ». Ce qui a change entre-temps, c est que le mot
   ROAD TO est desormais A L ECRAN - il EST le grand bouton de la bande
   figee. Deux titres peints diraient deux fois la meme chose.
   ⚠ IL N EST PAS RETIRE DU DOM : le document doit garder son `<h1>`. Meme
   technique que `.fmenu-label` (V54). */
.site-name--sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* V228 - LE LIEN D'EVITEMENT : hors ecran au repos, pose au-dessus de la bande
   figee (z-index 40) quand il a le focus. Il ne prend aucune place : il ne
   decale rien, ni au repos ni au focus. */
.evitement {
  position: absolute; left: .75rem; top: .6rem; z-index: 95;
  padding: .55rem .9rem; min-height: 34px;
  background: var(--ink-3); color: var(--text);
  border: 1px solid var(--ink-6); border-radius: var(--r-2);
  font-weight: 600; text-decoration: none;
  transform: translateY(-200%);
}
.evitement:focus { transform: none; }
@media (prefers-reduced-motion: no-preference) {
  .evitement { transition: transform .15s ease; }
}
/* La cible d'un saut ne se cache pas sous la bande figee. */
.roadmap, .archive, #tabFav, #tabCompte {
  scroll-margin-top: calc(var(--head-h, 45px) + var(--warn-h, 0px));
}
.roadmap:focus, .archive:focus, #tabFav:focus, #tabCompte:focus { outline: none; }

/* ============== L EN-TETE FIGE (V77 - B2) ==============================
   UNE SEULE BANDE, ET ELLE EN REMPLACE DEUX. Jusqu'a la V76, le HUD (V52/V59)
   et la barre d'outils (V43/V54) se figeaient l'un sous l'autre : 201 px de
   reserve, payes a CHAQUE pixel de defilement. La ligne fusionnee porte les
   onglets, "Filtres", la recherche et le profil ; les cases, le titre et la
   barre de progression redescendent dans le FLUX.

   ⚠️ CE N'EST PAS UNE QUATRIEME BANDE, C'EN EST UNE QUI EN REMPLACE DEUX.
   La V41 avait refuse d'AJOUTER une bande figee, jamais d'en fusionner deux.

   ⚠️ ELLE GARDE LE VOILE ET L'OMBRE DU HUD, tires de la rampe (V44) : depuis
   que le fond de page a quitte le noir pur, un voile noir rendrait la bande
   PLUS SOMBRE que la page qu'elle recouvre.

   ⚠️ ET SON FILET DU BAS EST « LA BARRE DE SEPARATION DE LA LIGNE DES ONGLETS
   TOUT EN HAUT DE PAGE » - la seule que B5 garde. Elle etait portee par
   ".tabs" ; dans une ligne a trois colonnes, un "border-bottom" pose sur la
   colonne du milieu n'aurait trace qu'un bout de filet. */
.topbar {
  position: sticky; top: 0; z-index: 40;
  padding: .34rem 0 0;
  /* ⚠️⚠️ V441 - UNE SEULE ECHELLE POUR TOUTE LA BANDE (retour de l auteur :
     « les elements ne sont pas a la taille du header quand il est grossi »).
     La V440 faisait grandir la bande ET la capsule, chacune de son cote et
     chacune a son rythme : le reste - recherche, Filtres, drapeau, profil -
     gardait ses 34 px au milieu d un rang de 62, et les trois morceaux
     bougeaient sur 180 ms. D ou le defaut graphique signale.
     Desormais UN SEUL NOMBRE commande tout : `--tete-ech`. Tout ce qui vit
     dans la bande se mesure en `--pastille`, et `--pastille` vaut ce nombre
     fois 34. Rien n est recopie, rien ne peut se desynchroniser.
     ⚠️⚠️ AUCUNE TRANSITION SUR CE QUI TIENT DE LA PLACE : la bande change de
     taille en UNE IMAGE. Une animation, meme de 180 ms, c est dix images
     pendant lesquelles `--head-h` ment - la ligne figee (V427) et les
     bandeaux de saga se posent trop haut. C est deja ce qui avait fait
     retirer la transition du rembourrage en V440. */
  --tete-ech: 1;
  --pastille: calc(34px * var(--tete-ech));
  /* ⚠️⚠️ V449 - LA CAPSULE A LE DROIT D ETRE PLUS GRANDE QUE LE RANG, ET
     C EST LE SEUL ELEMENT DANS CE CAS (retour de l auteur : « plus imposante
     tout en respectant la taille du header et sans depasser »). Elle est hors
     du flux (V101), donc sa taille ne commande AUCUNE hauteur : elle peut
     remplir ce que le rembourrage laisse, la ou les autres la fixent.
     ⚠️ LE PLAFOND N EST PAS UN GOUT : le rang vaut 40 x l echelle (le plancher
     de `.identity`), la pastille 34 x l echelle. 40 / 34 = 1,176 - au-dela, la
     capsule serait plus haute que le rang et sortirait de la bande. On s arrete
     juste en dessous, et ce rapport se recalcule tout seul si un jour le
     plancher bouge. */
  --caps-plus: 1;
  /* ⚠️⚠️ V440 - LA BANDE RESPIRE EN HAUT DE PAGE, ET SE RESSERRE AU
     DEFILEMENT (planche D1, demande de l auteur). C est la BANDE ENTIERE qui
     change, pas la seule capsule : `est-haut` est posee par `js/app.js` au
     premier pixel de defilement.
     ⚠️ LA CONTRAINTE DE LA V77 TIENT : une fois defilee, la bande revient a
     ses 45 px - c est-a-dire a ce qu elle coute pendant tout le reste de la
     lecture. Ce qu elle prend en plus, elle ne le prend qu a l arret.
     ⚠️ `--head-h` SUIT TOUT SEUL : elle est MESUREE (`mesureSticky`), et son
     ResizeObserver voit la bande grandir et retrecir. Aucun nombre ici.
     ⚠️⚠️ SA HAUTEUR NE S ANIME PAS, ET C EST MESURE : une transition sur le
     rembourrage fait LAGUER --head-h de 180 ms, donc la ligne figee (V427) et
     les bandeaux de saga se posent 16 px trop haut pendant ce temps. Seule la
     capsule s anime : elle est hors du flux, elle ne commande aucune hauteur. */
  /* ⚠⚠ V101 (D3) - LA RESERVE DU GRAND BOUTON, ET C EST LE SEUL LEVIER.
     Un `margin-bottom` sur un element `sticky` pousse ce qui suit SANS
     grandir sa propre boite : `--head-h`, mesuree au
     `getBoundingClientRect`, ne bouge pas d un pixel. C est ce qui permet
     au bouton de DEPASSER de la bande sans l EPAISSIR - la contrainte
     ecrite de la fiche (la V77 a passe une version a ramener cette bande de
     201 px a 45, et elle se paie a chaque pixel de defilement, V53).
     ⚠⚠ V110 (E4) - ET C EST POSE ICI PARCE QUE LA BANDE NE S EN VA
     JAMAIS. La V79 puis la V101 le justifiaient par la tete de page, MASQUEE
     sur cinq des sept ecrans : elle n existe plus du tout depuis E4, et la
     raison de fond est la meme, en plus forte. Une reserve doit etre portee
     par ce qui est TOUJOURS a l ecran. */
  margin-bottom: var(--roadto-air);   /* V408 : plus rien ne pend */
  background: color-mix(in srgb, var(--ink-0) 95%, transparent);
  backdrop-filter: blur(8px);
  box-shadow: 0 1px 0 var(--rule), 0 12px 26px -8px rgba(0, 0, 0, .9);
}

/* ⚠️ TROIS COLONNES minmax(0,1fr) / auto / minmax(0,1fr), ET C'EST CE QUI
   EMPECHE LES ONGLETS DE SAUTER D'UN ONGLET A L'AUTRE. "Filtres" et la
   recherche suivent l'onglet (V52) : ils disparaissent sous Archives,
   Favoris, Compte et les apercus. Avec deux fractions EGALES de part et
   d'autre, la rangee du milieu reste centree sur la PAGE qu'elles soient la
   ou non - c'est le geste que la V59 avait pose pour le profil, reemploye
   tel quel. Il n'y a donc aucune place a « garder » ni a « rendre » a la
   main, et rien a mesurer pour ca.

   ⚠️ align-items: center - les trois blocs n'ont pas la meme hauteur, et
   c'est la ligne qui les tient, pas eux. */
.topbar.est-haut { padding: .5rem 0 .35rem; --tete-ech: 1.18; --caps-plus: 1.1; }
/* ⚠️ PLUS AMPLE DES 621 px SEULEMENT : sur telephone la capsule vit entre le
   drapeau et le profil, et au-dela de ce facteur elle les touche (mesure a
   375 px, essai « touche » de la V425). */
@media (min-width: 621px) {
  .topbar.est-haut { --tete-ech: 1.22; --caps-plus: 1.16; }
}
/* ⚠️ LES MENUS DE LA BANDE NE GRANDISSENT PAS AVEC ELLE : ils sont dedans par
   le document, pas par le regard. Leurs lignes gardent la cible du site. */
.topbar .fmenu-pop, .topbar .langue-pop, .topbar .profile-pop, .topbar .notif-pop {
  --tete-ech: 1;
}
.topbar-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  column-gap: var(--sp-4);
}

/* A GAUCHE : le menu "Filtres" et la recherche, dans cet ordre (maquette D).
   Ils AGISSENT sur la rangee, donc ils suivent l'onglet - mais la LIGNE
   reste : c'est le conteneur qui se masque, pas la bande. */
.topbar-tools {
  grid-column: 1; grid-row: 1; justify-self: start;
  display: flex; align-items: center; gap: .55rem;
  width: 100%; min-width: 0;
}

/* ================ V306 - LE COMPTE A REBOURS (retour de l auteur) ========
   ⚠️⚠️ V408 - IL QUITTE LA BANDE FIGEE POUR LE CENTRE DE LA ZONE PARCOURS
   (demande de l auteur) : « Road to » en blanc, le nom de la destination a sa
   couleur, puis les chiffres. Il est dans le flux : la bande ne le porte plus.
   ⚠️ DESSIN SOBRE, EN ARCHIVO (il n est pas en `--cosmic` : Orbitron ne sert
   qu au pourcentage). */
.decompte {
  margin: 0; display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: center; gap: .1rem .45rem; min-width: 0;
  font-family: var(--display); font-variant-numeric: tabular-nums;
  color: var(--text-2); text-align: center;
}
.decompte-titre {
  display: inline-flex; align-items: baseline; gap: .35rem;
  font-stretch: var(--ch-ui); font-weight: 800; font-size: var(--fs-s);
  letter-spacing: .12em; text-transform: uppercase; white-space: nowrap;
}
.decompte-road { color: var(--text); }
.decompte-nom { color: var(--roadto-teinte, var(--doom)); }   /* V371 - vert Doomsday ; V423 - ou la teinte du film choisi */
.decompte-case { display: inline-flex; align-items: baseline; gap: .12rem; }
.decompte-val { font-stretch: var(--ch-affiche); font-weight: 700; font-size: var(--fs-m); color: var(--text); }
.decompte-unite { font-size: var(--fs-micro); color: var(--text-dim); }
.decompte-jour { font-weight: 700; font-size: var(--fs-s); color: var(--text); }

/* ⚠️⚠️ V483 - « ROAD TO » PLUS IMPOSANT, MAIS PAS SUR TELEPHONE (retour de
   l auteur, deux phrases de la meme liste : « Road to doit etre plus imposant
   sans abuser non plus » et, pour le telephone, « le compte a rebours doit
   etre celui de la version actuelle - tient sur la largeur d une seule ligne
   uniquement »).
   ⚠️ UN SEUL CRAN DE L ECHELLE, PAS DEUX : le titre passe de `--fs-s` a
   `--fs-m`, les chiffres de `--fs-m` a `--fs-base`, l unite du plancher
   d etiquette a `--fs-s`. « Sans abuser » est une consigne, pas une figure de
   style - le compte a rebours reste le voisin de Complet/Express, pas leur
   titre.
   ⚠️ ET LE SEUIL EST 621 px, LE MEME QUE TOUTE LA ZONE PARCOURS : en dessous,
   la bande du compte a rebours prend deja la ligne entiere (voir plus bas), et
   un cran de plus la ferait passer a DEUX lignes. */
@media (min-width: 621px) {
  .decompte-titre { font-size: var(--fs-m); }
  .decompte-val   { font-size: var(--fs-base); }
  .decompte-unite { font-size: var(--fs-s); }
  .decompte-jour  { font-size: var(--fs-m); }
}

/* ============== LA TETE DE PAGE - ELLE N EXISTE PLUS (V110 - E4) =======
   ⚠️⚠️ `.pagehead` ET `.pagehead-inner` SONT SUPPRIMEES, ET CE N EST PAS UN
   RANGEMENT. La V77 les avait posees pour porter le TITRE et les CASES ;
   le titre est parti dans la bande figee (V101), le bouton SYNCHRONISER
   aussi (V105), et le menu PROGRESSION vient de sortir du flux (E4). Il ne
   restait qu un conteneur vide portant son seul padding - c est-a-dire
   exactement les 27 px de PADDING VIDE que la V92 a passe une version a
   retirer, et que rien ne signale puisque le conteneur est vide, pas casse.

   ⚠️ CE QUI PART, PART EN ENTIER (piege nº 4) : la section, sa grille a
   trois colonnes, son `row-gap` et son repli sous 700 px. Une regle CSS
   dont l objet a disparu ne se laisse pas « au cas ou » - le code mort ne
   fait rien, une regle PERIMEE fait quelque chose de FAUX (lecon V59). */
/* ====== LE MENU PARCOURS (V80 -> V430) EST PARTI EN ENTIER (V431) ========
   Demande de l auteur : *Suivi titres* et *Mon marathon* s ouvrent sous leurs
   portes (`.ancre-pop`, plus bas). Le flanc gauche qu il occupait sous
   1200 px est vide : la reserve des rangees de cartes part avec lui. */

/* ================= LE BOUTON <social> (V210 - partie 5, V203) ============
   ⚠️⚠️ C EST `<progression>` EN MIROIR, ET IL EST CONSTRUIT COMME LUI - PAS
   COMME UN OBJET NEUF. Les trois regles que le menu Progression a payees
   (V80, V110) s appliquent mot pour mot :

     1. `writing-mode`, JAMAIS `transform: rotate()` - une rotation tourne la
        boite SANS reserver sa place, donc deux valeurs a tenir d accord a la
        main (piege nº 6) ;
     2. un CENTRAGE PAR MARGES - `top` calcule sur la bande figee, `bottom: 0`,
        `margin-block: auto` - il ne peut donc jamais se poser sous la bande,
        quelle que soit sa hauteur ;
     3. un APLAT (--ink-1) - colle au bord sans fond, il flotterait par-dessus
        les affiches : illisible.

   ⚠️⚠️ MAIS LE SENS DE LECTURE S INVERSE, ET C EST LA PARTIE QU ON NE
   RECOPIE PAS. `Progression` est a GAUCHE : son libelle porte un
   `rotate(180deg)` pour se lire du bas vers le haut, et sa rangee est en
   `row-reverse` pour que le chevron ne se lise pas AVANT le mot. Ici le
   bouton est a DROITE et le retour demande qu il se lise « du S vers le L »,
   c est-a-dire de HAUT en BAS : `writing-mode: vertical-rl` SEUL donne ce
   sens, donc PAS de rotation, et l ordre du document (libelle puis chevron)
   est deja le bon - donc PAS de `row-reverse`. On transpose, on ne recopie
   pas.

   ⚠️ `.socmenu-*` EST UN ESPACE DE NOMS. `.pmenu-pop` a deja donne une fois
   son `left` au popover de progression (V80), SANS qu aucune erreur ne se
   produise.

   ⚠️⚠️ LE PANNEAU VIT DANS LE FLUX DU CONTENEUR, PAS EN `position: absolute`,
   ET C EST CE QUI BORNE TOUT SANS UN CALCUL. `.socmenu` est une rangee
   `row-reverse` ancree au bord droit : le bouton reste colle au bord, la
   colonne des deux sous-boutons se pose a sa gauche, le panneau a gauche
   d elle. La hauteur du conteneur suit son plus grand enfant, donc
   `margin-block: auto` centre l ENSEMBLE - « il grandit ou rapetisse selon le
   nombre d elements » sans une seule hauteur ecrite.

   ⚠️ SON `z-index` EST UNE REGLE, JAMAIS UN ORDRE DE DOCUMENT (piege nº 2) :
   30, comme `Progression` - sous l avertissement « sans compte » (35), sous
   la bande figee (40), sous l accueil (70), les fiches (78 et 80) et le
   bandeau de consentement (90). Les fenetres qu il OUVRE le recouvrent donc
   par construction.

   ⚠️ LE LOGO ET L ENCADREMENT SONT ILLUMINES A LA COULEUR DU PROFIL -
   `--star-mine`, qui passe deja par le PLANCHER DE CONTRASTE (V129). Une
   couleur presque noire ne peut donc pas rendre le bouton invisible. */
/* ⚠️⚠️ V293 - IL PASSE AU BORD GAUCHE, SOUS LE BOUTON PROGRESSION (voir
   `.progmenu`) : meme boite, meme centrage, rail pose en BAS. Ce qui est dit
   plus haut du bord DROIT (row-reverse, coin droit carre) est historique. */
/* ⚠️⚠️ V410 - LE PANNEAU DESCEND DE LA BANDE, A DROITE, sous le bouton des
   amis (entre la cloche et le profil). La boite est fixe : la bande l est. */
.socmenu {
  position: fixed; z-index: 30;
  top: calc(var(--head-h) + var(--warn-h) + .4rem);
  right: max(1rem, calc((100% - var(--shell)) / 2));
  pointer-events: none;
}
.socmenu-pop { pointer-events: auto; }
/* ⚠️ LE COIN DROIT EST CARRE, LE GAUCHE ARRONDI - l exact miroir de
   `Progression`. Le bouton est COLLE au bord de la fenetre, et un cote
   arrondi contre un bord droit laisserait deux croissants de fond. Meme
   valeur `--r-pill`, posee sur les deux coins qui FLOTTENT, et sur eux seuls.
   ⚠️ LA CIBLE DE 34 px SE TIENT PAR LA LARGEUR, comme celle de `Progression`
   et comme le coeur de favori (V6). En dessous, c est impossible au doigt. */
/* ⚠️⚠️ V220 - `.socmenu-btn`, `.socmenu-label` ET `.socmenu-caret` SONT
   PARTIES AVEC LEUR PORTEUR : le bouton <social> n existe plus (renversement
   de la V210). Ce qu il portait - l aplat, le filet a la couleur du profil,
   le coin droit carre colle au bord - passe aux DEUX boutons, IDENTIQUES,
   « superposes avec un petit espace ».
   ⚠️ 40 x 44 px : au-dessus du plancher tactile de 34 px, et ce sont
   desormais les seules commandes de la couche sociale sur la page. */
/* V410 - le bouton des amis : le gabarit de la cloche, en BLANC pur, quelle
   que soit la couleur du profil (demande de l auteur). Ouvert, il s eclaircit. */
/* Deux classes : `.notifs-btn` vit plus bas dans la feuille et gagnerait a
   l ordre de source (piege nº 2). */
.notifs-btn.amis-btn { color: #FFFFFF; }
.notifs-btn.amis-btn svg { width: 22px; height: 22px; }
.notifs-btn.amis-btn[aria-expanded="true"] { background: var(--ink-3); border-color: var(--ink-6); }


/* ⚠️ LA LARGEUR EST BORNEE ET LA HAUTEUR SUIT LE CONTENU - « ni trop petit ni
   trop large », « il grandit ou rapetisse selon le nombre d elements ». Le
   plafond compte ce qui est A DROITE du panneau (le bouton, la colonne des
   sous-boutons et leurs deux gouttieres) : sans ca, a 320 px, le panneau
   pousserait le bouton hors de la fenetre. Mesure a 320 px.
   ⚠️ V142 - MEME REPLI `vh` -> `dvh` que les trois autres surfaces flottantes
   qui defilent : `90vh` se resout sur la GRANDE fenetre du telephone.
   ⚠️ ET `overscroll-behavior: contain` : sans lui, arrive en bout de course,
   le geste CHAINE vers la page derriere. */
.socmenu-pop {
  /* ⚠️ V211 - LA LARGEUR CONFORTABLE DEMANDEE PAR LE RETOUR (« le panneau
     prend une largeur confortable sur grand ecran »). Elle reste BORNEE - « ni
     trop petit ni trop large » - et le plafond compte toujours ce qui est a
     sa droite, mesure a 320 px.
     ⚠️⚠️ « LES ELEMENTS GRANDISSENT PROPORTIONNELLEMENT » SE PAIE PAR LA
     LARGEUR, PAS PAR UNE MEDIA QUERY, ET C EST UNE DECISION. La ligne est un
     flex dont la colonne du pseudo porte `flex: 1 1 8rem` : elle prend TOUT
     ce que le panneau gagne, donc les lignes s allongent par construction.
     Grossir la BULLE, en revanche, demanderait de grossir le DESSIN - et
     depuis la V199 sa taille vit dans le JS, seule autorite sur elle. Une
     taille de boite ecrite ici pendant que le dessin reste a 28 px, ce
     seraient deux valeurs a tenir d accord dont une seule est visible
     (piege nº 6), et un embleme perdu au milieu de son cadre. */
  /* ⚠️⚠️ V220 - RENVERSEMENT DE LA LARGEUR DE LA V211 : le panneau devient
     VERTICAL, « comme les listes d amis de LoL ou de Steam ». 46 rem etalaient
     une liste de NOMS sur 736 px, et une ligne de nom ne se lit pas mieux
     parce qu elle est longue. Il passe a 24 rem et prend de la HAUTEUR : une
     colonne se lit de haut en bas, et c est le sens d une liste de gens.
     ⚠️ LA HAUTEUR EST UN PLANCHER, PAS UNE VALEUR : borne par l espace sous
     la bande figee, donc il ne sort jamais de la fenetre. Le plafond compte le
     meme espace - `100dvh - 2rem` ignorait la bande, et un panneau long
     debordait par le bas. */
  /* V293 - le panneau part a DROITE du rail, centre sur le flanc : il est
     sorti du flux pour ne pas grandir la boite partagee avec Progression. */
  /* V410 - sous la bande, dans le flux de sa boite fixe : plus de rail a sa gauche. */
  width: min(24rem, calc(100vw - 2rem));
  --soc-place: calc(100vh - var(--head-h) - var(--warn-h) - 1.5rem);
  /* ⚠️ V303 - LE PLANCHER DE 34 REM EST PARTI (retour de l auteur : « des
     espaces vides qui ne servent a rien ») : le panneau prend la hauteur de
     son contenu, et seul le plafond reste, pour ne jamais sortir de la
     fenetre. */
  max-height: var(--soc-place);
  overflow-y: auto; overscroll-behavior: contain;
  background: var(--ink-3);
  border: 1px solid var(--star-mine); border-radius: var(--r-3);
  box-shadow: var(--lift-2);
}
/* ⚠️ LES DEUX SECTIONS SONT DEPLACEES, JAMAIS REECRITES (geste V61, V155) :
   memes ids, memes classes, meme CSS - donc `js/amis.js` et `js/equipe.js`
   n ont pas change d une ligne, `MutationObserver` compris. Ce qu il reste a
   ecrire ici est ce que leur ancienne place leur donnait : elles etaient des
   SECTIONS DE PAGE, calees sur la coquille, elles sont maintenant le contenu
   d un panneau. */
.socmenu-pop .amis-tab { flex: none; padding: .9rem .95rem 1.1rem; }
.socmenu-pop .wrap { width: 100%; }
.socmenu-pop .amis-inner { max-width: none; }

/* ⚠️ LA COLONNE EST GARDEE, ET AVEC ELLE L ETIREMENT (V59). Les cases
   s etirent a la plus large des trois pour que les trois valeurs tombent
   sur le meme axe VERTICAL : c est tout l interet de la colonne, et un
   depliage vers le bas la conserve telle quelle. La disposition en RANGEE,
   essayee le 31 aout, est ecartee - elle perdait cet axe et redonnait a
   chaque case le pave que la V14 avait retire, pour ne rien gagner une
   fois le panneau sous le bouton. */
.stats {
  display: flex; flex-direction: column; align-items: stretch; gap: .32rem;
}


/* ⚠️ LA CASE GRANDIT D UN CRAN ET REPREND SES HEURES (V59). Elle etait
   « compacte » parce que la V52 la voulait discrete a cote d une identite
   qui devenait le sujet ; les deux blocs ne se disputent plus rien depuis
   qu'ils sont independants, et la duree est precisement ce qu'on vient
   chercher. Trois valeurs - l intitule, le couple, les heures - et elles
   s alignent parce que la case s etire. ⚠️ V102 : elles ne sont plus « une
   par colonne », les heures ayant pris leur propre rang. L etirement, lui,
   fait exactement le meme travail. */
/* ⚠️ V102 (D9 a + e) - LE FILET VERTICAL ROUGE EST PARTI, ET LES HEURES
   SONT PASSEES SUR LEUR PROPRE RANG. Deux consequences qui ne se voient
   qu en les cherchant :
   1. « position: relative » et « overflow: hidden » n existaient QUE pour
      le filet absolu (.stat::before). Une regle dont l objet a disparu ne
      se laisse pas « au cas ou » : le code mort ne fait rien, une regle
      perimee fait quelque chose de faux (lecon V59). Elles partent avec.
   2. Le padding gauche valait .78rem contre .7rem a droite - l ecart
      degageait les 3 px du filet. Sans filet, la case redevient
      SYMETRIQUE, sinon il reste a l ecran le decalage d un objet absent.
   ⚠️ DEUX COLONNES, PLUS TROIS : l intitule et le couple sur le premier
   rang, les heures seules sur le second. */
.stat {
  display: grid; grid-template-columns: auto 1fr; align-items: baseline;
  column-gap: var(--sp-3); row-gap: .1rem;
  padding: .3rem .7rem .34rem;
  background: var(--panel);
  border: var(--frame-dim);
  border-radius: var(--r-1);
  box-shadow: var(--lift-0);
}

.stat-label {
  font-family: var(--display); font-stretch: var(--ch-etiquette); font-weight: 700;
  font-size: var(--fs-micro); letter-spacing: .22em; text-transform: uppercase;
  color: var(--text-dim);
}

/* ⚠️⚠️ V103 (D9 c) - LA VALEUR DOUBLE EST DEVENUE UNE VALEUR COMMUTEE.
   La case portait DEUX nombres et une legende disait lesquels ; elle en
   porte UN, et deux boutons disent lequel. Partent avec le couple :
   .stat-pair, .stat-all, .stat-road et .stat-sep - le separateur n a plus
   rien a separer, et une classe sans porteur est du code mort (piege nº 4,
   geste de la gelule .profile-btn en V59).
   ⚠️ LA TEINTE EST PORTEE PAR LE CONTENEUR, PAS PAR LA VALEUR. Trois cases
   qui changeraient de couleur ensemble, c est UNE decision : la poser sur
   .stats la rend vraie pour les trois par construction, et le jour ou une
   quatrieme case arrive elle l herite sans qu on ait a y penser.
   ⚠️ LES DEUX TEINTES SONT CELLES DE LA V102, AU JETON PRES - blanc
   --text-2 pour le catalogue, vert --doom pour la route. Les boutons les
   reprennent : une commande qui dirait une couleur et une valeur une autre
   serait la legende fausse que la V102 vient justement de corriger. */
.stat-val {
  grid-column: 2; justify-self: end;
  font-family: var(--display); font-stretch: var(--ch-affiche); font-weight: 800;
  font-size: clamp(.96rem, 1.6vw, 1.14rem); line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: var(--text-2);
}

/* ⚠️ V102 (D9 e + f) - LES HEURES ONT LEUR PROPRE RANG, ET LEUR FILET
   PART AVEC LEUR VOISIN. La V59 leur avait pose un plancher de 4,7 rem
   parce que le filet etait porte par la VALEUR : sans lui il se decalait
   d une ligne a l autre (mesure : « 2 h 06 » posait le sien 18 px a droite
   de celui de « 216 h 29 »), et trois traits verticaux a trois abscisses
   font sauter l alignement qui est tout l interet de la colonne.
   ⚠️ SEULES, LES HEURES N ONT PLUS DE VOISIN A SEPARER : le filet, le
   plancher et le padding qu il reservait partent ENSEMBLE (piege nº 4).
   ⚠️ ET L ALIGNEMENT NE DEPEND PLUS D UN PLANCHER : justify-self: end plus
   l etirement des cases (V59) posent les trois valeurs sur le meme bord
   droit PAR CONSTRUCTION, ce qu aucune longueur de nombre ne peut decaler.
   La contrainte est SUPPRIMEE, pas contournee.
   ⚠️ LA COULEUR PASSE AU GRIS (retour du 1er septembre 2026). Elles
   prenaient le rouge de la route « sans quoi la legende mentirait sur ce
   que le rouge designe » : la legende ne porte plus de rouge du tout, donc
   l argument tombe avec lui. Le gris est --text-dim, deja dans la rampe
   d encre (V44) - on n en invente PAS un quatorzieme. */
.stat-hours {
  grid-column: 2; justify-self: end; text-align: right;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-s); line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  white-space: nowrap;
}

/* ⚠️ V103 (D9 d) - LES DEUX BOUTONS SONT CENTRES, ET LES CASES NE LE SONT
   PAS. La legende etait alignee a gauche ; la commande se pose au milieu de
   la colonne qu elle commande. ⚠️ Centrer ces deux-la ne touche PAS les
   cases : elles sont dans .stats, qui garde « column + stretch » (V59) -
   c est l etirement qui aligne les trois valeurs sur le meme axe vertical,
   et il n a rien a voir avec l axe de ce qui est en dessous.
   ⚠️ LA CIBLE FAIT 30 px DE HAUT, PAS 34. Les 34 px du coeur (V6), de la
   roue (V59) et du bouton « Progression » (V82) valent pour une cible
   ISOLEE sur une affiche ou dans une bande ; ici deux boutons se touchent
   presque, sur toute la largeur du panneau, et 34 px auraient ajoute deux
   fois - ils sont deja plus larges que hauts. La cible reste au-dessus du
   plancher tactile courant et le panneau est un popover absolu, donc ce
   qu elle coute en hauteur ne se paie nulle part ailleurs. */
/* ------------------ LE SELECTEUR DE ROUTE (V131 - C2c) --------------------
   ⚠️⚠️ LES DEUX CLASSES SONT CELLES QUE LA V116 A RETIREES, ET ELLES
   REVIENNENT AVEC LEUR ECRAN. Elles dessinaient ce selecteur SANS QU AUCUN
   BALISAGE NE LES PORTE - du code mort « en preparation », piege nº 4 - et la
   V116 ecrivait mot pour mot : « C2c les redessinera avec son ecran ; une
   mise en forme posee d avance decide de la forme sans que personne l ait
   choisie. » C est cet ecran-la.

   ⚠️ IL EMPRUNTE LA FORME DU SELECTEUR DE PORTEE (V103), ET CE N EST PAS UNE
   COPIE : c est le meme objet - deux boutons voisins dans un popover, dont
   l etat actif se dit par la COULEUR de ce qu ils commandent, jamais par un
   aplat plein qui ferait de la commande le sujet du panneau (V49).

   ⚠️ LA CIBLE FAIT 30 px, COMME CELLE DE LA PORTEE : les 34 px du coeur
   (V6), de la roue (V59) et du crayon (V130) valent pour une cible ISOLEE ;
   ici deux boutons se touchent presque et sont plus larges que hauts. */
/* ⚠️⚠️ V160 (5d) - IL A QUITTE LE MENU PROGRESSION POUR LA BARRE HERO, ET LA
   MARGE A CHANGE DE COTE. Dans le menu il precedait les cases, donc il poussait
   PAR LE BAS ; sous le pourcentage il LEGENDE la barre, donc il respire par le
   HAUT et se colle a ce qui suit - le premier bandeau de saga porte deja le
   sien. Une marge basse gardee ici aurait creuse deux fois le meme vide. */
/* ⚠️ V191 - IL EST `relative` PARCE QUE LA ROUE CRANTEE S Y ANCRE, a
   `left: 100%` : elle ne prend donc pas un pixel de largeur et les deux
   boutons restent CENTRES sous la barre. */
.route-switch {
  position: relative;
  margin: .5rem 0 0;
  display: flex; justify-content: center; align-items: center; gap: .25rem;
}
/* ⚠️⚠️ V446 - LE MOT ACTIF EST NETTEMENT PLUS GROS QUE LES DEUX AUTRES
   (planche M2, choix de l auteur) : on doit savoir SUR QUELLE ROUTE on est
   sans rien lire. Blanc et souligne a la couleur de la route ; les deux
   autres restent sourds. AUCUN ENCADREMENT - l auteur l a ecrit deux fois.
   ⚠️ LA LIGNE NE SAUTE PAS QUAND ON CHANGE DE ROUTE : les trois boutons ont
   le meme plancher de hauteur, et c est la taille du texte qui change, pas la
   boite. */
.route-opt {
  appearance: none; cursor: pointer;
  min-height: 34px; padding: .3rem .55rem;
  display: inline-flex; align-items: center;
  background: transparent; border: 0; border-radius: var(--r-2);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-s); letter-spacing: .01em;
  color: var(--ink-7);
  transition: color .16s, background-color .16s;
}
.route-opt:hover { color: var(--text-2); background: var(--ink-4); }
.route-opt:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.route-opt[aria-pressed="true"] {
  color: var(--paper);
  font-size: calc(var(--fs-l) * 1.1); font-stretch: var(--ch-affiche); letter-spacing: .02em;
  text-decoration: underline;
  text-decoration-color: var(--route-accent);
  text-decoration-thickness: 2px;
  /* 5 px : le trait passe sous les jambages, comme le ruban du duo (V435) */
  text-underline-offset: 5px;
}
/* ⚠️ ET LA ROUTE DUO PORTE LA COULEUR CHOISIE A DEUX, avec un repli sur le
   vert Doomsday tant que personne ne l a choisie. La variable est posee sur
   la RACINE par `peindreRoute()` : la barre, l orbe, le halo, le pourcentage
   et ce bouton la lisent chacun de leur cote - geste de `--star-mine` (V63).
   ⚠️ Un litteral recopie ici serait le piege nº 6. */
/* V446 - le mot actif est BLANC ; ce qui porte la couleur de la route, c est
   son SOULIGNEMENT. Un mot colore et un mot blanc se comparaient mal. */
.route-opt--duo[aria-pressed="true"] { color: var(--paper); }

/* ⚠️⚠️ V427 - LA LIGNE SOLO / DUO / GROUPE FIGEE sous l en-tete (et sous
   l avertissement sans compte) : la teinte de la bande, un filet, 40 px.
   ⚠️ LE BANDEAU DE LA ROUTE OUVERTE SE FIGE DESSOUS, dans sa route : on peut
   le replier ou tout cocher sans remonter (demande de l auteur). Son fond
   deborde en pleine largeur par une ombre coupee (`clip-path`), pas par une
   boite plus large - rien ne deborde horizontalement de la page. */
.route-fige {
  position: fixed; left: 0; right: 0; z-index: 34;
  top: calc(var(--head-h) + var(--warn-h));
  background: color-mix(in srgb, var(--ink-0) 95%, transparent);
  backdrop-filter: blur(8px);
  box-shadow: 0 1px 0 var(--rule);
}
.route-fige .route-fige-in { margin: 0; min-height: 40px; }
/* ⚠️⚠️ V455 - AU DEFILEMENT, ELLE GARDE SA FORME HORIZONTALE (demande de
   l auteur) : la colonne du coin est une mise en page de REPOS, pas une
   lecture. Mais son bandeau ne barre plus la page : il se reduit a la largeur
   des trois mots, centre, en pilule.
   ⚠️ SA HAUTEUR RESTE MESUREE (`--fige-h`), donc les bandeaux de saga se
   posent dessous sans qu un nombre soit recopie : c est la boite exterieure
   qui est mesuree, et elle ne change pas de hauteur - seule sa largeur bouge.
   ⚠️ LE FOND PART DE LA BOITE EXTERIEURE POUR ALLER SUR LA PILULE : un fond
   pleine largeur sous une pilule aurait fait deux plaques l une sur l autre. */
@media (min-width: 1200px) {
  .route-fige { background: none; backdrop-filter: none; box-shadow: none; }
  .route-fige .route-fige-in {
    width: max-content; max-width: calc(100vw - 2rem); margin-inline: auto;
    padding: .1rem 1.1rem; gap: .9rem;
    background: color-mix(in srgb, var(--ink-0) 92%, transparent);
    backdrop-filter: blur(8px);
    border-radius: var(--r-pill);
    box-shadow: 0 6px 20px -10px rgba(0, 0, 0, .95);
  }
  /* plus imposante qu au repos d avant, sans retrouver la taille de la colonne */
  .route-fige .route-opt { font-size: var(--fs-base); }
  .route-fige .route-opt[aria-pressed="true"] { font-size: var(--fs-xl); }
}
/* deux classes : `.phase-header { position: relative }` vit plus bas (piege nº 2) */
.roadmap .phase-header {
  position: sticky; z-index: 20;
  top: calc(var(--head-h) + var(--warn-h) + var(--fige-h, 0px));
  background: var(--ink-0);
  box-shadow: 0 0 0 100vmax var(--ink-0);
  clip-path: inset(-6px -100vmax -6px -100vmax);
}

/* ==================== LE RUBAN DU DUO (V162 - 5d) ========================
   « Les deux pseudos cote a cote, separes par un filet vertical,
     l initiateur a gauche. »

   ⚠️ LE PREFIXE `.ruban-` EST UN ESPACE DE NOMS : `.duo-*` appartient a
   l ECRAN du duo depuis la V96, et le site a deja paye une collision de
   prefixes (`.pmenu-pop` donnant son `left` au popover de progression, V80).

   ⚠️ IL NE PORTE NI FOND NI CADRE. Ce qui le pose est la SURFACE de ce qui
   l entoure : il vit sous la barre hero, entre deux choses qui ont deja leur
   elevation. Un aplat de plus ici ferait de la legende un objet - et la
   regle « un seul objet plein appelle » vaut sur cet ecran comme ailleurs. */
/* ⚠️⚠️ V189 - LES DEUX COTES SONT DES COLONNES : LE PSEUDO PASSE SOUS
   L EMBLEME, CENTRE SUR LUI. Cote a cote, un pseudo long poussait l embleme
   hors de l axe et les deux cotes ne se repondaient plus ; empile, l embleme
   tient sa place quelle que soit la longueur du nom - c est le principe de la
   carte de film (chaque bloc a une hauteur fixe, donc les pastilles
   s alignent par construction).
   ⚠️ LE FILET RESTE AU MILIEU SANS RIEN CONNAITRE DES DEUX COTES :
   `align-items: center` le centre sur la plus haute des deux colonnes. */
.ruban {
  margin: .55rem 0 0;
  display: flex; align-items: center; justify-content: center;
  flex-wrap: wrap;
  /* ⚠️ V195 - L ECART TIENT COMPTE DU CRAYON, QUI DEBORDE DE SA COLONNE : son
     dessin de 12 px est centre dans une cible de 34, donc il court jusqu a
     ~23 px du bord du nom. En dessous, il toucherait le filet. */
  gap: var(--ruban-ecart);
}
/* ⚠️⚠️ V435 - LE FILET DU DUO EST LE CENTRE DE LA PAGE (demande de l auteur) :
   trois colonnes, les deux cotes de meme largeur, donc deux pseudos de
   longueurs differentes ne le decalent plus. Mon cote a gauche du filet, cale
   contre lui ; l autre (ou le [+]) a droite. */
.ruban {
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
}
.ruban > .ruban-cote:first-child { grid-column: 1; justify-self: end; }
.ruban > .ruban-filet { grid-column: 2; }
.ruban > .ruban-plus, .ruban > .ruban-cote + .ruban-cote,
.ruban > .ruban-plus + .ruban-cote { grid-column: 3; justify-self: start; }
/* ⚠️ L ECART ENTRE L EMBLEME ET LE NOM PASSE DE .1 A .34 rem, sur
   retour d usage : colles, les deux se lisaient comme un seul bloc et le nom
   paraissait poser sur la bulle. */
.ruban-cote {
  display: flex; flex-direction: column; align-items: center; gap: .34rem;
  min-width: 0;
}
/* Le rang du nom : le pseudo, et le crayon qui le suit.
   ⚠️ V190 - IL EST `relative` PARCE QUE LE MENU DU SURNOM S Y ACCROCHE. Le
   menu vit DERRIERE le crayon (`js/duo.js` l y pose a chaque peinture), donc
   son ancre est ce rang-la, quel que soit le cote qui porte le crayon. */
.ruban-sous {
  position: relative;
  display: flex; align-items: flex-start; gap: .1rem;
  min-width: 0; max-width: 100%;
}

/* ============ LE MENU DU SURNOM (V190) - LE SEPTIEME DU SITE ============
   ⚠️⚠️ IL PAIE LE PRIX ECRIT D UN MENU, ET IL EST ECRIT UNE FOIS : un seul
   menu ouvert a la fois (garde EN CAPTURE, et son declencheur n appelle PAS
   `stopPropagation()`), Echap qui rend le focus, un PREFIXE DE CLASSE a lui
   (`.rsurnom-`, parce que `.ruban-` decrit le ruban et `.duo-` l ecran du
   duo), et la borne `max-width: calc(100vw - 3rem)` MESUREE a 320 px.
   ⚠️ CE N EST PAS UNE FENETRE : il n entre donc PAS dans
   `libererDefilement()` ni dans l ordre d Echap global - un popover ne prend
   pas son defilement a la page (regle V106). */
.rsurnom-pop {
  position: absolute; z-index: 30;
  top: calc(100% + .35rem); left: 50%; transform: translateX(-50%);
  /* ⚠️⚠️ LA LARGEUR EST DECLAREE, ELLE N EST PAS DEDUITE, ET C EST UNE
     MESURE QUI L A DECIDE. Ecrit `width: max-content`, ce panneau rendait
     21 px : la contribution intrinseque d un `<input>` a largeur declaree ne
     remonte pas jusqu a son conteneur absolu, dont le bloc contenant est ici
     un rang de 49 px. Une largeur declaree ne depend de personne.
     ⚠️ LA BORNE EST MESUREE A 320 px : centre sous le crayon, un panneau de
     14 rem depasserait de 2 px ; `calc(100vw - 8rem)` le ramene a 192 px,
     c est-a-dire dans la fenetre - la regle des menus (V80). */
  width: min(14rem, calc(100vw - 8rem));
  display: flex; flex-direction: column; gap: .4rem;
  padding: .6rem;
  background: var(--ink-1); border: var(--frame-dim); border-radius: var(--r-3);
  box-shadow: var(--lift-2);
}
/* ⚠️ LE CHAMP EST VIDE ET SON FOND EST GRIS ; SON PLACEHOLDER EST BLANC, ET
   C EST LE RETOUR QUI LE DIT. Un placeholder sourd se lit comme une consigne ;
   celui-ci porte une VALEUR - le pseudo qu on garde si l on n ecrit rien. */
.rsurnom-champ {
  width: 100%; min-width: 0;
  padding: .42rem .55rem;
  background: var(--ink-2); color: var(--text);
  border: var(--frame-dim); border-radius: var(--r-2);
  font-family: var(--body); font-size: var(--fs-s);
}
.rsurnom-champ::placeholder { color: var(--text); opacity: 1; }
.rsurnom-champ:focus-visible { outline: 2px solid var(--route-accent); outline-offset: 1px; }
/* ⚠️ « A LA COULEUR DU DUO » : `--route-accent` est deja borne par le plancher
   de contraste (V117) avant d etre posee sur la racine, donc un texte sombre
   dessus est lisible par construction. */
.rsurnom-ok {
  appearance: none; cursor: pointer;
  min-height: 34px; padding: .32rem .7rem;
  background: var(--route-accent); color: var(--ink-0);
  border: 0; border-radius: var(--r-2);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-xs); letter-spacing: .01em;
}
.rsurnom-ok:hover { filter: brightness(1.12); }
.rsurnom-ok:focus-visible { outline: 2px solid var(--paper); outline-offset: 2px; }
.rsurnom-msg { margin: 0; }
/* ⚠️ L APERCU EST LA COMMANDE (V78), ET LA CIBLE FAIT AU MOINS 34 px comme le
   coeur (V6), la roue (V59) et le bouton Progression (V80).

   ⚠️⚠️ V189 - L EMBLEME ETAIT ETEINT, ET LA CAUSE TIENT EN UNE LIGNE
   MANQUANTE. `fillAvatar()` pose `--avatar-color` sur son hote et injecte un
   SVG en `currentColor` ; c est `.profile-avatar` qui LIT ce jeton
   (`color: var(--avatar-color)`), et ce bouton-ci ne porte pas cette classe.
   L embleme heritait donc de la couleur du bouton, c est-a-dire de rien. Il
   lit desormais le meme jeton - une variable deja posee, jamais une seconde
   formule. C est ce qui permet au cercle de couleur de partir : l embleme dit
   ce que ce cercle disait.

   ⚠️⚠️ V199 - LE CADRE ET L APLAT DE REPOS SONT PARTIS, comme sur
   `.profile-avatar` : c est le meme objet, il suit la meme regle. CE QUI
   RESTE EST L ETAT DE SURVOL - un fond qui apparait sous le doigt n est pas
   un cadre, c est ce qui dit que la bulle est un BOUTON (lecon de la V152 sur
   le code de secours : on ne depouille pas une commande au point qu elle
   cesse de se lire comme telle). Le rembourrage part avec le cadre : sans
   filet, il n y a plus rien dont s ecarter, et la cible reste a 40 px par son
   plancher.

   ⚠️ LA TAILLE MONTE DE ~18 % (34 -> 40 px de cible, 26 -> 31 px de dessin),
   ce que le retour demande. La cible ne descend jamais sous 34.
   ⚠️ V199 - LE DESSIN PASSE A 36 px : la place que le cadre liberait. */
.ruban-bulle {
  appearance: none; cursor: pointer;
  background: transparent; padding: 0; border: 0;
  color: var(--avatar-color, var(--brand));
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 40px; min-height: 40px;
  border-radius: var(--r-pill);
  transition: background-color .16s;
}
.ruban-bulle:hover { background: var(--ink-3); }
.ruban-bulle:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
/* ⚠️ LE PSEUDO EST EN BLANC, ET C EST LE RETOUR QUI LE DIT : ce qui porte la
   couleur de chacun est l EMBLEME au-dessus, pas le nom. Deux porteurs de la
   meme information auraient fait du ruban un nuancier - la raison pour
   laquelle un filtre actif peint son TEXTE et son rond, jamais un aplat.
   ⚠️ V189 - la phrase disait l ETOILE EN DESSOUS : le cercle de couleur est
   parti, et c est l embleme qui a pris sa charge. */
.ruban-nom {
  min-width: 0; max-width: 12ch;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-m); letter-spacing: .01em;
  color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ⚠️⚠️ V189 - LE SOULIGNEMENT DIT LEQUEL DES DEUX JE SUIS, ET CHACUN VOIT
   LE SIEN. Ce n est pas une decoration : le ruban nomme deux personnes
   au-dessus d une seule barre, et rien ne disait laquelle regarde. Il ne peut
   donc PAS venir du serveur, qui ne sait pas qui regarde - il se pose cote
   page, sur le cote qui porte MON identite, que `peindreRuban()` connait deja
   (`jeSuisAGauche`).
   ⚠️ IL PREND `--route-accent`, c est-a-dire la couleur du duo quand on est
   sur la route duo : un litteral ici aurait ete un second porteur de cette
   teinte (piege nº 6).
   ⚠️ `text-decoration`, PAS UNE BORDURE : une bordure aurait ajoute de la
   hauteur au rang, donc decale l un des deux cotes par rapport a l autre. */
.ruban-cote.is-moi .ruban-nom {
  text-decoration: underline;
  text-decoration-color: var(--route-accent);
  text-decoration-thickness: 2px;
  /* V435 - 5 px au lieu de 3 : le trait passait sur les jambages (y, p, q, g, j),
     le cyrillique et les ecritures asiatiques (demande de l auteur). */
  text-underline-offset: 5px;
}
/* ⚠️⚠️ V163 (5d) - LE CRAYON EST EN EXPOSANT, ET C EST LA SEULE CHOSE QUE
   CETTE REGLE FAIT. Tout le reste - la cible de 34 px, le dessin de 13 px, le
   survol, l anneau de focus - vient de `.crayon` (V130), REEMPLOYEE telle
   quelle. Un second jeu de proprietes ici, c eut ete deux crayons qui
   divergent au premier reglage (V45).
   ⚠️⚠️ ET LA CIBLE RESTE A 34 px, ELLE NE SE RESSERRE PAS. Une premiere
   ecriture la ramenait a 26 px pour rapprocher les deux cotes : c est sous le
   plancher tactile du site (le coeur V6, la roue V59, le cercle d aide V62,
   le bouton Progression V80), et ce plancher est une regle ecrite, pas un
   gout. Ce qui se resserre, ce sont les MARGES - la cible deborde sur le
   vide, elle ne le cede pas. C est deja ce que fait le rang de favori depuis
   la V153 : « la cible pousse VERS LE HAUT, par un pseudo-element ».
   ⚠️⚠️ ET LES DEUX SELECTEURS SONT SCOPES A `.ruban-cote`, CE QUI N EST PAS
   DE LA COQUETTERIE : PIEGE Nº 2, ATTRAPE A LA MESURE. Ecrits `.ruban-crayon`
   (0,1,0) et `.ruban-crayon svg` (0,2,0), ils avaient EXACTEMENT le poids de
   `.crayon` et de `.crayon svg` - qui vivent 660 lignes plus BAS. A
   specificite egale, l ordre de source tranche : le dessin restait a 13 px et
   sa marge a 7 px, releve au DOM. « Une regle qui ne gagne que par ORDRE DE
   SOURCE ne gagne pas, elle attend. » Le site a deja sa reponse a cette
   forme-la, deux blocs plus loin : `.sheet-label .crayon svg` (V130).
   ⚠️ ON SCOPE, ON NE DEPLACE PAS le bloc sous `.crayon` : la famille
   `.ruban-*` est un espace de noms et elle se lit d un seul tenant. */
/* ⚠️⚠️ V195 - LE CRAYON SORT DU FLUX, ET CA REPARE DEUX DESALIGNEMENTS D UN
   COUP. Dans le flux, il faisait partie du rang du nom : le rang etait centre
   sur l embleme, donc LE NOM se retrouvait pousse a gauche de la moitie du
   crayon (mesure : embleme a 716, nom a 707) ; et sa hauteur de 34 px rendait
   la colonne du partenaire PLUS HAUTE que la mienne, donc `align-items: center`
   decalait les deux cotes verticalement de 5 px (mesure : 194 contre 199).
   Hors du flux, le nom se centre sur son embleme et les deux colonnes ont la
   MEME hauteur - les deux defauts tombent ensemble, parce qu ils n en
   faisaient qu un.
   ⚠️ C EST LE GESTE DU CRAYON DU POURCENTAGE (V165), DE LA ROUE CRANTEE (V191)
   ET DU GRAND BOUTON ROAD TO (V101) : un objet ancre sur un bord, qui ne prend
   pas un pixel a ce qu il legende.
   ⚠️ LA CIBLE RESTE A 34 px et DEBORDE sur la gouttiere du ruban - « la cible
   deborde sur le vide, elle ne le cede pas » (V153, V189). C est pourquoi
   `.ruban` gagne de l ecart : le dessin ne doit pas toucher le filet. */
.ruban-cote .ruban-crayon {
  position: absolute; left: 100%; top: 50%;
  transform: translateY(-50%);
  margin: 0;
}
.ruban-cote .ruban-crayon svg { width: 12px; height: 12px; margin-top: 0; }

/* ⚠️⚠️ V189 - `.ruban-etoile` EST PARTIE EN ENTIER, BALISAGE ET REGLE. Elle
   disait « cette couleur, c est cette personne » ; depuis que l embleme porte
   la couleur (voir `.ruban-bulle`), elle le REDISAIT. Deux porteurs de la meme
   information font un nuancier, pas une legende - et une classe sans porteur
   part en entier (piege nº 4). */
/* ⚠️ LE FILET EST VERTICAL, ET IL SE TIRE DE LA RAMPE (`--rule` = `--ink-5`) :
   pas un quatorzieme gris. Il grandit avec les deux colonnes (V189) : un trait
   de 18 px entre deux blocs de 60 px aurait cesse de les separer. */
.ruban-filet {
  width: 1px; height: 34px; flex: 0 0 auto;
  background: var(--rule);
}
/* ========= LA ROUE CRANTEE ET SON MENU (V191) - LE HUITIEME =============
   ⚠️⚠️ `.ruban-roue` EST PARTIE AVEC SON BOUTON : la roue a quitte le ruban
   pour la droite du bouton DUO, et elle a change de nature - elle MENAIT a
   l onglet du duo, elle OUVRE un menu. Une classe sans porteur part en entier
   (piege nº 4).

   ⚠️⚠️ LE HUITIEME MENU PAIE LE PRIX ECRIT : un seul menu ouvert a la fois
   (garde EN CAPTURE, et son declencheur n appelle PAS `stopPropagation()`),
   Echap qui rend le focus, un PREFIXE DE CLASSE a lui (`.rduo-`), et la borne
   `max-width` MESUREE a 320 px. Ce n est pas une fenetre : il n entre pas
   dans `libererDefilement()` (regle V106).

   ⚠️ LE BOUTON EST HORS FLUX (`left: 100%`), donc les deux boutons de route
   restent centres sous la barre : c est le geste du crayon du pourcentage
   (V165). La cible fait 34 px, comme le coeur (V6) et la roue de profil
   (V59). */
.rduo { position: absolute; left: 100%; top: 50%; transform: translateY(-50%); }

/* ============ LE PANNEAU DE LEGENDE DES STATUTS =========================
   ⚠️⚠️ V487 - IL N A PLUS QU UN SEUL OUVREUR : le bouton de statut pose sur
   la languette de chaque affiche (V485). Le (i) du bandeau de saga (V452),
   celui de la ligne des routes avant lui (V305, V446) et toutes leurs regles
   de placement sont partis en entier - `.legende`, `.legende-btn`, leurs etats
   au survol, leur ancrage a droite du bandeau et le `z-index` qui allait avec
   (retour de l auteur : « tu peux meme supprimer les 2 boutons »). Ce qui n a
   plus de porteur part en entier ; ce qui reste ci-dessous est le PANNEAU,
   qui, lui, a toujours le sien.
   ⚠️ `position: relative` NE REVIENT PAS SUR `.saga-band` : sa propre regle le
   porte deja, mille lignes plus bas. Deux ecrivains pour une declaration, ce
   sont deux endroits ou la retirer. */
/* ⚠️⚠️ V489 - IL FLOTTE, IL NE PEND PLUS (retour de l auteur : « le menu
   legende au clic du statut doit etre superpose, actuellement ca nous demande
   de scroller »). En absolu il restait borne par le bas de la page : ouvert
   depuis la derniere rangee, il tombait sous le pli. `fixed` + `poserPopover`
   (V209) le posent par calcul et le bornent a la fenetre sur les quatre
   bords - c est le dessin du menu des notes, au meme rang d empilement (85,
   au-dessus de la fiche), et les deux se lisent donc pareil.
   ⚠️ `left` ET `top` SONT ECRITS PAR LE MOTEUR, pas ici : ce zero n est qu un
   point de depart avant la premiere mesure. */
.legende-pop {
  position: fixed; z-index: 85;
  left: 0; top: 0;
  width: max-content; max-width: min(17rem, calc(100vw - 3rem));
  margin: 0; padding: .6rem .7rem; list-style: none;
  display: grid; gap: .45rem;
  background: var(--ink-1); border: var(--frame-dim); border-radius: var(--r-3);
  box-shadow: var(--lift-2); text-align: left;
}
@media (max-width: 900px) {
  /* Mesure a 320 px : ouvert depuis le bouton (x = 69), le panneau finissait
     PILE sur le bord de la fenetre. `100vw - 6rem` lui rend une gouttiere. */
  .legende-pop { max-width: calc(100vw - 6rem); }
}
.legende-item { display: flex; align-items: center; gap: .55rem; }
.legende-item .status-fanion { flex: 0 0 auto; }
.legende-mot {
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-xs); letter-spacing: .06em; text-transform: uppercase;
  color: var(--text);
}
.legende-sens { display: block; font-size: var(--fs-xs); color: var(--text-dim); text-transform: none; letter-spacing: 0; font-weight: 500; }
.rduo-btn {
  appearance: none; cursor: pointer;
  border: 0; background: transparent; padding: 5px; margin-left: .15rem;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 34px; min-height: 34px;
  border-radius: var(--r-pill);
  color: var(--ink-7);
  transition: color .16s, background-color .16s;
}
.rduo-btn svg { width: 18px; height: 18px; }
.rduo-btn:hover { color: var(--text-2); background: var(--ink-3); }
.rduo-btn[aria-expanded="true"] { color: var(--text-2); background: var(--ink-3); }
.rduo-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

/* ⚠️ LE MENU S OUVRE A DROITE, comme le retour le demande - donc a cote de
   la roue, pas dessous. */
.rduo-pop {
  position: absolute; z-index: 30;
  left: calc(100% + .35rem); top: 50%; transform: translateY(-50%);
  width: min(15rem, calc(100vw - 3rem));
  display: flex; flex-direction: column; gap: .45rem;
  padding: .6rem;
  background: var(--ink-1); border: var(--frame-dim); border-radius: var(--r-3);
  box-shadow: var(--lift-2);
}
/* ⚠️⚠️ SOUS 900 px IL BASCULE SOUS LA ROUE ET S ALIGNE A DROITE, ET C EST
   UNE MESURE : a 320 px la roue vit vers x = 240, donc un panneau ouvert VERS
   LA DROITE sortirait de la fenetre. Aligne par son bord droit, il s etale
   vers la gauche, la ou il y a la place. */
@media (max-width: 900px) {
  .rduo-pop {
    left: auto; right: 0; top: calc(100% + .35rem); transform: none;
  }
}
/* ============ V406 - LA BULLE DE L INVITE, SOUS DUO / GROUPE ============
   Centree sous le selecteur, qui est lui-meme centre sous la barre : elle
   tient donc a 320 px sans bascule. Meme fond, meme cadre, meme ombre que les
   deux menus voisins (`.rduo-pop`, `.legende-pop`) - une famille de bulles. */
/* ⚠️ 31 ET NON 30 : a 320 px elle passe sous le rail social fixe (`#socMenu`,
   30, plus loin dans le document) - mesure par `elementFromPoint`. */
.rinvite {
  position: absolute; z-index: 31;
  top: calc(100% + .35rem); left: 0;   /* V408 : la route est au bord gauche */
  width: min(19.5rem, calc(100vw - 2rem));
  display: grid; gap: .3rem;
  padding: .7rem .8rem .55rem;
  background: var(--ink-1); border: var(--frame-dim); border-radius: var(--r-3);
  box-shadow: var(--lift-2); text-align: left;
}
.rinvite-titre {
  margin: 0;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 800;
  font-size: var(--fs-s); letter-spacing: .01em;
  color: var(--text-2);
}
.rinvite-titre:focus { outline: none; }
.rinvite-txt { margin: 0; font-size: var(--fs-m); line-height: 1.45; color: var(--text-2); }
.rinvite-note { margin: .15rem 0 0; font-size: var(--fs-s); color: var(--text-dim); }
.rinvite-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .2rem .9rem; margin-top: .35rem; }
.rinvite-creer, .rinvite-entrer {
  appearance: none; cursor: pointer; min-height: 34px; white-space: nowrap;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 800;
  font-size: var(--fs-xs); letter-spacing: .01em;
  transition: background-color .16s, color .16s;
}
/* Le geste principal est CERNE, jamais un aplat : c est le langage de
   « S inscrire » dans la bande (V321). */
.rinvite-creer {
  padding: .35rem .85rem; border: 0; border-radius: var(--r-pill);
  background: transparent; color: var(--paper);
  box-shadow: inset 0 0 0 1.5px var(--brand);
}
.rinvite-creer:hover { background: color-mix(in srgb, var(--brand-plein) 22%, transparent); }
.rinvite-entrer {
  padding: .35rem .2rem; border: 0; background: transparent;
  color: var(--text-dim); text-decoration: underline; text-underline-offset: 3px;
}
.rinvite-entrer:hover { color: var(--paper); }
.rinvite-creer:focus-visible, .rinvite-entrer:focus-visible { outline: 2px solid var(--paper); outline-offset: 3px; }

/* ---- La ligne de reglage : le terme, le `?`, la bascule (V192) ---- */
.rduo-ligne { display: flex; align-items: center; gap: .35rem; }
.rduo-terme {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-xs); letter-spacing: .01em;
  color: var(--text-2);
}
/* ⚠️ LE `?` EST LE CERCLE D AIDE DU SITE (V62) : meme forme, meme cible. */
.rduo-aide {
  appearance: none; cursor: pointer; flex: 0 0 auto;
  width: 17px; height: 17px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--ink-7);
  border: 1px solid var(--ink-5); border-radius: 50%;
  font-family: var(--display); font-size: var(--fs-xs); font-weight: 700; line-height: 1;
}
.rduo-aide:hover, .rduo-aide[aria-expanded="true"] { color: var(--text-2); border-color: var(--ink-6); }
.rduo-aide:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* ⚠️⚠️ LE COULISSANT : GAUCHE = OFF, DROITE = ON. L etat se lit dans
   `aria-checked` (`role="switch"`), pas dans une classe - donc le dessin suit
   l attribut, et il ne peut pas mentir sur ce que le clavier annonce.
   ⚠️ LA CIBLE FAIT 34 px DE HAUT PAR SON PSEUDO-ELEMENT, pas par du padding :
   le padding aurait grandi la piste. C est le geste du rang de favori (V153) -
   la cible deborde sur le vide, elle ne le cede pas.
   ⚠️⚠️ V203 (R1) - ALLUME, IL PREND `--switch-on`, PLUS `--route-accent`. Il
   prenait la couleur de la route « qu il synchronise » (V192) ; le retour le
   refuse, et il a raison : cet interrupteur ne dit rien de la barre, il
   commande un versement. Sa couleur est donc UNIQUE, et le jeton vit sur
   `:root` - le jour ou un second coulissant arrive, il n y aura pas une
   seconde valeur a tenir d accord. Un litteral ici aurait ete un second
   porteur (piege nº 6), et ca n a pas change. */
.rduo-switch {
  appearance: none; cursor: pointer; flex: 0 0 auto; position: relative;
  width: 38px; height: 20px; padding: 0;
  background: var(--ink-4); border: 1px solid var(--ink-5);
  border-radius: var(--r-pill);
  transition: background-color .16s, border-color .16s;
}
.rduo-switch::after {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 34px; transform: translateY(-50%);
}
.rduo-switch-bille {
  position: absolute; top: 2px; left: 2px;
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--text-2);
  transition: transform .16s, background-color .16s;
}
.rduo-switch[aria-checked="true"] {
  background: color-mix(in srgb, var(--switch-on) 45%, transparent);
  border-color: var(--switch-on);
}
.rduo-switch[aria-checked="true"] .rduo-switch-bille {
  transform: translateX(18px);
  background: var(--switch-on);
}
.rduo-switch:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* ⚠️ V269 - LE JETON ETAIT `--serif`, ET IL N A JAMAIS EXISTE. La prose du
   site est `--read` (Literata) ; `--serif` ne se declare nulle part, donc
   cette ligne heritait d Archivo - une phrase qui promet la voix de la prose
   et rend celle de l interface, SANS qu aucune erreur ne se produise. C est
   le piege nº 4 par l autre bout : un NOM sans porteur. */
.rduo-aide-txt {
  margin: 0;
  font-family: var(--read); font-size: var(--fs-xs); line-height: 1.45;
  color: var(--text-dim);
}
.rduo-msg { margin: 0; }

/* ⚠️ EN ROUGE DE MARQUE, ET EN FIN DE MENU : c est la teinte du site pour ce
   qui reclame une decision - la meme que « Supprimer mon compte » (V78). */
.rduo-rompre {
  appearance: none; cursor: pointer;
  min-height: 34px; padding: .35rem .55rem;
  background: transparent; color: var(--brand);
  border: 1px solid color-mix(in srgb, var(--brand) 55%, transparent);
  border-radius: var(--r-2);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-xs); letter-spacing: .01em;
  text-align: center;
}
.rduo-rompre:hover { background: color-mix(in srgb, var(--brand) 14%, transparent); }
.rduo-rompre:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* ⚠️ SOUS 480 px, LE NOM CEDE, JAMAIS L EMBLEME NI L ETOILE. C est la regle de
   la quatrieme case de la fiche (V132) : « c est le libelle qui cede
   (ellipse), jamais la valeur ». Ici la valeur est l IDENTITE - une bulle et
   une couleur - et elle tient dans 34 px. */
/* ⚠️⚠️ V195 - L ECART NE SE RESSERRE PLUS ICI, ET C EST UNE MESURE. Il
   valait `.5rem`, puis `1.1rem` : dans les deux cas le DESSIN du crayon
   touchait le filet (releve a 375 px : encre a 180-192, filet a 187). La
   raison est que l ecart necessaire ne depend pas de la largeur de l ecran -
   il depend de la CIBLE de 34 px, qui ne retrecit jamais (plancher tactile du
   site : le coeur V6, la roue V59, le cercle d aide V62). Une valeur de moins
   a tenir d accord avec une autre. */
@media (max-width: 480px) {
  .ruban-nom { max-width: 8ch; font-size: var(--fs-s); }
  .ruban-filet { height: 30px; }
}

/* ⚠️⚠️ V189 - SUR GRAND ECRAN, LE RUBAN PREND DE LA PLACE AU LIEU DE RESTER
   SERRE AU CENTRE. Le retour demande d utiliser toute la largeur disponible
   hors page principale ; le ruban vit DANS la barre hero, donc ce qui grandit
   ici, ce sont ses ELEMENTS et l ecart entre les deux cotes - jamais une
   seconde largeur a tenir d accord avec `--shell`. */
@media (min-width: 1200px) {
  .ruban { margin-top: .75rem; }
  :root { --ruban-emb: 46px; --ruban-ecart: 2.2rem; }   /* V424 - duo et groupe */
  .ruban-cote { gap: .45rem; }
  .ruban-bulle, .ruban-plus { min-width: 52px; min-height: 52px; padding: 6px; }
  /* ⚠️ V199 - L EMBLEME SUIT SA CIBLE, exactement comme la croix du `[+]`
     juste dessous : meme regle, une ligne plus bas. Sa taille de base est
     ecrite dans `js/duo.js` (36 px) ; ceci est une surcharge responsive, pas
     un second jeu de valeurs. */
  .ruban-bulle svg { width: var(--ruban-emb); height: var(--ruban-emb); }
  .ruban-plus svg { width: 40px; height: 40px; }
  .ruban-nom { max-width: 16ch; font-size: var(--fs-base); }
  .ruban-filet { height: 44px; }
}

/* ⚠️ V213 - LES CINQ REGLES DU SELECTEUR DE SALLE SONT PARTIES AVEC LUI :
   `.salle-switch`, `.salle-label`, `.salle-select` et ses deux etats. « Une
   classe sans porteur part en entier » (piege nº 4), et `verif-selecteurs.js`
   le confirme - aucun jeton ne subsiste dans index.html ni dans les js/*.js.
   ⚠️ `.salle-dot` ETAIT RESTEE (la pastille de coequipier) ; elle est
   partie en V221 avec sa ligne. */

.stat-scope {
  margin: .42rem 0 0;
  display: flex; justify-content: center; align-items: center; gap: .25rem;
}
.stat-scope-btn {
  appearance: none; cursor: pointer;
  min-height: 30px; padding: .3rem .55rem;
  background: transparent; border: 0; border-radius: var(--r-2);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-s); letter-spacing: .01em;
  color: var(--ink-7);
  transition: color .16s, background-color .16s;
}
.stat-scope-btn:hover { color: var(--text-2); background: var(--ink-4); }
.stat-scope-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
/* ⚠️ L ETAT ACTIF SE DIT PAR LA COULEUR DU NOMBRE QU IL COMMANDE, jamais
   par un aplat : un aplat plein sur deux boutons voisins ferait de la
   commande le sujet du panneau, alors que le sujet est la case au-dessus
   (meme arbitrage que le cadre de l encart en V49). */
.stat-scope-btn--tout.is-on  { color: var(--text-2); }
/* ⚠️ V363 - LA TROISIEME PORTEE PREND `--tier-must`, QUI EST `--doom` : ce
   qu elle montre, ce sont exactement les INDISPENSABLES, et cette echelle a
   deja sa couleur. Une septieme teinte pour redire la meme chose serait un
   nuancier de plus (V54). */
.stat-scope-btn--express.is-on { color: var(--express); }
/* ⚠️⚠️ ET LES BOUTONS TIENNENT SUR UNE LIGNE A 320 px : la boite
   s enroule plutot que de deborder - un troisieme bouton ne peut pas pousser
   le panneau hors de la fenetre (regle des menus, `max-width: calc(100vw -
   3rem)`). */
.stat-scope { flex-wrap: wrap; }

/* ⚠️⚠️ V131 - LA BARRE HERO EST LA MEME SUR LES DEUX ROUTES, ET C EST LA
   DECISION DE L AUTEUR (2 septembre 2026) : « identique en design, sauf la
   couleur ». Il n y a donc PAS de seconde barre - ni second `#progressFill`,
   ni second `#progressPct` -, ce qui garde vraie la phrase de la V88 (« la
   barre hero est unique par construction ») au lieu de la renverser. Le
   sortilege de Doom et Orbitron restent : ils appartiennent a CETTE barre,
   pas a la route qu elle compte.

   ⚠️ SEULE LA TEINTE CHANGE, ET ELLE PASSE PAR le jeton de ROUTE, pose sur la
   RACINE. Les endroits qui peignent la barre lisent
   deja une variable ; les reecrire un par un aurait ete autant de valeurs a
   tenir d accord (piege nº 6). */

/* ⚠️ DES QU UNE COULEUR EST CHOISIE, LES TEINTES DERIVEES SE RECALCULENT.
   `--route-accent` est posee sur la RACINE par `peindreRoute()` (geste de
   `--star-mine`, V63) ; celles-ci s en derivent ici, par `color-mix` - deja
   employe par le libelle de `MA NOTE` depuis la V63. Les ecrire en JS aurait
   mis des formules de couleur dans un fichier qui n en porte aucune.

   ⚠️⚠️ V188 - LA CLASSE DIT « UNE TEINTE EST POSEE », PLUS « ON EST EN
   DUO », ET LE RENOMMAGE EST LA MOITIE QUI COMPTE. La route solo gagne le
   meme choix de couleur : conditionner les derivees a `is-duo` aurait donne
   une barre solo dont l accent change mais dont la nappe, l orbe et le lit
   restent VERTS - c est-a-dire exactement le defaut que cette version
   corrige, deplace d une route a l autre.

   ⚠️ `--route-spark` EST UNE VARIABLE PARCE QU ELLE A SIX PORTEURS : cinq
   etincelles et l orbe. Six litteraux clairs, c eut ete le piege nº 6 sur une
   valeur dont la divergence ne leve rien. */
.hero-progress.is-teinte {
  --route-soft: color-mix(in srgb, var(--route-accent) 55%, #FFFFFF);
  --route-deep: color-mix(in srgb, var(--route-accent) 32%, #000000);
}
.hero-progress {
  --route-spark: color-mix(in srgb, var(--route-soft) 34%, #FFFFFF);
}

/* ---- Barre de progression : "conduit d'energie" facon sortilege de Doom ---
   Aucune diagonale, aucune rayure : l'energie est faite de nappes radiales
   qui DERIVENT lentement, d'etincelles qui scintillent et d'une orbe en tete
   de barre. La barre n'est pas un rectangle arrondi : ses extremites sont
   taillees en pointe (clip-path), comme un conduit.                          */

/* ================== LA BARRE HERO (V53) ==============================
   Elle a quitte le HUD. Le bandeau qui la porte est dans le FLUX : figer
   une quatrieme bande refait exactement la barre que la V41 a refusee, et
   la V43 a ecrit la regle - ce qu'on manie EN LISANT reste sous la main,
   le reste descend dans le flux. Une jauge ne se manie pas.

   ⚠️ ET ELLE NE SUIT PAS L ONGLET, contrairement a la barre d'outils.
   La V52 a pose le partage sans le nommer : ce qui suit l'onglet, c'est ce
   qui AGIT sur la rangee ; ce qui RAPPORTE reste. La colonne de stats reste
   visible sous les quatre apercus "Bientot", la barre aussi - elle dit ou
   l'on en est du parcours entier, pas de ce qui est a l'ecran. */
/* ⚠️ LA RESERVE DU HAUT SE RESSERRE (V77) : la tete de page la precede
   desormais avec la sienne, et la barre doit se lire « juste en dessous » du
   titre. */
/* V371 - le haut se resserre (SNAPLINE plus proche de la barre, demande de l auteur) */
.hero-progress {
  --hero-haut: clamp(.1rem, .3vw, .2rem);
  --bar-h:   clamp(19px, 1.9vw, 24px);
  padding: var(--hero-haut) 0 clamp(.9rem, 2vw, 1.4rem);
}

/* La barre est CENTREE sur la page et bornee : elle ne s'etire pas d'un bord
   a l'autre de l'ecran, sinon le pourcentage se retrouve loin du regard.
   ⚠️ LA BORNE PASSE DE 760 A 980 px : en hero, une barre bornee a la largeur
   qu'elle avait dans une bande de 20 px de haut se lirait comme le meme
   objet deplace, pas comme le sujet de la zone. */
/* ⚠️ V408 - LE POURCENTAGE PASSE A DROITE DE LA BARRE (zone Parcours
   compacte) : une ligne au lieu de deux. Le crayon de couleur se pose a sa
   GAUCHE (a droite, il sortirait de la page). */
.progress {
  display: flex; align-items: center; gap: .7rem;
  width: min(var(--barre-l), 100%); margin-inline: auto;

  /* ⚠️ TROIS LITTERAUX DEVIENNENT UNE VARIABLE ET DEUX CALCULS. La pointe du
     conduit valait 10px pour une barre de 20, et l'orbe 15px avec sa marge
     de -7.5px "qui doit rester la moitie" - c'est le piege nº 6 du fichier,
     une regle ecrite au lieu d'une variable. La pointe est desormais la
     MOITIE de la hauteur (donc a 45 degres par construction, quelle que soit
     la hauteur), et l'orbe se centre par marges automatiques. */
  /* V371b - `--bar-h` est pose sur `.hero-progress` : le ticket le lit aussi */
  --bar-tip: calc(var(--bar-h) * .5);
  --orb:     calc(var(--bar-h) * .72);
}

/* ====== LE BOUTON SYNCHRONISER (V71 · V79 · FIGE EN V105 - D4) ==========
   ⚠️⚠️ V105 (D4) - IL EST DANS LA BANDE FIGEE, ET C EST UN RENVERSEMENT DE
   LA V79. Elle l avait pose dans le FLUX, troisieme colonne de
   `.pagehead-inner`, en ecrivant : « il n entre PAS dans la bande figee, qui
   se paie a chaque pixel de defilement (V53) et que la V77 vient de ramener
   de 201 px a 45 ». Le sixieme retour tranche autrement. C est sa decision,
   et son abandon s ecrit - sinon quelqu un le ressortira du flux en lisant la
   V79 et pas la V105.

   ⚠️⚠️ ET LA BANDE NE GRANDIT PAS D UN PIXEL : C EST CE QUI REND LE
   RENVERSEMENT PAYABLE. La fiche annoncait « +34 px sur TOUS les ecrans, y
   compris ceux ou le bouton n existe pas », et laissait une question ouverte -
   garder sa place a vide (une bande plus haute pour rien) ou la rendre (la
   bande saute quand on cree son compte). LES DEUX BRANCHES DISPARAISSENT, et
   pas par astuce : `.identity` porte un PLANCHER de 40 px depuis la V77,
   taille pour que le profil et les deux boutons de l invite ne fassent jamais
   sauter la ligne. Le bloc y est un enfant de plus, et il tient DANS ce
   plancher - donc rien a reserver, rien a rendre, aucun saut. Mesure :
   `--head-h` vaut toujours 45 px.

   ⚠️ IL EST DONC HORIZONTAL, LEGENDE A GAUCHE. Empile (V79), il faisait 69 px
   et aurait commande la bande. A cote du bouton, ses deux lignes de .6rem
   font ~25 px : elles tiennent dans les 40. ⚠️ ET C EST L ORDRE DU DOCUMENT,
   pas un `order` en CSS - une inversion visuelle que le document ne porte pas
   est une inversion que le clavier ne voit pas (V45, V53, V77).

   ⚠️ LA BORNE DE 6,4 rem RESTE, ET ELLE CHANGE ENCORE D EMPLOI. Elle
   reservait une place a droite de la barre (V71), puis gardait etroite la
   troisieme colonne de la tete de page (V79) ; elle empeche desormais
   « Modifie ailleurs » de pousser la bulle de profil vers la gauche. Meme
   valeur, meme raison de fond : la reserve doit etre CONNUE quelle que soit
   la phrase.

   ⚠️ LA TETE DE PAGE Y GAGNAIT, ET DEPUIS LA V110 (E4) ELLE N EXISTE
   PLUS DU TOUT. Ce bouton l avait quittee en V105, le titre en V101, et le
   menu Progression a fini par en sortir : `.pagehead` etait un conteneur
   vide portant son seul padding. La hauteur ne quitte pas le flux pour la
   bande figee - elle disparait, et le conteneur avec. */
.hero-row { display: flex; flex-direction: column; align-items: center; }

/* ================= LA CLOCHE (V167 - partie 5e) ========================
   ⚠️ 34 px, ET C EST UN GABARIT, PAS UN GOUT : c est la cible du coeur de
   favori (V6), de la roue (V59) et du bouton Progression (V82) - le plancher
   au doigt du site. Le bouton Synchroniser, lui, fait 40 px parce qu il est
   SEUL dans du vide (V105) ; la cloche est collee a lui, elle n a pas besoin
   de la meme generosite. ⚠️ Et 34 tient dans le plancher de 40 px de
   `.identity`, donc la bande figee ne grandit pas d un pixel : `--head-h` vaut
   toujours 45.

   ⚠️ `flex: 0 0 auto` N EST PAS DECORATIF (lecon de la V105) : `.identity` est
   une rangee flex qui peut manquer de place, et a 320 px avec un pseudo long
   le bouton Synchroniser s etait laisse comprimer a 31 px. Ce qui cede, c est
   le pseudo - il a deja sa borne - jamais la cible.

   ⚠️ ELLE EST SOURDE AU REPOS, ET C EST LE CONTRAIRE DU BOUTON SYNCHRONISER.
   Celui-la porte la couleur du profil parce qu il est LE chemin vers la
   synchronisation ; une cloche allumee en permanence dirait « il y a du
   neuf » alors qu il n y a rien. Elle prend la teinte au SURVOL, et le rouge
   de marque quand quelque chose attend. */
.notifs-btn {
  appearance: none; cursor: pointer;
  position: relative;
  flex: 0 0 auto;
  width: 34px; height: 34px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ink-2); color: var(--text-2);
  border: var(--frame-dim);
  border-radius: var(--r-2);
  box-shadow: var(--lift-0);
  transition: color .16s, border-color .16s, background .16s;
}
.notifs-btn svg { width: 20px; height: 20px; }
.notifs-btn:hover { color: var(--paper); border-color: var(--ink-6); background: var(--ink-3); }
.notifs-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }
.notifs-btn.is-neuf { color: var(--brand); border-color: var(--brand); }

/* ⚠️ LA PASTILLE EST POSEE EN ABSOLU SUR L ARETE : elle ne coute pas un pixel
   a la cible, donc les 34 px restent 34. ⚠️ Et elle porte un LISERE DE FOND :
   sans lui, un point rouge sur un trait rouge (le bouton en etat neuf) se
   confondrait avec le cadre. */
.notifs-dot {
  position: absolute; top: 4px; right: 4px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--brand);
  box-shadow: 0 0 0 2px var(--ink-2);
}

/* --------- LE MENU DE LA CLOCHE - cinquieme menu, cinquieme prefixe ------
   ⚠️ IL EST LE FRERE DE `.fmenu-pop` (V54) ET DE `.pmenu-pop` (V78), DONC IL
   EN REPREND LES JETONS : `--ink-3`, `--frame`, `--r-3`, `--lift-2`. Un
   popover flotte, c est l echelle d elevation de la V44 - pas des valeurs
   choisies ici.
   ⚠️ `position: absolute`, donc il ne pese pas un pixel dans la hauteur de la
   bande figee, menu ouvert comme ferme.
   ⚠️ ET IL SE BORNE A LA FENETRE : un menu ouvert pres d un bord deborde sans
   lever d erreur, et ca se mesure a 320 px (regle du menu Filtres). */
.nmenu-pop {
  position: absolute; top: calc(100% + .45rem); right: 0; z-index: 4;
  width: 300px; max-width: calc(100vw - 3rem);
  padding: .45rem;
  background: var(--ink-3);
  border: var(--frame); border-radius: var(--r-3);
  box-shadow: var(--lift-2);
}
/* ⚠️⚠️ V197 (2-2) - LA TETE DU MENU PORTE LE FILET, PAS LE TITRE NI LA LISTE.
   Sur le titre seul il aurait la largeur du titre ; sur la liste, il aurait
   appartenu a ce qu il separe. Meme motif que `.account-modif` (V152) : filet
   + marge, et rien d autre - un troisieme dessin de separateur aurait ete un
   dessin de trop. */
.nmenu-tete {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  padding-bottom: .3rem; margin-bottom: .35rem;
  border-bottom: var(--frame-dim);
}
.nmenu-titre {
  margin: .1rem .35rem;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 800;
  font-size: var(--fs-xs); letter-spacing: .01em;
  color: var(--text-dim);
}
/* ⚠️ 34 px DE HAUT, COMME `.nmenu-acte` JUSTE DESSOUS : c est le plancher
   tactile du site (le coeur V6, la roue V59, le bouton Progression V82, la
   cloche elle-meme), et deux cibles du meme menu qui ne feraient pas la meme
   taille seraient une irregularite qu on lit sans savoir la nommer.
   ⚠️ IL N EST PAS TEINTE : la regle du bouton plein (V51) veut qu un seul
   objet appelle, et dans ce menu c est « Accepter ». Vider est possible, il
   n est pas propose. */
.nmenu-vider {
  appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; flex: 0 0 auto;
  min-height: 34px; padding: .2rem .5rem;
  background: transparent; color: var(--text-dim);
  border: 0; border-radius: var(--r-1);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-xs); letter-spacing: .01em;
  transition: color .16s, background .16s;
}
.nmenu-vider:hover { color: var(--paper); background: var(--ink-2); }
.nmenu-vider:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* ⚠️ LA LISTE DEFILE EN INTERNE, ELLE NE POUSSE PAS LE MENU HORS DE L ECRAN :
   cinquante lignes, c est plus haut que n importe quel telephone. ⚠️ `dvh`
   APRES `vh`, comme la fiche et le panneau de profil (V142) : `vh` se resout
   sur la GRANDE fenetre, celle qu on a barre d URL repliee.
   ⚠️ ET `overscroll-behavior: contain` : sans lui, arrive en bout de course, le
   geste CHAINE vers la page derriere - on croit que le menu ne defile pas
   alors que c est la page qui a pris le geste. */
.nmenu-liste {
  display: flex; flex-direction: column; gap: 1px;
  max-height: 60vh; max-height: 60dvh;
  overflow-y: auto; overscroll-behavior: contain;
}
/* ⚠️⚠️ V168 - LA LIGNE EST UN CONTENEUR, LE TEXTE EST LE BOUTON. La V167 en
   faisait un `<button>` pleine ligne ; des qu elle porte « Accepter » et
   « Refuser », ce serait un bouton DANS un bouton - invalide, et deja ecrit
   pour la ligne d un ami (V85 : « le bouton entoure la bulle et le nom, pas
   la LIGNE »). */
.nmenu-item {
  display: flex; flex-direction: column;
  border-radius: var(--r-2);
  color: var(--text);
}
.nmenu-aller {
  appearance: none; cursor: pointer; text-align: left;
  display: flex; flex-direction: column; gap: .12rem;
  width: 100%; padding: .5rem .55rem;
  background: transparent; border: 0; border-radius: var(--r-2);
  color: inherit;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 600;
  font-size: var(--fs-s); line-height: 1.3;
  transition: background-color .16s, color .16s;
}
.nmenu-aller:hover { background: var(--ink-2); color: var(--paper); }
.nmenu-aller:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

/* ⚠️ LES DEUX GESTES SONT SOUS LE TEXTE, PAS A COTE : a 320 px, deux boutons
   a droite d une phrase de deux lignes laissent 90 px au texte. Empiles, ils
   ne coutent qu une rangee - et la liste defile deja en interne. */
.nmenu-actes {
  display: flex; flex-wrap: wrap; align-items: center; gap: .3rem;
  padding: 0 .55rem .5rem;
}
/* ⚠️ 34 px DE HAUT, ET C EST LE PLANCHER TACTILE DU SITE - celui du coeur de
   favori (V6), de la roue (V59), du bouton Progression (V82) et de la cloche
   elle-meme. Mesure avant correction : 22 px, une cible SOUS le plancher, sans
   qu aucune erreur ne se produise. C est le padding qui la donne, jamais une
   hauteur ecrite a cote du texte. */
.nmenu-acte {
  appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; min-height: 34px;
  padding: .28rem .6rem;
  background: transparent; color: var(--text-2);
  border: var(--frame-dim); border-radius: var(--r-1);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-xs); letter-spacing: .01em;
  transition: color .16s, border-color .16s, background .16s;
}
.nmenu-acte:hover { color: var(--paper); border-color: var(--ink-6); background: var(--ink-2); }
.nmenu-acte:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.nmenu-acte[disabled] { opacity: .5; cursor: default; }
/* ⚠️ UN SEUL DES DEUX EST TEINTE, ET C EST « Accepter » : c est la regle du
   bouton plein de l accueil (V51) - un seul appelle, l autre reste possible. */
.nmenu-acte--oui { color: var(--star-mine); border-color: color-mix(in srgb, var(--star-mine) 42%, transparent); }
.nmenu-acte--oui:hover { color: var(--star-mine); border-color: var(--star-mine); }
.nmenu-dit {
  font-family: var(--read); font-style: italic;
  font-size: var(--fs-xs); color: var(--brand);
}
/* ⚠️ CE QUI EST NEUF PORTE UN FILET A GAUCHE, PAS UN APLAT : six lignes
   peintes en fond feraient du menu un nuancier (regle du menu Filtres, V54) -
   et le filet dit « depuis la derniere fois » sans rien effacer d autre. */
.nmenu-item.is-neuf { box-shadow: inset 2px 0 0 var(--brand); }
.nmenu-qui { color: var(--star-mine); font-weight: 800; }
.nmenu-quand {
  font-size: var(--fs-micro); letter-spacing: .07em; text-transform: uppercase;
  color: var(--text-dim);
}
.nmenu-vide {
  margin: .2rem .35rem .3rem;
  font-family: var(--read); font-style: italic;
  font-size: var(--fs-s); color: var(--text-dim);
}

/* ⚠️⚠️ V408 - SYNCHRONISER DESCEND DE LA BANDE ET PERD SON CADRE (demande de
   l auteur) : un petit bouton BLANC, la fleche, l heure du dernier echange.
   Il vit a droite de la ligne de tete de la zone Parcours.
   ⚠️ LE MOT « Synchronise » SE TAIT A L ECRAN QUAND L HEURE LE DIT (il reste
   dans le nom accessible, `peindreSync`). « Jamais envoye » et « Modifie
   ailleurs » n ont pas d heure : ils se lisent, et le second en rouge de
   marque - la seule chose que la V79 ne pouvait pas perdre. */
.syncnow { display: flex; justify-content: flex-end; min-width: 0; }
.syncnow-btn {
  appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: .35rem;
  min-height: var(--pastille); padding: 0 .45rem;
  background: transparent; color: #FFFFFF;
  border: 0; border-radius: var(--r-2);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-xs); letter-spacing: .01em;
  font-variant-numeric: tabular-nums;
  transition: color .16s, background-color .16s;
}
.syncnow-btn svg { width: 17px; height: 17px; flex: 0 0 auto; }
.syncnow-btn:hover { background: var(--ink-3); }
.syncnow-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

/* ⚠️ IL TOURNE PENDANT QU'IL TRAVAILLE, ET SEULEMENT LA. Pas de point vert
   permanent : le site n'a pas de synchronisation continue et ne doit pas faire
   croire qu'il en a une. */
@keyframes syncspin { to { transform: rotate(360deg); } }
.syncnow-btn.is-spinning svg { animation: syncspin .9s linear infinite; }

.syncnow-when { display: inline-flex; white-space: nowrap; }
.syncnow-time { color: var(--text-2); }
.syncnow:has(.syncnow-time:not([hidden])) .syncnow-word {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.syncnow.is-stale .syncnow-btn { color: var(--brand); }

/* ⚠️ LA REGLE GLOBALE DE prefers-reduced-motion COUPE DEJA LA ROTATION - donc
   sans ce repli, « en train de travailler » ne se verrait plus du tout. Un
   etat qui ne s'annonce plus est pire qu'une animation de trop. */
@media (prefers-reduced-motion: reduce) {
  .syncnow-btn.is-spinning { color: var(--doom); border-color: var(--doom); }
}

/* ⚠️⚠️ V79 - LE SEUIL DE REPLI CALCULE A DISPARU AVEC LA POSITION ABSOLUE.
   La V71 ecrivait : « la barre est bornee a 980 px, la legende a 6,4 rem et
   sa gouttiere a 20 - il faut donc 980 + 2 x 122 + 40, soit ~1264 px, le
   seuil est pose a 1280 ». C'etait un nombre accorde a la main pour tomber
   juste, donc a recalculer au premier mot ajoute a la legende : le piege
   nº 6 sous sa forme la plus discrete. Dans une cellule de grille, il n'y a
   plus rien a accorder. Le seul repli qui reste est celui de la tete de page
   elle-meme, a 700 px, ou la grille passe a UNE colonne (voir plus bas). */

/* ⚠️⚠️ V188 - LE LIT DE LA BARRE PREND LA TEINTE, LUI AUSSI. Il etait
   ecrit en dur (`#0C1A0C` -> `#070B07`), c est-a-dire en VERT : une jauge
   doree posee sur un lit vert se lit comme un defaut d affichage, et c est le
   symptome rapporte. Il se derive donc de `--route-accent`, en variante
   SOURDE - c est deja le rapport `--route-accent` / `--route-deep`.
   ⚠️ LE VERT RESTE LE DEFAUT, IL NE DEVIENT PAS UNE EXCEPTION : sans couleur
   choisie, `--route-accent` vaut `--doom`, et le lit retombe sur le meme vert
   presque noir qu avant. Il n y a pas de second jeu de variables. */
.progress-bar {
  position: relative; flex: 1 1 auto; min-width: 0; height: var(--bar-h);
  background:
    radial-gradient(120% 200% at 50% 50%,
                    color-mix(in srgb, var(--route-accent) 9%, #060609) 0%,
                    color-mix(in srgb, var(--route-accent) 4%, #040406) 100%);
  /* ⚠️ LES DEUX PROPORTIONS SONT MESUREES, PAS CHOISIES : a 9 % et 4 % du vert
     Doomsday, le lit rend rgb(5,23,8) puis rgb(6,14,9) - c est-a-dire le meme
     lit qu avant, au point pres (`#0C1A0C` -> `#070B07`). Un premier releve a
     14 % / 6 % rendait une bande doree LISIBLE derriere la jauge : on ne juge
     pas ce genre de valeur sur le papier (regle des halos du podium, V154). */
  overflow: hidden;
  /* Extremites en pointe : le "conduit" */
  clip-path: polygon(0 50%, var(--bar-tip) 0, calc(100% - var(--bar-tip)) 0, 100% 50%,
                     calc(100% - var(--bar-tip)) 100%, var(--bar-tip) 100%);
  /* drop-shadow (et non box-shadow) : seule ombre qui suit le clip-path */
  filter: drop-shadow(0 0 8px color-mix(in srgb, var(--route-accent) 38%, transparent));
}

/* Nappes d'energie : deux lueurs radiales qui derivent en sens inverse */
.progress-fill {
  display: block; position: relative; height: 100%; width: 0%;
  /* ⚠️ V188 - LES DEUX NAPPES ETAIENT VERTES EN DUR, donc elles REPEIGNAIENT
     en vert le degrade de route qu elles recouvrent. Elles se derivent
     desormais de `--route-soft`, qui vaut `--doom-soft` par defaut. */
  background:
    radial-gradient(65% 180% at 22% 50%,
                    color-mix(in srgb, var(--route-soft) 46%, transparent), transparent 72%),
    radial-gradient(55% 160% at 68% 50%,
                    color-mix(in srgb, var(--route-soft) 32%, transparent), transparent 72%),
    linear-gradient(90deg, var(--route-deep) 0%,
                    color-mix(in srgb, var(--route-accent) 70%, #000000) 58%,
                    var(--route-accent) 100%);
  background-size: 190% 100%, 150% 100%, 100% 100%;
  background-repeat: no-repeat;
  animation: doomswirl 7s ease-in-out infinite alternate;
  /* ⚠️ V336 (S8) - ELLE GARDE `width`, ET C EST UNE DECISION MESUREE. La
     critique demandait `transform` sur les DEUX jauges ; celle-ci porte TROIS
     fonds en `background-size` (les nappes, le degrade) plus les etincelles du
     `::after` : un `scaleX` les ECRASERAIT horizontalement - le sortilege
     deviendrait un aplat etire, et le prix serait visible. La jauge d amis,
     elle, est un aplat : elle passe a `transform` juste au-dessus. */
  transition: width .45s cubic-bezier(.4, 0, .2, 1);
}
@keyframes doomswirl {
  from { background-position: 0% 50%, 100% 50%, 0 0; }
  to   { background-position: 100% 50%, 0% 50%, 0 0; }
}

/* Etincelles : de petits points qui scintillent dans le flux */
.progress-fill::after {
  content: ""; position: absolute; inset: 0;
  /* ⚠️ V188 - LES CINQ ETINCELLES LISENT `--route-spark`. Elles etaient
     ecrites en cinq blancs verdatres : sur une barre doree ou violette, elles
     restaient vertes. Les cinq opacites, elles, ne bougent pas - c est le
     scintillement, pas la teinte. */
  background:
    radial-gradient(2px 2px at 11% 32%, color-mix(in srgb, var(--route-spark) 95%, transparent), transparent 62%),
    radial-gradient(1.5px 1.5px at 31% 70%, color-mix(in srgb, var(--route-spark) 85%, transparent), transparent 62%),
    radial-gradient(2px 2px at 52% 26%, color-mix(in srgb, var(--route-spark) 90%, transparent),  transparent 62%),
    radial-gradient(1.5px 1.5px at 71% 66%, color-mix(in srgb, var(--route-spark) 85%, transparent), transparent 62%),
    radial-gradient(2px 2px at 88% 42%, color-mix(in srgb, var(--route-spark) 95%, transparent), transparent 62%);
  animation: doomsparks 3.2s steps(1) infinite;
}
@keyframes doomsparks {
  0%, 100% { opacity: .85; } 22% { opacity: .3; } 48% { opacity: 1; } 74% { opacity: .45; }
}

/* Orbe d'energie en tete de barre */
/* ⚠️ CENTREE PAR MARGES AUTOMATIQUES, PAS PAR `transform` : la pulsation
   (`doompulse`) occupe deja le `transform` avec son `scale`, et les deux se
   seraient ecrases. Les marges auto n'ont besoin d'aucune moitie tenue a la
   main - c'etait `margin-top: -7.5px` pour `height: 15px`. */
.progress-fill::before {
  content: ""; position: absolute; top: 0; bottom: 0; right: -3px;
  width: var(--orb); height: var(--orb); margin-block: auto;
  border-radius: 50%;
  background: radial-gradient(circle, var(--route-spark) 0%, var(--route-soft) 42%, transparent 72%);
  animation: doompulse 1.9s ease-in-out infinite;
}
@keyframes doompulse {
  0%, 100% { opacity: .55; transform: scale(.9); }
  50%      { opacity: 1;   transform: scale(1.15); }
}

/* Decoupage en 4 segments : separateurs a 25 / 50 / 75 % */
.progress-ticks {
  position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(90deg,
    transparent 0 calc(25% - 2px), rgba(0, 0, 0, .8) calc(25% - 2px) 25%);
}

/* ============ V484 - LES CINQ FORMES DE BARRE, ET CHACUNE EST LA BARRE ====
   ⚠️⚠️ RENVERSEMENT DE LA V456, ET LE RETOUR DE L AUTEUR LE DIT MOT POUR MOT :
   « il faut que la barre elle-meme se modifie a la personnalisation elle-meme
   entiere, pas juste appliquer un visuel sur celle existante ». La V456 avait
   choisi `.progress-ticks` - la nappe posee PAR-DESSUS la jauge - comme
   porteur unique de quatre formes sur cinq, en notant que « elle ne coute
   aucun noeud de plus ». Le prix de cette economie, c est que les quatre
   formes PEIGNAIENT DU NOIR DE PAGE sur une barre qui, elle, ne changeait
   pas : trois barres identiques sous trois pochoirs.
   ⚠️⚠️ ELLES SONT MAINTENANT DECOUPEES, PAS COUVERTES. Chaque forme pose un
   `mask-image` sur `.progress-bar` : la ou le masque est transparent, la
   barre N EXISTE PAS - on voit la page au travers, l ombre portee suit la
   decoupe, et le remplissage s arrete net a chaque bord. La difference se
   mesure : sous un pochoir, le pixel est du noir opaque ; sous un masque,
   c est le fond de la page, quel qu il soit.
   ⚠️ ET LA FORME CHANGE AUSSI CE QUI N EST PAS LA DECOUPE : les bouts en
   pointe du defaut (`clip-path`) ne conviennent ni a une pellicule ni a une
   rangee de crans, et la pellicule demande de la hauteur. Une forme, c est la
   silhouette entiere.
   ⚠️ AUCUN NOMBRE DE TITRES ICI : `--barre-crans` et `--barre-masque` sont
   ecrits par `js/app.js` a chaque peinture de la jauge, donc Complet et
   Express n ont pas la meme barre sans qu une ligne le dise.
   ⚠️ `h1` EST LE DEFAUT ET IL N A PAS DE REGLE : c est la barre ci-dessus,
   telle quelle, avec ses quatre filets. Une regle qui redit le defaut, c est
   deux endroits a tenir d accord le jour ou le defaut change.
   ⚠️ LE PREFIXE `-webkit-` ACCOMPAGNE CHAQUE `mask-*` : sans lui, Safari
   d avant 15.4 n applique AUCUN masque - donc il ne montre pas une forme
   degradee, il montre la forme par defaut en croyant montrer l autre. */

/* Les quatre formes qui decoupent n ont plus rien a faire de la nappe. */
:root[data-forme-barre="h2"] .progress-ticks,
:root[data-forme-barre="h3"] .progress-ticks,
:root[data-forme-barre="h4"] .progress-ticks,
:root[data-forme-barre="h5"] .progress-ticks { display: none; }

/* --- H2 - LA PELLICULE : une bande de film, perforee de part en part.
   Plus haute (c est une pellicule, pas un filet), a bouts droits, et les
   perforations sont des TROUS - trois bandes de masque : le rang du haut, le
   corps plein, le rang du bas. */
:root[data-forme-barre="h2"] .hero-progress { --bar-h: clamp(24px, 2.4vw, 30px); }
:root[data-forme-barre="h2"] .progress-bar {
  clip-path: none; border-radius: 2px;
  -webkit-mask-image:
    repeating-linear-gradient(90deg, #000 0 5px, transparent 5px 10px),
    linear-gradient(#000, #000),
    repeating-linear-gradient(90deg, #000 0 5px, transparent 5px 10px);
  mask-image:
    repeating-linear-gradient(90deg, #000 0 5px, transparent 5px 10px),
    linear-gradient(#000, #000),
    repeating-linear-gradient(90deg, #000 0 5px, transparent 5px 10px);
  -webkit-mask-size: 100% 26%, 100% 48%, 100% 26%;
  mask-size: 100% 26%, 100% 48%, 100% 26%;
  -webkit-mask-position: 0 0, 0 50%, 0 100%;
  mask-position: 0 0, 0 50%, 0 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* --- H3 - LES ROUTES : la barre est coupee la ou une route finit et la
   suivante commence. Les coupes viennent de `--barre-masque`, ecrit par le
   moteur depuis la MEME liste que la jauge. */
:root[data-forme-barre="h3"] .progress-bar {
  clip-path: none; border-radius: 3px;
  -webkit-mask-image: var(--barre-masque, none);
  mask-image: var(--barre-masque, none);
}

/* --- H4 - LE FIL ET L ORBE : la barre se reduit a un trait, l orbe reste.
   ⚠️ L ORBE NE SUIT PLUS LA HAUTEUR (elle vaut 6 px) : il garde une taille a
   lui, sinon il disparaitrait avec la barre. C est la seule des cinq que la
   V456 avait deja faite sur la barre elle-meme ; elle ne bouge pas. */
:root[data-forme-barre="h4"] .hero-progress { --bar-h: 6px; }
:root[data-forme-barre="h4"] .progress { --orb: 15px; }
:root[data-forme-barre="h4"] .progress-bar { clip-path: none; border-radius: var(--r-pill); }

/* --- H5 - LES CRANS : un cran par titre, et la barre EST le compteur. Elle
   devient une rangee de briques separees par du vide.
   ⚠️ SOUS 640 px LES CRANS SE TOUCHENT (62 traits dans 300 px) : la forme
   retombe alors sur la barre par defaut, masque retire. Une barre illisible
   n est pas un choix qu on propose. */
:root[data-forme-barre="h5"] .progress-bar {
  clip-path: none; border-radius: 2px;
  -webkit-mask-image: repeating-linear-gradient(90deg,
    #000 0 calc(100% / var(--barre-crans, 62) - 2px),
    transparent calc(100% / var(--barre-crans, 62) - 2px) calc(100% / var(--barre-crans, 62)));
  mask-image: repeating-linear-gradient(90deg,
    #000 0 calc(100% / var(--barre-crans, 62) - 2px),
    transparent calc(100% / var(--barre-crans, 62) - 2px) calc(100% / var(--barre-crans, 62)));
}
@media (max-width: 640px) {
  :root[data-forme-barre="h5"] .progress-bar {
    clip-path: polygon(0 50%, var(--bar-tip) 0, calc(100% - var(--bar-tip)) 0, 100% 50%,
                       calc(100% - var(--bar-tip)) 100%, var(--bar-tip) 100%);
    -webkit-mask-image: none;
    mask-image: none;
  }
  :root[data-forme-barre="h5"] .progress-ticks { display: block; }
}

/* POURCENTAGE : centre SOUS la barre (V77 - B2), a la couleur de Doomsday,
   dans la seule police "cosmique" du site.

   ⚠️ IL EST REPASSE DESSOUS, ET C EST UN RENVERSEMENT DE LA V53. Elle l avait
   monte au-dessus en ecrivant « en hero, le nombre est le sujet, la barre le
   montre » ; la maquette B2 le remet dessous, ou il LEGENDE la jauge - comme
   de la V5.1 a la V52. ⚠️ IL GARDE SON CORPS (V58, ~2.1rem) : un nombre de
   33 px pose sous une barre est encore lu comme un sujet, et le rapetisser
   aurait ete un second changement dans le meme geste.

   ⚠️ ET C EST L ORDRE DU DOM QUI A CHANGE, PAS UN `order` EN CSS : rien
   n est reordonne ici, contrairement a la vue liste.

   ⚠️ ET C EST LE SEUL ELEMENT DU SITE EN `--cosmic`, ce qui ne change pas :
   Orbitron ne sert qu'a ce nombre, et l'agrandir est exactement l'usage pour
   lequel une geometrique de ce genre est lisible. */
/* ============ LE CRAYON EN INDICE DU POURCENTAGE (V165 - 5d) ============
   ⚠️⚠️ LE NOMBRE RESTE CENTRE SOUS LA BARRE, ET C EST LA SEULE CHOSE QUE
   CE BLOC A EU A GARANTIR. Le crayon est en `position: absolute` : il ne
   pese PAS un pixel dans la largeur de `.pctmenu`, donc la boite fait
   exactement la largeur du nombre et se centre comme avant. Pose dans le
   flux, il aurait decale le pourcentage de 17 px vers la gauche - un
   deplacement que rien a l ecran n expliquerait. C est le geste du grand
   bouton ROAD TO (V101) : ancre, pas dans le flux.
   ⚠️ `bottom: 0` FAIT L INDICE PAR CONSTRUCTION, `left: 100%` le pose a la
   droite du nombre : deux ancrages, aucun nombre tenu a la main (piege
   nº 6). Le crayon du ruban (V163) est son symetrique en EXPOSANT.
   ⚠️ ET LE SELECTEUR EST SCOPE, comme celui du ruban et pour la meme raison
   mesuree en V163 : `.pctmenu-btn` seul aurait eu le poids de `.crayon`, qui
   vit plus BAS dans le fichier - donc l ordre de source aurait tranche, et
   `.crayon` aurait gagne. Piege nº 2. */
.pctmenu { position: relative; display: flex; justify-content: flex-end; flex: 0 0 auto; }
/* ⚠️⚠️ ET C EST `align-items` QUI FAIT L INDICE, PAS UNE MARGE NEGATIVE.
   `.crayon` pose son dessin en HAUT de sa cible de 34 px (`flex-start`,
   V130) - c est ce qui en fait un exposant sur le ruban (V163). Ici il doit
   tomber en BAS : on retourne l alignement, et le dessin se pose contre le
   bas du nombre PAR CONSTRUCTION. Releve a la mesure : ecrit `margin-top: 0`
   sans toucher a l alignement, le crayon restait EN HAUT - un exposant qui
   se prenait pour un indice, sans qu aucune erreur ne se produise.
   ⚠️ LES 6 px SONT LE SYMETRIQUE EXACT du `margin-top: 6px` du crayon de
   ruban : meme dessin, meme retrait, deux sens. */
.pctmenu .pctmenu-btn {
  position: absolute; right: 100%; bottom: 0;   /* V408 : a gauche du nombre */
  align-items: flex-end;
}
.pctmenu .pctmenu-btn svg { width: 13px; height: 13px; margin: 0 0 6px; }

/* ⚠️ LE PANNEAU S OUVRE VERS LE BAS ET SE CENTRE SUR LE NOMBRE, pas sur le
   crayon : centre sur le crayon il partirait vers la droite de la page, et
   un menu ouvert pres d un bord deborde SANS LEVER D ERREUR (regle des
   quatre menus). La borne `100vw - 3rem` est celle des trois autres, et elle
   se mesure a 320 px.
   ⚠️ IL PREND UN APLAT (`--ink-1`) COMME LE MENU PROGRESSION : sans fond, il
   flotterait au-dessus des affiches - illisible (V110).
   ⚠️ `z-index: 30`, LE RANG DES MENUS, ET IL N Y A PAS DE CONFLIT : un seul
   menu est ouvert a la fois, et celui-ci s ouvre vers le BAS - il ne peut
   pas rencontrer la bande figee (40). */
.pctmenu-pop {
  position: absolute; top: calc(100% + .55rem); right: 0;   /* V408 : le nombre est au bord droit */
  z-index: 30;
  width: max-content; max-width: min(300px, calc(100vw - 3rem));
  padding: .7rem .8rem;
  background: var(--ink-1); border: var(--frame-dim);
  border-radius: var(--r-3); box-shadow: var(--lift-2);
  display: grid; gap: .5rem; text-align: left;
}
/* ⚠️ LE TITRE ET LA NOTE EMPRUNTENT LEUR ECHELLE AUX DEUX CLASSES QUI
   DISENT DEJA CA AILLEURS - `.sheet-label` pour l intitule, `.sheet-hint`
   pour la phrase. On ne les REEMPLOIE pas telles quelles ici (elles portent
   des marges taillees pour le panneau de profil), mais on ne cree pas une
   troisieme echelle : memes corps, memes teintes. */
.pctmenu-titre {
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-xs); letter-spacing: .01em;
  color: var(--text-2);
}
/* ⚠️ V188 - `.pctmenu-note` EST PARTIE AVEC SA PHRASE (« vous pouvez la
   changer tous les deux »), retiree par le retour. Elle n avait pas d autre
   porteur : une classe sans porteur part en entier (piege nº 4). */
/* V304 - « Couleur par defaut » : un bouton cerne, a la cible tactile du site. */
/* V473 - le choix de forme, sous la couleur, dans le menu de la barre. */
.pctmenu-sep { border: 0; border-top: 1px solid var(--rule); margin: .75rem 0 .6rem; }
.pctmenu-formes { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .45rem; }
.pctmenu-formes .chip-f { font-size: var(--fs-xs); }

.pctmenu-defaut {
  min-height: var(--pastille); padding: 0 .8rem;
  background: transparent; color: var(--text-2);
  border: var(--frame-dim); border-radius: var(--r-2);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 600;
  font-size: var(--fs-xs); cursor: pointer;
}
.pctmenu-defaut:hover { color: var(--paper); border-color: var(--ink-6); }
.pctmenu-defaut:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

.progress-pct {
  font-family: var(--cosmic);
  font-weight: 700;
  font-size: clamp(1.15rem, 2.6vw, 1.5rem);   /* V408 : il legende la barre, a cote d elle */
  line-height: 1;
  letter-spacing: .1em;
  color: var(--route-accent);
  text-shadow: 0 0 18px color-mix(in srgb, var(--route-accent) 55%, transparent);
  font-variant-numeric: tabular-nums;
  text-align: center;
}

/* ===================== LE PROCHAIN TITRE (V226) =========================
   ⚠️ C EST UNE COMMANDE, ET ELLE EN A L AIR (lecon V152) : un filet au repos,
   un fond au survol. Depouillee, elle se lirait comme une legende de la
   barre - or elle MENE quelque part.
   ⚠️ 34 px DE HAUT, le plancher tactile du site (`--pastille`).
   ⚠️ LE TITRE CEDE, JAMAIS LE MOT NI LA DUREE : a 320 px un titre long
   s ellipse, et `min-width: 0` le lui permet - un enfant de flex refuse
   sinon de descendre sous son contenu (V85, V132).
   ⚠️ AUCUNE COULEUR NOUVELLE : le mot est `--text-dim`, le titre `--text`,
   et un recommande prend `--tier-reco` sur son seul mot de statut - la
   couleur du statut, pas une septieme famille. */
/* ⚠️⚠️ V359 - LES SELECTEURS `.soiree*` SONT PARTIS, LES REGLES SONT RESTEES.
   La V262 ecrivait « la soiree duo PARTAGE ce dessin, elle ne le recopie
   pas » : elle vivait donc dans des selecteurs GROUPES avec `.suivant*`.
   ⚠️ ON RETIRE DES SELECTEURS, JAMAIS UNE PLAGE DE LIGNES - c est la faute
   que la V194 a payee, et `tools/verif-selecteurs.js` existe pour ca : chaque
   regle ci-dessous perd UN nom et garde son porteur, `.suivant`.
   ⚠️ ET AUCUNE COULEUR NEUVE : le mot reste `--text-dim`. */
/* ⚠️ V371b - LE TICKET : vignette a gauche, texte a droite sur deux ou trois
   lignes (le mot, le titre, puis statut et duree). */
.suivant {
  appearance: none; cursor: pointer;
  display: inline-grid; grid-template-columns: auto minmax(0, 1fr); align-items: center;
  gap: var(--sp-3); max-width: 100%; min-width: 0; text-align: left;
  min-height: var(--pastille); margin-top: .35rem;
  padding: .38rem .8rem;
  background: transparent; border: var(--frame-dim); border-radius: var(--r-2);
  color: var(--text);
  font-family: var(--display);
  transition: background-color .16s, border-color .16s;
}
.suivant:hover { background: var(--ink-3); border-color: var(--ink-6); }
.suivant:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
.suivant-affiche {
  display: block; width: 30px; height: 45px; object-fit: cover;
  border-radius: var(--r-1); background: var(--ink-3);
}
.suivant-texte {
  display: flex; flex-wrap: wrap; align-items: baseline; column-gap: var(--sp-2);
  min-width: 0;
}
.suivant-mot, .suivant-titre { flex: 1 0 100%; }
.suivant-mot {
  font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-xs); letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-dim);
}
.suivant-titre {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-stretch: var(--ch-ui); font-weight: 800; font-size: var(--fs-base);
}
.suivant-meta {
  flex: 0 0 auto;
  font-size: var(--fs-s); font-weight: 600; color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
/* ⚠️ SOUS 520 px LE TITRE PASSE A LA LIGNE AU LIEU DE S ELLIPSER : mesure a
   320 px, l ellipse ne laissait que « Captain Am… » - une commande qui ne
   dit plus ou elle mene. La boite grandit en hauteur, jamais en largeur. */
@media (max-width: 520px) {
  .suivant-titre { white-space: normal; }
}
.suivant-statut {
  flex: 0 0 auto;
  /* V453 - c est un mot de STATUT : il prend l ambre des suggeres, pas le
     jaune d avertissement (le prochain titre n est jamais un requis). */
  font-size: var(--fs-s); font-weight: 700; color: var(--statut-suggere);
}
/* ---- V398 - LE PARCOURS : COMPLET | EXPRESS ---------------------------------
   Deux segments dans une capsule (`--r-pill`, le rayon des pastilles de
   filtre). Le segment choisi MONTE en s eclaircissant (`--ink-3` + `--lift-0`,
   la regle de profondeur), jamais un aplat colore : c est un reglage, pas le
   sujet. Express choisi prend `--tier-must`, la couleur de ce qu il garde -
   exactement l arbitrage du bouton de portee (V363). Cible : 34 px. */
.parcours {
  display: flex; flex-direction: column; align-items: center; gap: .3rem;
}
.parcours-choix {
  display: inline-flex; padding: 2px; gap: 2px;
  border: 1px solid var(--ink-5); border-radius: var(--r-pill);
}
.parcours-btn {
  appearance: none; cursor: pointer;
  min-height: 34px; padding: 0 1rem;
  background: transparent; border: 0; border-radius: var(--r-pill);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-s); letter-spacing: .01em;
  color: var(--text-dim);
  transition: color .16s, background-color .16s;
}
.parcours-btn:hover { color: var(--text-2); }
.parcours-btn:focus-visible { outline: 2px solid var(--text); outline-offset: 1px; }
.parcours-btn.is-on { background: var(--ink-3); box-shadow: var(--lift-0); color: var(--text); }
.parcours-btn--express.is-on { color: var(--express); }
/* ============ V408 - LA ZONE PARCOURS, COMPACTE (demande de l auteur) =======
   Trois lignes basses au lieu d une colonne centree : (1) le menu Parcours et
   Complet / Express a gauche, « Road to » et son compte a rebours au centre,
   Synchroniser a droite ; (2) la barre et son pourcentage ; (3) la route
   (legende, Solo / Duo / Groupe) a gauche, le prochain titre a droite. La
   bibliotheque remonte d autant : c est elle, le sujet de la page.
   ⚠️ LES TROIS LIGNES ONT LA LARGEUR DE LA BARRE (980 px au plus) : c est
   elle qui donne l axe, les deux autres s y alignent. */
.hero-tete, .hero-pied {
  width: min(var(--barre-l), 100%); margin-inline: auto;
}
.hero-tete {
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center; column-gap: var(--sp-3);
  margin-bottom: .45rem;
}
.hero-tete-g {
  grid-column: 1; justify-self: start;
  display: flex; align-items: center; gap: .6rem; min-width: 0;
}
.hero-tete .roadto { grid-column: 2; }
/* ⚠️⚠️ V446 - SOLO / DUO / GROUPE SOUS COMPLET / EXPRESS, AU CENTRE (planche
   M2). Un rang a lui, sur toute la largeur de la tete : c est la derniere
   chose qu on lit avant la barre. */
.hero-tete .route-switch { grid-column: 1 / -1; grid-row: 3; justify-self: center; margin: .1rem 0 0; }

/* ============ V409 - « ROAD TO » SE CHOISIT ================================
   Le bloc devient un bouton sans cadre : un fond au survol, un chevron qui dit
   qu il s ouvre. Son panneau descend, centre sur lui, avec la liste des films
   encore a sortir ; le choisi porte le nom en couleur et une coche. */
.roadto { position: relative; z-index: 30; min-width: 0; }
.roadto-btn {
  appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: .4rem; max-width: 100%;
  min-height: var(--pastille); padding: .2rem .55rem;
  background: transparent; color: inherit;
  border: 0; border-radius: var(--r-2);
  transition: background-color .16s;
}
.roadto-btn:hover:not(:disabled), .roadto-btn[aria-expanded="true"] { background: var(--ink-3); }
.roadto-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.roadto-btn:disabled { cursor: default; }
.roadto-btn:disabled .roadto-chevron { display: none; }
.roadto-chevron { width: 12px; height: 12px; flex: 0 0 auto; color: var(--text-dim); transition: transform .16s; }
.roadto-btn[aria-expanded="true"] .roadto-chevron { transform: rotate(180deg); }
.roadto-pop {
  position: absolute; top: calc(100% + .35rem); left: 50%; transform: translateX(-50%);
  width: max-content; min-width: 15rem; max-width: calc(100vw - 2rem);
  display: grid; gap: .1rem; padding: .35rem;
  background: var(--ink-1); border: var(--frame-dim); border-radius: var(--r-3);
  box-shadow: var(--lift-2);
}
.roadto-opt {
  appearance: none; cursor: pointer;
  display: flex; align-items: baseline; justify-content: space-between; gap: 1.2rem;
  min-height: var(--pastille); padding: .4rem .6rem .4rem 1.5rem; position: relative;
  background: transparent; color: var(--text); text-align: left;
  border: 0; border-radius: var(--r-2);
  font-family: var(--display);
}
.roadto-opt:hover { background: var(--ink-3); }
.roadto-opt:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.roadto-opt[aria-pressed="true"]::before {
  content: ""; position: absolute; left: .6rem; top: 50%;
  width: .45rem; height: .25rem; margin-top: -.2rem;
  border: solid var(--doom); border-width: 0 0 2px 2px; transform: rotate(-45deg);
}
.roadto-opt-nom {
  font-stretch: var(--ch-ui); font-weight: 800; font-size: var(--fs-s);
  letter-spacing: .12em; text-transform: uppercase; white-space: nowrap;
}
.roadto-opt-quand { font-size: var(--fs-xs); color: var(--text-dim); font-variant-numeric: tabular-nums; white-space: nowrap; }
.hero-tete .syncnow { grid-column: 3; justify-self: end; }
/* ⚠️ V420 - AU-DESSUS DE 620 PX, SYNCHRONISER QUITTE LA LARGEUR DE LA BARRE
   pour le coin droit de la page, a l aplomb de la langue et sur la ligne de
   « Road to » (demande de l auteur). `.hero-row` a la largeur de l en-tete :
   son bord droit EST celui du drapeau, sans un nombre recopie. */
/* ⚠️⚠️ V448 - IL SE DECROCHE DE LA LIGNE DE « ROAD TO » (retour de l auteur :
   « dans le coin, tout en laissant un peu d espace »). Il etait a l aplomb du
   compte a rebours, donc on le lisait comme un morceau de cette ligne ; il
   remonte dans l AIR que la bande reserve deja sous elle (`--roadto-air`), et
   il n a donc rien coute en hauteur.
   ⚠️ LA REMONTEE EST LE JETON MOINS DEUX PIXELS, PAS UN NOMBRE : si l air
   change un jour, le bouton suit sans qu on y pense, et il garde les deux
   pixels qui l empechent de toucher l ombre de la bande. */
@media (min-width: 621px) {
  .hero-row { position: relative; }
  .hero-tete .syncnow {
    position: absolute; right: .2rem;
    top: calc((var(--roadto-air) - var(--coin-air)) * -1);   /* V458 - le jeton partage */
    height: var(--pastille); align-items: center;
  }
}
/* ⚠️⚠️ V423 - SUR ORDINATEUR (1200 px et plus), LA ZONE PARCOURS S OUVRE
   (demande de l auteur) :
   - Complet / Express descendent sur leur propre ligne, CENTREE SUR LA PAGE,
     entre « Road to » et la barre ;
   - ⚠️ V432 - la barre ne va PLUS de la recherche au coin de profil (demande
     de l auteur : « trop large ») : elle garde `--barre-l`, centree - la
     gauche de la barre et la droite du pourcentage sont a egale distance ;
   - la ligne du dessous prend la meme largeur : la route a gauche, « J ai
     devant moi » au centre, le prochain et son compteur a droite.
   ⚠️ `.hero-tete-g` devient `display: contents` : ses deux enfants deviennent
   des cases de la grille de la tete, sans que le document change d ordre. */
@media (min-width: 1200px) {
  .hero-row .hero-tete { width: auto; align-self: stretch; margin-inline: 0; }
  .hero-row .hero-tete-g { display: contents; }
  .hero-row .hero-tete .parcours { grid-column: 1 / -1; grid-row: 2; justify-self: center; margin-top: .35rem; }   /* V458 - les 35 px de vide sont tombes a 2 px : on en rend 4 pour que les deux lignes respirent */

  /* ⚠️⚠️ V455 - SUR ORDINATEUR, LES TROIS ROUTES PRENNENT LE COIN SUPERIEUR
     GAUCHE, EN COLONNE (retour de l auteur : « a gauche il n y a rien sur la
     vue PC, ca me perturbe »). C est la place qu occupait le compte a rebours
     avant la V408, et elle est vide depuis. La zone Parcours cesse d etre un
     axe central flanque de deux vides.
     ⚠️ SEULEMENT AU-DESSUS DE 1200 px, ET C EST LA DEMANDE : « pour la vue PC
     uniquement ». Sous ce seuil la colonne de gauche n existe pas - la tete y
     est une pile centree - et trois mots empiles y voleraient la hauteur que
     le telephone compte au pixel.
     ⚠️ ELLE ENJAMBE LES DEUX RANGS de la tete : le compte a rebours occupe le
     premier, Complet / Express le second, et la colonne se centre sur les
     deux. Sans `grid-row: 1 / -1` elle aurait pousse un troisieme rang. */
  /* ⚠️⚠️ V458 - ELLE SORT DU FLUX DE LA GRILLE, ET C EST LA CAUSE DES DEUX
     VIDES (retour de l auteur). Mesure avant : la colonne faisait 99 px de
     haut et ENJAMBAIT les deux rangs de la tete ; comme c est le plus grand
     element qui fixe la hauteur d un rang, elle ecartait « Road to » de la
     bande (50 px) et Complet / Express de « Road to » (35 px) - deux vides au
     MILIEU pour un bloc pose sur le BORD. Posee en absolu dans `.hero-row`,
     elle ne commande plus aucune hauteur : les deux rangs se referment sur ce
     qu ils contiennent vraiment.
     ⚠️ MEME ANCRAGE QUE SYNCHRONISER, EN FACE (V448) : les deux coins partent
     de l air que la bande reserve deja et gardent `--coin-air`. Le vide de
     gauche que la V455 est venue combler reste comble - le bloc n a pas bouge
     d un pixel horizontalement, il a seulement cesse de pousser ses voisins.
     ⚠️ `.hero-row` EST DEJA `position: relative` AU-DESSUS DE 621 px (V420) :
     aucun nouveau repere, donc rien a tenir d accord. */
  .hero-row .hero-tete .route-switch {
    position: absolute; left: 0; top: calc((var(--roadto-air) - var(--coin-air)) * -1);
    flex-direction: column; align-items: flex-start; gap: .1rem; margin: 0;
  }
  /* ⚠️ PLUS IMPOSANT, ET L ACTIF PLUS ENCORE (demande de l auteur) : les deux
     mots sourds montent deja d un cran, et le mot actif est une fois et demie
     plus grand qu eux. Il est ALLUME - un halo a la couleur de la route, pas
     un cadre : l auteur a ecrit deux fois « pas d encadrement ». */
  .hero-row .hero-tete .route-opt { font-size: var(--fs-l); padding: .1rem 0; min-height: 0; }
  .hero-row .hero-tete .route-opt[aria-pressed="true"] {
    font-size: calc(var(--fs-l) * 2); line-height: 1.02; font-stretch: var(--ch-chiffre);
    text-shadow: 0 0 22px color-mix(in srgb, var(--route-accent) 45%, transparent);
  }
}
.hero-pied {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: .25rem var(--sp-3); margin-top: .4rem;
}
/* ⚠️⚠️ V482 - LA PAIRE : « Prochain » puis « J ai devant moi », cote a cote et
   centres sur la page (retour de l auteur : « plus imposants... cote a cote,
   a toi de determiner l ordre »).
   ⚠️ PROCHAIN D ABORD : l un nomme le geste suivant, l autre l horizon. On lit
   ce qu on fait maintenant avant ce qu on a devant soi.
   ⚠️ LE FILET ENTRE LES DEUX N EST PAS UN ORNEMENT : sans lui, deux boutons
   sans cadre colles l un a l autre se lisent comme un seul objet long. */
.hero-paire {
  flex: 1 1 100%; min-width: 0;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: .2rem var(--sp-2);
}
.hero-paire .devant { position: relative; }
@media (min-width: 621px) {
  .hero-paire .devant::before {
    content: ""; position: absolute; left: calc(var(--sp-2) / -2); top: 22%; bottom: 22%;
    width: 1px; background: var(--ink-5);
  }
}
.hero-pied .route-switch { margin: 0; justify-content: flex-start; }

/* Complet / Express : deux mots, pas deux blocs. Chacun a SA couleur - Complet
   en blanc, Express au vert de ce qu il garde (`--tier-must`, le choix de la
   V398) ; le choisi s allume et se souligne, l autre reste teinte et sourd. */
.parcours-choix { border: 0; padding: 0; gap: .1rem; }
/* V431 - plus grands (demande de l auteur) : deux classes, la regle de base vit plus haut. */
.parcours-choix .parcours-btn { font-size: var(--fs-base); padding: 0 .7rem; }
.parcours-choix .parcours-btn.is-on::after { left: .7rem; right: .7rem; }
.parcours-btn { position: relative; padding: 0 .55rem; color: color-mix(in srgb, var(--text) 62%, var(--ink-0)); }
.parcours-btn--express { color: color-mix(in srgb, var(--express) 62%, var(--ink-0)); }
.parcours-btn:hover { color: var(--text); }
.parcours-btn--express:hover { color: var(--express); }
.parcours-btn.is-on { background: transparent; box-shadow: none; color: var(--text); }
/* V423 - Express choisi prend la couleur du film de « Road to » ; cette regle
   vient APRES la precedente, qui le repeignait en blanc (meme poids, ordre). */
.parcours-btn--express.is-on { color: var(--express); }
.parcours-btn.is-on::after {
  content: ""; position: absolute; left: .55rem; right: .55rem; bottom: 5px;
  height: 2px; border-radius: 2px; background: currentColor;
}

/* Le prochain titre : une ligne, secondaire. La vignette reste (c est le repere),
   le cadre part ; le titre s ellipse plutot que de faire grandir la zone. */
/* ⚠️⚠️ V482 - LE COMPTEUR A CHANGE D ADRESSE : il suit le POURCENTAGE, sur la
   ligne de la barre, et non plus le prochain titre. Les deux disent la meme
   progression dans deux unites - « 42 % » et « 26/61 » - et se lisent
   ensemble. Le filet vertical reste : c est lui qui dit « deux mesures, un
   seul sujet » plutot que « deux boutons ». */
.suivi-compte {
  appearance: none; cursor: pointer; flex: 0 0 auto;
  display: inline-flex; align-items: center; min-height: var(--pastille);
  padding: 0 .55rem 0 .7rem; position: relative;
  background: transparent; border: 0; border-radius: var(--r-2);
  color: var(--text-2);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-s); letter-spacing: .04em; font-variant-numeric: tabular-nums;
  transition: background-color .16s, color .16s;
}
.suivi-compte::before {
  content: ""; position: absolute; left: 0; top: 25%; bottom: 25%;
  width: 1px; background: var(--ink-5);
}
.suivi-compte.is-express { color: var(--express); }
.suivi-compte:hover { background: var(--ink-3); color: var(--text); }
.suivi-compte.is-express:hover { color: var(--express); }
.suivi-compte:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
/* ⚠️⚠️ V482 - `.hero-droite` ET `--cpt-decal` SONT PARTIS AVEC LEUR OBJET, ET
   C EST LE GAIN PRINCIPAL DE CETTE VERSION. La V432 alignait le filet du
   compteur sur le debut du pourcentage en MESURANT l ecart entre deux boites
   eloignees de toute la largeur de la page : un observateur de taille sur
   chacune, un observateur de classe sur la racine, un ecouteur de
   redimensionnement, et une valeur posee en propriete CSS. C est cette mesure
   qui a produit le debordement de 399 px de la V471 (l ecart s ajoutait au
   precedent). Deux objets qu on veut voisins se RANGENT COTE A COTE ; on ne
   les aligne pas a distance. Trente lignes de moteur et quatre regles de
   feuille partent avec le probleme.
   ⚠️ ET LA COLONNE DE DROITE N A PLUS DE CONTENU : le prochain titre rejoint
   « J ai devant moi » au centre. */
@media (min-width: 621px) {
  /* Complet / Express seuls sur leur ligne, centres (comme au-dessus de 1200 px) */
  .hero-tete .hero-tete-g { grid-column: 1 / -1; grid-row: 2; justify-self: center; }
  /* La ligne est plus etroite : « PROCHAIN » et la duree montent au-dessus du
     titre, qui garde la largeur pour lui. Trois classes : les regles
     `.hero-pied .suivant-*` vivent plus bas (piege nº 2). */
  .hero-row .hero-pied .suivant-texte {
    display: grid; grid-template-columns: auto auto minmax(0, 1fr);
    grid-template-areas: "mot statut meta" "titre titre titre";
    column-gap: .45rem; align-items: baseline;
  }
  .hero-row .hero-pied .suivant-mot { grid-area: mot; font-size: var(--fs-s); line-height: 1.15; }   /* V482 */
  .hero-row .hero-pied .suivant-statut { grid-area: statut; font-size: var(--fs-micro); line-height: 1.15; }
  .hero-row .hero-pied .suivant-meta { grid-area: meta; font-size: var(--fs-micro); line-height: 1.15; }
  .hero-row .hero-pied .suivant-titre { grid-area: titre; line-height: 1.2; }
}
/* ⚠️ V482 - PLUS IMPOSANTS (retour de l auteur). La vignette passe de 20x30 a
   26x39, le titre de `--fs-m` a `--fs-base`, et le mot « Prochain » quitte le
   plancher d etiquette. Ce n est pas un agrandissement general : ce sont les
   DEUX boutons que le retour nomme, et eux seuls. */
.hero-pied .suivant {
  margin: 0; flex: 0 1 auto; min-width: 0; max-width: min(100%, 27rem);
  padding: .2rem .5rem; gap: .55rem;
  border-color: transparent;
}
.hero-pied .suivant-affiche { width: 26px; height: 39px; }
.hero-pied .suivant-texte { flex-wrap: nowrap; }
.hero-pied .suivant-mot, .hero-pied .suivant-titre { flex: 0 1 auto; }
.hero-pied .suivant-mot { flex: 0 0 auto; }
.hero-pied .suivant-titre { min-width: 0; font-size: var(--fs-base); }   /* V482 */

/* Sous 620 px : le compte a rebours prend la ligne du haut, Complet / Express
   et Synchroniser la suivante ; le prochain passe sous la route. */
@media (max-width: 620px) {
  .hero-tete { grid-template-columns: minmax(0, 1fr) auto; row-gap: .15rem; }
  .hero-tete .roadto { grid-column: 1 / -1; grid-row: 1; justify-self: center; }
  .hero-tete-g { grid-row: 2; }
  .hero-tete .syncnow { grid-column: 2; grid-row: 2; }
  .hero-pied .suivant { max-width: 100%; }
  .hero-pied .suivant-titre { white-space: nowrap; }
  /* ⚠️ V426 - LA LIGNE DU BAS SE CENTRE (demande de l auteur) : la route
     (Solo / Duo / Groupe), puis « J ai devant moi », puis le prochain et son
     compteur - trois rangs centres sur la page, plus rien cale a gauche. */
  .hero-pied .route-switch { justify-content: center; }

  /* ================= V502 - LE WIDGET DU BAS (planche T3 amendee) ========
     « [complet/express], [j ai devant moi], [prochains] dans un widget en bas
     centre » (retour de l auteur).

     ⚠️⚠️ C EST LA THESE DE LA PLANCHE T3 TENUE JUSQU AU BOUT : le site promet
     « savoir quoi regarder ensuite », et sur telephone il ouvrait sur 279 px
     de reglages avant la premiere affiche. Les trois reglages ne partent pas,
     ils changent d ETAGE : ils quittent le premier ecran pour le bas de la
     vitre, ou le pouce les trouve sans remonter la page.
     ⚠️ CE N EST PAS UN ORDRE CSS : `js/app.js` DEPLACE les deux blocs dans le
     document (V45). Le clavier et le lecteur d ecran lisent ce qu on voit.
     ⚠️ LA HAUTEUR EST UNE VARIABLE MESUREE (`--dock-h`), pas un nombre : le
     widget flotte, donc la page doit se reserver sa hauteur SOUS elle, et
     cette hauteur change quand « Prochain » se cache.
     ⚠️ ET IL S ARRETE AU BORD SUR, pas au bord de la vitre : `env(safe-area-
     inset-bottom)` est ce qui l empeche de passer sous la barre de geste des
     telephones sans bouton. */
  .hero-row .hero-pied {
    position: fixed; z-index: 60;
    left: 50%; transform: translateX(-50%);
    bottom: calc(.45rem + env(safe-area-inset-bottom, 0px));
    width: min(100% - .9rem, 26rem);
    margin: 0; padding: .3rem .5rem .35rem;
    flex-direction: row; align-items: center; justify-content: center; gap: .2rem;
    background: color-mix(in srgb, var(--ink-1) 94%, transparent);
    border: var(--frame-dim); box-shadow: var(--lift-2);
    -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  }
  /* Vide, il ne se dessine pas : un cadre pose sur rien serait un objet mort
     (V18). Les deux seuls blocs qui peuvent disparaitre portent `hidden`. */
  .hero-row .hero-pied:not(:has(> .hero-paire > .suivant:not([hidden]))):not(:has(> .hero-paire > .parcours:not([hidden]))) {
    display: none;
  }
  /* « Prochain » prend sa ligne, les deux reglages partagent la suivante. */
  .hero-row .hero-paire { flex: 1 1 100%; flex-direction: row; flex-wrap: wrap; gap: 0 var(--sp-2); }
  .hero-row .hero-paire > .suivant { flex: 1 1 100%; justify-content: center; }
  .hero-row .hero-paire > .parcours, .hero-row .hero-paire > .devant { flex: 0 0 auto; }
  .hero-row .hero-paire > .parcours { display: block; }
  /* ⚠️ LES TROIS BLOCS SE RESSERRENT POUR TENIR EN DEUX LIGNES, ET C EST
     MESURE : a pleine taille le widget faisait 131 px - un sixieme de la
     vitre d un telephone, pour trois reglages. « Prochain » garde sa ligne
     (c est le sujet), Complet / Express et « J ai devant moi » partagent la
     suivante : 170 + 208 px ne tenaient pas dans 320. */
  .hero-row .hero-pied .suivant { padding: .1rem .35rem; gap: .4rem; }
  .hero-row .hero-pied .suivant-affiche { width: 20px; height: 30px; }
  .hero-row .hero-pied .suivant-titre { font-size: var(--fs-m); }
  .hero-row .hero-pied .suivant-mot { font-size: var(--fs-xs); }
  .hero-row .hero-pied .parcours-choix .parcours-btn { font-size: var(--fs-s); padding: 0 .3rem; }
  .hero-row .hero-pied .parcours-choix .parcours-btn.is-on::after { left: .3rem; right: .3rem; }
  .hero-row .hero-pied .devant-btn { padding: .1rem .25rem; gap: .2rem; font-size: var(--fs-s); }
  .hero-row .hero-pied .devant-btn > svg:first-child { width: 14px; height: 14px; }
  .hero-row .hero-pied .devant-mot { font-size: var(--fs-xs); }
  .hero-row .hero-pied .devant-puce { padding: 0 .25rem; }
  /* ⚠️⚠️ LE PANNEAU DES DUREES SE POSE SUR LA VITRE, PAS SUR SON BOUTON.
     Ancre sur « J ai devant moi » il s ouvrait vers le BAS, hors de l ecran
     (mesure : bas a 796 px pour une vitre de 780), et sa largeur de 343 px
     centree sur un bouton colle a droite sortait de 49 px a droite. Pose en
     ~fixed~, il se centre sur la vitre et monte juste au-dessus du widget -
     dont il lit la hauteur MESUREE, la meme variable que la page.
     ⚠️ LA SPECIFICITE MONTE D UN CRAN, et c est la meme lecon qu au-dessus :
     la regle de base de ~.devant-pop~ vit PLUS BAS dans le fichier et gagnait
     a poids egal par l ordre. */
  .hero-row .hero-pied .devant-pop {
    position: fixed; top: auto;
    bottom: calc(var(--dock-h, 4.5rem) + .9rem + env(safe-area-inset-bottom, 0px));
    left: 50%; transform: translateX(-50%);
    width: min(26rem, calc(100vw - 1.2rem));
    max-height: calc(100vh - var(--dock-h, 4.5rem) - var(--head-h) - 2rem);
    overflow-y: auto;
  }

  /* La page se reserve la hauteur du widget : sans cela, la derniere affiche
     et le bas de page vivraient dessous. */
  :root.a-dock body { padding-bottom: calc(var(--dock-h, 4.5rem) + .9rem); }
}

/* La carte visee se SIGNALE une fois, puis se tait : un cadre qui s eteint,
   pas une pulsation qui continue (regle du defile, V35). */
@keyframes signale {
  0%   { box-shadow: 0 0 0 2px var(--route-accent), 0 0 0 7px color-mix(in srgb, var(--route-accent) 25%, transparent); }
  100% { box-shadow: 0 0 0 2px transparent, 0 0 0 14px transparent; }
}
.card.is-signale .poster-wrap { animation: signale 1.4s cubic-bezier(.16, 1, .3, 1) 1; }
@media (prefers-reduced-motion: reduce) {
  .card.is-signale .poster-wrap { animation-duration: .01s; }
}

/* ==================== LA RANGEE D ONGLETS (V52) ========================
   Cinq sections, une seule livree - les quatre autres ouvrent un apercu
   "Bientot" qui DIT ce qu elles porteront. C est le traitement de l onglet
   Equipe du profil depuis la V18 : rien de faux, rien de mort.

   ⚠️ ELLE EST FIGEE DEPUIS LA V77, ET C'EST LA REGLE V52 QUI CEDE. Elle
   disait : « un onglet se prend une fois, donc il vit dans le flux - le figer
   collerait une quatrieme bande en haut de l ecran ». L argument protegeait
   d une bande de PLUS ; la fusion en retire une. La regle V43 - ce qu on
   manie EN LISANT reste sous la main - l emporte donc ici, et les deux ne
   pouvaient pas tenir ensemble dans une seule ligne.

   ⚠️ ELLE SE REPLIE EN RANGS, elle ne defile pas (`flex-wrap: wrap`).
   Meme arbitrage que la barre d onglets du profil en V41 : un defilement
   horizontal cachait "Equipe" et "Compte" SANS LE DIRE. Deux rangs visibles
   valent mieux qu un rang tronque.

   ⚠️ SON FILET DU BAS EST PARTI SUR L EN-TETE (V77). Dans une ligne a trois
   colonnes, un `border-bottom` pose sur la colonne du milieu n aurait trace
   qu un bout de filet ; c est `.topbar` qui porte desormais la separation -
   la seule que B5 garde. */
.tabs { grid-column: 2; grid-row: 1; min-width: 0; }
.tabs-inner {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: .1rem .3rem; padding: 0;
}

.tab {
  appearance: none; cursor: pointer;
  position: relative; padding: .5rem .8rem .55rem;
  background: transparent; border: 0;
  color: var(--text-dim);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-s); letter-spacing: .06em; text-transform: uppercase;
  transition: color .16s;
}
.tab:hover { color: var(--text); }
.tab:focus-visible { outline: 2px solid var(--brand); outline-offset: -3px; border-radius: var(--r-1); }

/* L onglet courant porte un FILET, pas un fond : la rangee est posee sur le
   filet du bas, et un aplat en ferait une pastille de plus a distinguer des
   six pastilles de filtre juste dessous. */
.tab::after {
  content: ""; position: absolute; left: .55rem; right: .55rem; bottom: .12rem;
  height: 2px; background: var(--brand); border-radius: 2px;
  opacity: 0; transition: opacity .16s;
}
.tab.is-on { color: var(--paper); }
.tab.is-on::after { opacity: 1; }

/* ============ LE GRAND BOUTON ROAD TO (V101 - D3) =====================
   L ONGLET « Ma Road To » DEVIENT LE TITRE DU SITE. Il garde sa classe
   `.tab`, son `data-tab` et son `role` - donc `setTab` et la delegation de
   clic ne changent pas d une ligne (V45 : jamais deux mecanismes pour la
   meme chose).

   ⚠⚠ IL EST EN `position: absolute`, ET C EST TOUTE LA VERSION. Un
   element qui DEPASSE d une bande figee est soit DEDANS - et il en grandit
   la hauteur, ce que la V77 a passe une version entiere a empecher - soit
   DEHORS, hors du flux. Il est dehors : il ne pese pas un pixel dans la
   hauteur mesuree, et ce qui le suit est repousse par le `margin-bottom` de
   `.topbar`, pas par sa hauteur a lui.

   ⚠ SON CONTENANT EST `.topbar`, PAS `.tabs`. La bande est `sticky`, donc
   positionnee, donc c est elle le bloc contenant. `left: 50%` y designe le
   centre de la PAGE - le meme axe que les onglets qu il remplace, sans
   qu une grille ait a le centrer.

   ⚠ IL EST ANCRE SUR LE BAS, PAS SUR LE HAUT. Ancre en haut, il faudrait
   connaitre la hauteur de la bande pour savoir de combien il pend - or elle
   change avec les replis (un rang, deux, trois). Ancre en bas, il pend de
   `--roadto-drop` quelle que soit cette hauteur, par construction.

   ⚠ ROUGE DE MARQUE A ECRITURE BLANCHE, PAS COULEUR DE PROFIL. Un titre
   de site est GENERAL, et le rouge est « tout ce qui est general » (V63) ;
   la couleur de profil dit « vous ». ⚠ Et la dette de contraste (V63/V79)
   aurait mordu la : la roue accepte n importe quelle teinte, donc un titre
   de site presque noir sur noir.

   ⚠⚠ `--brand-deep` TROUVE ENFIN UN PORTEUR. CLAUDE.md le listait comme
   code mort depuis la V44 - « declare, reference nulle part ». Il sert ici
   a l etat NON courant : le bouton reste rouge en permanence (c est le
   titre), il fonce quand on regarde un autre ecran. Un filet `::after`
   n aurait rien dit - il n y a plus de rangee ou se distinguer.

   ⚠ PAS D OMBRE PORTEE : `.topbar` en pose deja une sous elle, et le
   bouton en est l enfant. Deux elevations superposees sur le meme bord
   feraient un halo, pas une arete (V44). */
.tab.tab--roadto {
  /* ⚠️ V407 - IL TIENT DANS LA BANDE, IL N EN PEND PLUS (demande de
     l auteur : « sans augmenter sa hauteur »). Centre sur le rang, pas sur
     la bande : `top` rattrape la moitie du `padding-top` de `.topbar`. Il a
     la hauteur de la pastille (34 px), le gabarit de la cloche et du profil. */
  position: absolute; left: 50%;
  top: calc(50% + .17rem);
  transform: translate(-50%, -50%);
  display: grid; place-items: center;
  height: calc(var(--pastille) * var(--caps-plus, 1));
  padding: 0 calc(1.15rem * var(--tete-ech, 1) * var(--caps-plus, 1));
  max-width: calc(100vw - 2.5rem);
  /* ⚠️⚠️ V332 - VISUEL_B (S4) : RENVERSEMENT ECRIT. Ce qui precede disait
     « rouge de marque a ecriture blanche » : le bouton est desormais PLEIN et
     CLAIR, en capsule, et porte le nom SNAPLINE. Le rouge reste l accent
     (liens, S inscrire, Tout accepter) ; la marque se dit par la forme.
     Etat non courant : un gris clair (--text-dim), jamais un aplat sombre qui
     le ferait disparaitre dans la bande. Encre --ink-0 : 8,9:1 et 17:1. */
  background: var(--text-dim); color: var(--ink-0);
  border-radius: var(--r-pill);
  font-family: var(--bande);
  /* V441 - elle grandit avec la bande, par la meme echelle que tout le rang ;
     V449 - avec le supplement qui n appartient qu a elle. */
  font-size: calc(var(--fs-l) * var(--tete-ech, 1) * var(--caps-plus, 1));
  font-stretch: var(--ch-affiche); text-transform: uppercase;
  font-weight: 800; letter-spacing: .08em; line-height: 1;
  white-space: nowrap;
  transition: background .16s, color .16s;
}
/* ⚠️⚠️ V440 - EN HAUT DE PAGE, LA CAPSULE EST PLUS GRANDE (planche D1) : la
   marque s affiche a l arrivee, puis se range des le premier geste de
   defilement. Elle reste ancree au centre du rang (V407) : seule sa taille
   change, jamais son ancrage. */
/* ⚠️ V441 - PLUS AUCUNE TAILLE EN DUR ICI : la capsule se mesure en
   `--pastille` et en `--tete-ech` comme le reste du rang, donc elle grandit
   AVEC lui et jamais a son rythme. Sa taille ne s anime pas : la bande change
   en une image (voir `.topbar`). */
.tab.tab--roadto::after { content: none; }
.tab.tab--roadto.is-on { background: var(--text); color: var(--ink-0); }
.tab.tab--roadto:hover { background: var(--paper); color: var(--ink-0); }
.tab.tab--roadto:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }

/* --- La pastille "Bientot" (V52) - TOUT CE QUI RESTE DE L APERCU ---------
   ⚠️ V96 : le PANNEAU d apercu et sa table `SOON` n existent plus (voir
   « L ONGLET DUO » plus bas). Cette classe, elle, garde deux porteurs -
   `#amisSoon` et `#duoSoon`, les etats « le serveur ne repond pas » des deux
   ecrans sociaux - et une classe garde sa place tant qu elle a un porteur. */
.soon-flag {
  margin: 0 0 .7rem;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 800;
  font-size: var(--fs-micro); letter-spacing: .22em; text-transform: uppercase;
  color: var(--tier-bonus);
}

/* ⚠️⚠️ V193 - L ONGLET DU DUO (V96) N EXISTE PLUS, ET `.duo-tab` AVEC LUI.
   Son commentaire disait que « il n y a presque rien ici, ce qui est le
   point » : l ecran empruntait ses classes a `.amis-*`. C est encore vrai, et
   c est ce qui a rendu sa suppression gratuite - la fenetre de creation
   reemploie exactement les memes.
   ⚠️ LA REGLE QU IL PORTAIT SURVIT A SON ECRAN : « on herite de la parade avec
   le dessin » - `.amis-item .amis-bulle` et `.amis-qui .amis-handle` sont
   SCOPEES parce que `.profile-avatar` et `.profile-handle` les battaient par
   ordre de source (piege nº 2). En reemployant ces classes, il n y a pas une
   seconde parade a ecrire ni a oublier. */

/* ================= L ONGLET EQUIPES (V98 - D2) ========================
   Le duo et l equipe partageaient une section parce qu ils partageaient un
   onglet ; le retour du 1er septembre les SEPARE, et l onglet lui-meme a
   disparu dans le menu de profil.
   ⚠️ DEUX REGLES, ET AUCUNE VALEUR NEUVE. `--sect-pad` (V92) porte deja le
   rythme de section, partage par `.roadmap`, `.archive`, `.favs`,
   `.amis-tab`, `.account-tab` et `.duo-tab` : une SEPTIEME copie du litteral
   aurait ete le piege nº 6 que la V92 a passe une version a retirer. Et
   44 rem est la meme colonne de lecture que les cinq autres.
   ⚠️ LA CARTE, ELLE, N A AUCUNE REGLE ICI : `.team-card` et `.method-soon`
   arrivent de la V41 et n ont JAMAIS ete scopees. Seul le
   CONTENEUR est neuf - c est ce qui a rendu gratuit le deplacement de
   l onglet Compte (V61) et celui des explications (V62). */
/* V373 - `.equipe-tab` et `.equipe-inner` sont parties avec l ancien ecran. */

/* ================== LE CRAYON (V130) - REVENU EN V194 ====================
   ⚠️⚠️ IL AVAIT ETE SUPPRIME PAR ERREUR EN V193, ET C EST LA PLUS CHERE DES
   FAUTES DE CETTE PARTIE. La V193 a retire d un bloc les regles `.duo-*` de
   l onglet du duo, PAR PLAGE DE LIGNES - et `.crayon` habitait au milieu de ce
   bloc sans lui appartenir. Elle a TROIS porteurs, tous ailleurs :
   `#profileEditPseudo` (le HUD, parti en V202), `#pctMenuBtn` (le menu de
   couleur) et les deux `.ruban-crayon`. Prives de leur regle, les trois
   boutons de l epoque sont retombes
   sur le style NATIF d un `<button>` : un rectangle gris clair, en plein
   milieu de la barre hero. C est ce que le retour signale.
   ⚠️⚠️ LA LECON EST ECRITE DANS `CLAUDE.md` ET ELLE VIENT D ETRE PAYEE :
   « la relecture se fait par ECRAN et par FICHIER ENTIER, pas par recherche du
   mot qu on croit avoir change » - ici, pas par PLAGE DE LIGNES. Une regle
   partagee peut habiter dans le bloc d un objet qu on supprime.

   ⚠️ LA CIBLE FAIT 34 px, ET LE DESSIN 13 : « en exposant » decrit le DESSIN
   et sa position, pas la surface cliquable - confondre les deux est
   exactement ce qui produit un bouton qu on ne peut pas viser.
   ⚠️ `vertical-align: super` SUR UN ELEMENT DE 34 px LEVERAIT LA LIGNE : on ne
   touche pas a l alignement de la boite, on remonte le DESSIN par une marge.
   ⚠️ LE FOND EST `none`, ET C EST LE POINT : un crayon est un signe, pas une
   surface. */
.crayon {
  appearance: none; cursor: pointer;
  width: 34px; height: 34px; flex: 0 0 auto; padding: 0;
  display: inline-flex; align-items: flex-start; justify-content: center;
  background: none; border: 0; border-radius: var(--r-1);
  color: var(--text-dim); vertical-align: middle;
  transition: color .14s;
}
.crayon:hover { color: var(--paper); }
.crayon:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.crayon svg { width: 13px; height: 13px; display: block; margin-top: 7px; }

/* ⚠️⚠️ V202 - `.crayon--hud` ET SA MEDIA QUERY SONT PARTIES AVEC LEUR
   UNIQUE PORTEUR. Elle masquait le crayon du HUD sous 700 px, ou la ligne
   figee se replie deja sur deux rangs ; le crayon n existe plus, donc la
   regle non plus - une classe, un jeton, un `id`, un `<button>` sans porteur
   part en entier (piege nº 4).
   ⚠️⚠️ `.crayon` RESTE, ET ELLE A ENCORE TROIS PORTEURS : `#pctMenuBtn` et
   les deux `.ruban-crayon`. C est exactement la verification que la V194 a
   payee cher pour ne pas l avoir faite - ON SUPPRIME DES SELECTEURS, ET ON
   CHERCHE UN PORTEUR POUR CHACUN. */

/* ⚠️ V194 - `.sheet-label .crayon` N EST PAS REVENUE, ET C EST EXACT : elle
   remontait le crayon sur la ligne des capitales de l intitule « Son surnom »,
   qui vivait dans l onglet du duo. Zero porteur, donc elle reste dehors - ce
   qui part avec son objet ne revient pas avec le voisin. */

/* ⚠️⚠️ V193 - TOUTES LES REGLES `.duo-*` DE L ECRAN SONT PARTIES AVEC LUI.
   `.duo-inner`, `.duo-carte*`, `.duo-tete`, `.duo-grosse-bulle`, `.duo-name`,
   `.duo-surnom`, `.duo-route`, `.duo-rompre*`, `.duo-demo*` : chacune n avait
   qu un porteur, et ce porteur etait la section. Ce qui part, part EN ENTIER -
   la mise en forme avec le balisage (piege nº 4, geste de la gelule
   `.profile-btn` en V59 et des cinq onglets du panneau en V62).
   ⚠️ CE QUI RESTE VIT AILLEURS : `.amis-*` (la ligne de personne) sert la
   fenetre de creation, et `.ruban-*`, `.pctmenu-*`, `.rsurnom-*` et `.rduo-*`
   portent le duo sur la page principale. */

/* ⚠️⚠️ V132 - « .duo-next » EST PARTIE AVEC SON PORTEUR. Le bloc « Ce qui
   vient ensuite » portait trois cartes, et les trois sont livrees : les
   surnoms (V130), la route a deux (V131) et l etoile du duo (V132). Une
   classe sans porteur laisse a l ecran l apparence d un mecanisme qui
   n existe plus - piege nº 4 en version interface, et c est le geste de la
   gelule « .profile-btn » (V59) et des « .archive-note » (V97). */

/* =================== L ONGLET ARCHIVES (V55) ==========================
   Le catalogue a plat, dans l ordre de sortie. Maquette C, validee telle
   quelle : une grille, et la fiche au clic.

   ⚠️ AUCUNE REGLE DE CARTE ICI, ET C EST VOULU. `.card` fait deja
   `width: var(--poster-w)` : une rangee en `flex-wrap` de cartes de meme
   largeur DONNE des colonnes alignees, sans qu on ait a redefinir la
   largeur d une carte pour une seconde disposition. Une grille CSS aurait
   demande `.archive-grid .card { width: 100% }`, c est-a-dire la premiere
   ligne d un second systeme de carte a tenir a jour - exactement ce que la
   vue colonnes (V45) a refuse de faire.

   ⚠️ ELLE NE SUIT PAS LES TROIS VUES. Liste et colonnes sont des regles
   `.roadmap.is-list …` : elles ne franchissent pas `#roadmap`, donc la
   grille reste une mosaique quoi qu on ait choisi. C est coherent avec le
   fait que la barre d outils est masquee ici - ce qui agit sur la rangee
   ne parle que d elle.

   ⚠️ `safe center` COMME PARTOUT : la derniere ligne se centre, et `safe`
   empeche le rognage par la gauche quand une seule carte ne tient pas.
   ==================================================================== */
/* ⚠️ V195 - `padding-top: 0` : la barre hero est au-dessus et porte deja la
   reserve. L argument entier est ecrit sur `.roadmap`, l autre section qui
   suit la barre (table `ENTETE`, `barre: true`). */
.archive { flex: 1; padding: var(--sect-pad); padding-top: 0; }

/* ⚠️ `.archive-note` EST PARTIE AVEC SON PORTEUR (V97, D1). Rien ne la
   remplace : la grille d archives n a pas de titre au-dessus d elle, donc
   c est `--sect-pad` qui donne deja sa reserve haute. Une classe sans porteur
   est du code mort (piege nº 4) - c est la regle inverse de `.soon-flag`, qui
   RESTE parce qu elle en a deux. */

.archive-grid {
  display: flex; flex-wrap: wrap;
  align-items: stretch;
  justify-content: safe center;
  column-gap: var(--gap);
  row-gap: calc(var(--gap) * 1.7);
}

/* =================== L ONGLET FAVORIS (V60) ===========================
   Un podium de trois marches, puis le reste en mosaique. Maquette E.

   ⚠️ AUCUNE REGLE DE CARTE, COMME POUR L ARCHIVE (V55). `.card` fait deja
   `width: var(--poster-w)` : ce qui change l echelle du podium, c est
   `--poster-w` REDEFINIE SUR LE PODIUM - exactement le geste de la carte
   de destination (V49), qui la redefinit sur elle-meme parce que toute son
   echelle en derive. Une seconde variable de taille aurait ete un second
   endroit a tenir d accord avec le premier.

   ⚠️ LES TROIS MARCHES DISENT LE RANG PAR LEUR HAUTEUR, et c est tout ce
   qu elles font. La FORME du repere de rang - pastille ou couronne - et
   l "effet vitrine" (un fond qui s eclaire derriere le numero) sont
   renvoyes a la refonte visuelle : ce sont des rendus, le podium tient
   sans eux.

   ⚠️ LA PREMIERE PLACE EST AU CENTRE PAR `order`, PAS PAR LE DOM. Le
   document reste 1-2-3 : c est ce que lisent le clavier et le lecteur
   d ecran. Meme geste que la vue liste (V45), pour la meme raison - une
   inversion visuelle que le document ne porte pas est une inversion que
   personne d autre que l oeil ne voit.
   ==================================================================== */
.favs { flex: 1; padding: var(--sect-pad); }

.favs-title {
  /* ⚠️ LA MARGE BASSE VIENT DE LA DESCRIPTION RETIREE (V97, D1), et c est le
     MEME jeton (`--tete-gap`) que les quatre sections d onglet portent sur
     leur titre : cinq ecrans, une seule valeur. (`.account-note` le portait
     aussi jusqu a la V149, ou elle est partie avec sa phrase.) */
  margin: 0 0 var(--tete-gap); text-align: center;
  font-family: var(--display); font-stretch: var(--ch-affiche); font-weight: 800;
  font-size: clamp(1.3rem, 3vw, 1.9rem); letter-spacing: .02em;
  color: var(--paper);
}

/* V242 - le commutateur de vue emprunte le dessin des boutons de portee des
   stats (`.stat-scope`) : deux commandes voisines, sans aplat. Seule sa place
   est a lui - entre le titre et le podium. */
.favs-vue { margin: calc(-1 * var(--tete-gap) + .2rem) 0 var(--tete-gap); }

/* ⚠️ `.favs-note` EST PARTIE AVEC SON PORTEUR (V97, D1) - piege nº 4. Sa
   marge basse est reprise ci-dessus par `.favs-title`. */

.favs-empty {
  margin: clamp(2rem, 6vw, 4rem) auto 0; max-width: 46ch; text-align: center;
  font-family: var(--body); font-size: var(--fs-base); line-height: 1.65;
  color: var(--text-2);
}

/* ⚠️ `align-items: end` EST CE QUI FAIT LE PODIUM. Les trois cellules se
   posent sur la meme ligne du bas - celle des fleches - donc une marche
   plus haute POUSSE sa carte vers le haut. La hauteur du rang n est pas
   une position calculee, c est une consequence. */

/* ⚠️⚠️ V153 - L ONGLET EST L ANCRE DU MENU DE CLASSEMENT, ET C EST TOUT CE
   QUE CETTE LIGNE FAIT. Le panneau est en `position: absolute` et se pose
   par calcul sur le bouton qui l ouvre : sans un ancetre positionne, son
   `offsetParent` serait le `<body>` et les deux coordonnees se compteraient
   depuis la page entiere. Il ne PEUT pas vivre dans la cellule - le repaint
   du podium la detruit a chaque deplacement. */
.favs-inner { position: relative; }

.podium {
  /* ⚠️ LA BORNE BASSE EST UNE MESURE, PAS UN GOUT. Le podium ne se plie
     JAMAIS - trois marches sur deux lignes ne sont plus un podium - donc
     trois cartes plus deux gouttieres doivent tenir dans la coquille au
     pire cas. A 320 px de fenetre la coquille en offre 280, et 3 x 88 + 16
     font exactement 280 : c est ce plancher-la qui empeche le debordement,
     le pire des maux depuis la V32. */
  --poster-w: clamp(88px, 25vw, 190px);
  /* ⚠️ V499 - LES DEUX MARCHES EXTREMES, ET LA TROISIEME SE DEDUIT (plus bas).
     Elles vivent ici, sur le podium, parce que c est lui qui les porte toutes
     les deux - et parce que les deux podiums du site (l onglet Favoris et la
     fiche de profil) lisent la meme classe, donc la meme forme. */
  --marche-or:     clamp(46px, 6vw, 84px);
  --marche-bronze: clamp(20px, 2.7vw, 38px);
  list-style: none; margin: 0 0 clamp(1.6rem, 4vw, 2.8rem); padding: 0;
  display: flex; align-items: end; justify-content: safe center;
  gap: clamp(.5rem, 2vw, 1.3rem);
}

.favs-cell--or     { order: 2; }
.favs-cell--argent { order: 1; }
.favs-cell--bronze { order: 3; }

/* ⚠️⚠️ V154 (4b) - LA MEDAILLE EST DECLAREE SUR LA CELLULE, PLUS SUR LA
   MARCHE. C est ce qui rend cette version possible : `--medal` HERITE, donc
   la marche, le numero, le halo et le reflet lisent LA MEME valeur au lieu
   de la redeclarer chacun. Elle etait ecrite a six endroits (trois pour la
   marche, trois pour le rang) et la V154 lui ajoutait deux porteurs - huit
   au total. Une seule declaration par metal, et les porteurs se servent.
   ⚠️ ET LES TROIS REGLES DE COULEUR DU RANG DISPARAISSENT AVEC : `.favs-rank`
   lit `var(--medal, var(--text-dim))`, donc une cellule de mosaique - qui n a
   pas de medaille - retombe sur le neutre PAR CONSTRUCTION. */
.favs-cell--or     { --medal: var(--medal-or); }
.favs-cell--argent { --medal: var(--medal-argent); }
.favs-cell--bronze { --medal: var(--medal-bronze); }

/* ⚠️⚠️ V154 (4b) - LE HALO. « Plus immersif, plus impactant sans etre trop en
   contraste » : le contraste n augmente pas d un point - ce qui arrive, c est
   une LUMIERE derriere l affiche, a la couleur du metal, dont l or est le plus
   fort et le bronze le plus discret. C est le seul moyen de hierarchiser les
   trois places sans toucher a l echelle des affiches : le jalon d une route se
   distingue par son CADRE et non par sa taille (V29), et la meme regle vaut
   ici.
   ⚠️ IL EST EN `z-index: -1`, ET IL NE TOUCHE AUCUN FRERE. Faire passer la
   carte, le rang et la marche « devant » aurait demande de leur poser un
   `position: relative` - donc de toucher `.card`, qui sert sur sept surfaces.
   Un negatif peint derriere son parent sans qu on rearrange qui que ce soit.
   ⚠️ ET IL NE DEBORDE PAS EN LARGEUR (`left: 0; right: 0`) : un halo plus
   large que sa cellule aurait pu rendre la page defilable a 320 px, ou la
   coquille fait exactement la largeur du podium (280 px). Il ne s etale que
   VERS LE HAUT, ou un depassement ne coute rien. */
.favs-cell--podium { position: relative; }
.favs-cell--podium::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  left: 0; right: 0; bottom: 0; top: -4%;
  background: radial-gradient(72% 42% at 50% 100%,
              color-mix(in srgb, var(--medal, var(--ink-5)) var(--halo, 6%), transparent),
              transparent 76%);
}
/* ⚠️ Les trois valeurs sont le SEUL endroit ou l or, l argent et le bronze se
   hierarchisent - et elles restent basses par decision : « plus impactant SANS
   etre trop en contraste » (retour du huitieme). Une lumiere qu on remarque
   sans la regarder. */
.favs-cell--or     { --halo: 10%; }
.favs-cell--argent { --halo: 7%; }
.favs-cell--bronze { --halo: 5%; }

/* La marche. Elle ne porte aucun mot : "or", "argent" et "bronze" se
   voient. Le filet du haut est l arete que la V44 a posee comme signe
   d elevation - une surface monte en attrapant la lumiere sur son bord.

   ⚠️⚠️ V154 - C EST UN SOCLE, PLUS UN APLAT DEGRADE. Un degrade vertical seul
   se lit comme une bande ; deux couches en font une FACE qui attrape la
   lumiere : une nappe radiale posee sur l arete haute (le metal y est le plus
   present) et un fond vertical qui s eteint jusqu a rien. Le `box-shadow`
   interieur double l arete d un cheveu, exactement le geste de `--lift-0`.
   ⚠️ Le degre le plus bas descend a 2 % au lieu de 5 : c est ce qui permet au
   reflet du dessous d etre le point le plus sombre, donc au socle de POSER. */
.favs-step {
  position: relative;
  width: 100%; margin-top: .55rem;
  border-radius: var(--r-1) var(--r-1) 0 0;
  border-top: 2px solid var(--medal, var(--ink-6));
  background:
    radial-gradient(118% 94% at 50% 0%,
      color-mix(in srgb, var(--medal, var(--ink-5)) 19%, transparent),
      transparent 78%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--medal, var(--ink-5)) 9%, transparent),
      color-mix(in srgb, var(--medal, var(--ink-5)) 1%, transparent));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--medal, var(--ink-5)) 26%, transparent);
}

/* ⚠️⚠️ V154 - LE SOL, ET IL EST PAR CELLULE, JAMAIS SOUS LE PODIUM ENTIER.
   Un filet pose sur `.podium::after` aurait la largeur du CONTENEUR - donc
   1 600 px de trait sous 610 px de marches sur un grand ecran - et il aurait
   fallu le borner a un contenu qu il ne connait pas. Par cellule, il fait
   exactement la largeur de la marche qu il porte, a n importe quelle taille
   d ecran.
   ⚠️ ET C EST CE QUI LE REND JUSTE SUR LE PODIUM D UN AMI, qui peut n avoir
   qu UNE ou DEUX marches (le serveur n en descend que trois, et un id inconnu
   n est pas dessine) : un trait continu de trois segments se serait coupe en
   silence des qu il en manque un. Une empreinte par socle ne peut pas etre
   incomplete.
   ⚠️ IL VIT SOUS LA MARCHE, DANS LA MARGE BASSE DU PODIUM (`top: 100%`) : il
   ne prend donc pas un pixel de hauteur a la marche, et l alignement des trois
   bases - qui EST le podium - ne bouge pas. */
.favs-step::after {
  content: ""; position: absolute; pointer-events: none;
  left: 0; right: 0; top: 100%;
  height: clamp(9px, 1.3vw, 20px);
  border-top: 1px solid color-mix(in srgb, var(--medal, var(--ink-5)) 22%, transparent);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--medal, var(--ink-5)) 8%, transparent), transparent 84%);
}

/* ⚠️⚠️ V499 - LA DEUXIEME MARCHE N EST PLUS UN TROISIEME NOMBRE CHOISI A LA
   MAIN, C EST LE MILIEU DES DEUX AUTRES (retour de l auteur : « corrige le
   podium sur mon profil, le deuxieme est pas bien placé »).
   ⚠️ LE DEFAUT SE MESURE, il ne se devine pas : les trois marches faisaient
   61,4 / 41 / 27,6 px, donc l escalier montait de 13,4 px puis de 20,4 - la
   deuxieme etait 3,5 px TROP BASSE, collee a la troisieme au lieu de se tenir
   a mi-chemin. Sur les cartes, cela donnait des bas espaces de 13,3 puis 20,5
   px : une irregularite qu on voit sans pouvoir la nommer.
   ⚠️⚠️ LA CAUSE EST TROIS VALEURS INDEPENDANTES POUR UNE SEULE FORME. 6vw,
   4vw et 2,7vw ne se contredisaient pas : personne ne leur avait demande de
   former un escalier. La moyenne l exige desormais PAR CONSTRUCTION, aux
   trois bornes du `clamp` a la fois - le plancher, la pente et le plafond.
   Une valeur recopiee a la main etait une variable manquante.
   ⚠️ LES DEUX EXTREMES NE BOUGENT PAS D UN PIXEL : la hauteur de l or et
   celle du bronze sont des decisions de la V154, et elles restent. Ce qui
   change est ce qui n en etait pas une. */
.favs-cell--or     .favs-step { height: var(--marche-or); }
.favs-cell--bronze .favs-step { height: var(--marche-bronze); }
.favs-cell--argent .favs-step {
  height: calc((var(--marche-or) + var(--marche-bronze)) / 2);
}

/* Une cellule : le rang, la carte, la marche s il y en a une, les fleches.
   Meme construction partout - c est le podium qui ajoute sa marche, pas une
   seconde cellule. */
.favs-cell {
  display: flex; flex-direction: column; align-items: center;
  width: var(--poster-w);
}

.favs-rank {
  margin-bottom: .3rem;
  font-family: var(--display); font-stretch: var(--ch-chiffre); font-weight: 800;
  font-size: var(--fs-s); letter-spacing: .04em;
  /* ⚠️ V154 - UNE SEULE REGLE POUR LES QUATRE CAS. La medaille est declaree
     sur la CELLULE et elle herite ; une cellule de mosaique n en a pas, donc
     elle retombe sur le neutre - par construction, sans une regle de plus. */
  color: var(--medal, var(--text-dim));
}
.favs-cell--podium .favs-rank { font-size: clamp(1.1rem, 2.2vw, 1.5rem); }

/* ⚠️⚠️ V153 (4a) - « .favs-move-row » ET « .favs-move » SONT PARTIES AVEC
   LEURS DEUX BOUTONS. La maquette du huitieme retour les barre en rouge : le
   NUMERO devient la commande. ⚠️ Ce qui part, part en entier - les deux
   regles, la fonction `favMove`, le `<div>` de la cellule et le report de
   focus de `reorderFav` (piege nº 4).
   ⚠️ ET LA CELLULE PERD SON TROISIEME ENFANT : le podium se lit desormais
   « rang / carte / marche », et sa ligne du bas est celle des MARCHES. C est
   toujours `align-items: end` qui fait le podium - la hauteur du rang reste
   une consequence, jamais une position calculee. */

/* ============ V153 (4a) - LE NUMERO EST LA COMMANDE ====================
   ⚠️ IL GARDE SON APPARENCE EXACTE : ni cadre, ni chevron, ni fond. On ne
   lui rend que ce qu un bouton doit avoir - un curseur, un anneau de focus,
   et un survol. Tout le reste (famille, chasse, graisse, corps, couleur de
   medaille) vient de `.favs-rank`, qui n est pas touchee.
   ⚠️ LE SURVOL PASSE PAR `filter`, PAS PAR UNE COULEUR : les trois premiers
   rangs portent leur medaille avec une specificite de (0,2,0), donc une
   couleur de survol en (0,2,0) ne gagnerait que par ORDRE DE SOURCE - piege
   nº 2 - et surtout elle EFFACERAIT la medaille. `brightness` marche sur
   n importe quelle teinte, medaille comprise. */
.favs-rank--btn {
  appearance: none; -webkit-appearance: none;
  padding: 0; background: none; border: 0;
  cursor: pointer; line-height: inherit;
  transition: filter .16s;
}
.favs-rank--btn:hover { filter: brightness(1.35); }
.favs-rank--btn:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 3px;
  border-radius: var(--r-1);
}
/* ⚠️ LA CIBLE DU SITE EST DE 34 px (le coeur, la roue, le bouton
   Progression), et un numero de .82rem n en fait que 13 de haut. La zone
   cliquable est etendue par un PSEUDO-ELEMENT, jamais par du padding : du
   padding aurait pousse la carte vers le bas et casse l alignement des
   marches, qui EST le podium.
   ⚠️⚠️ ET ELLE POUSSE VERS LE HAUT, JAMAIS VERS LE BAS. L affiche entiere est
   un `<button>` (`.poster-btn`, il ouvre la fiche) : une zone centree
   aurait recouvert ses dix premiers pixels et vole ses clics au centre haut,
   sans qu aucune erreur ne se produise. Au-dessus du rang il n y a que du
   vide - la reserve de la cellule sur le podium, la gouttiere de rangee dans
   la mosaique. */
.favs-rank--btn { position: relative; }
.favs-rank--btn::after {
  content: ""; position: absolute; left: 50%; bottom: 0;
  width: max(34px, 100%); height: max(34px, 100%);
  transform: translateX(-50%);
}

/* ⚠⚠ V207 - LE MENU DES NOTES. `position: fixed`, ET C EST CE QUI LE REND
   SERVABLE DES DEUX COTES : il s ouvre depuis une carte (la page defile) ET
   depuis la fiche (c est `.modal-body` qui defile). Un ancrage absolu se
   serait cale sur l un des deux seulement.
   ⚠ SON `z-index` PASSE AU-DESSUS DE LA FICHE (80) : ouvert depuis elle, il
   doit se voir. C est une REGLE, jamais un ordre de document (piege nº 2).
   ⚠ IL SE FERME AU DEFILEMENT (voir `js/app.js`) : un menu ancre par calcul
   dont l ancre s en va est un etat qui ment (V52).
   ⚠ LA BORNE `calc(100vw - 3rem)` EST LE PRIX D UN MENU, et elle se mesure
   a 320 px comme les huit autres. */
.notemenu {
  position: fixed; z-index: 85;
  min-width: 9.5rem; max-width: calc(100vw - 3rem);
  padding: .45rem; display: flex; flex-direction: column; gap: .15rem;
  background: var(--ink-1); border: var(--frame-dim);
  border-radius: var(--r-3); box-shadow: var(--lift-2);
}
.notemenu-row {
  display: flex; align-items: baseline; gap: .55rem;
  padding: .4rem .55rem; min-height: 34px; box-sizing: border-box;
  border-radius: var(--r-1);
  text-decoration: none; color: inherit;
}
.notemenu-row--tmdb { transition: background .14s; }
.notemenu-row--tmdb:hover { background: var(--ink-3); }
.notemenu-row--tmdb:focus-visible { outline: 2px solid var(--paper); outline-offset: -2px; }
.notemenu-val {
  font-family: var(--display);
  font-size: var(--fs-l); font-weight: 700; font-stretch: var(--ch-chiffre); line-height: 1;
  color: var(--paper); font-variant-numeric: tabular-nums;
  min-width: 2.2rem;
}
.notemenu-row.is-empty .notemenu-val { color: var(--text-dim); }
.notemenu-src {
  font-family: var(--display);
  font-size: var(--fs-micro); font-weight: 700; font-stretch: var(--ch-ui);
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-dim); line-height: 1; white-space: nowrap;
}
/* ⚠ LE DEGRADE OFFICIEL TMDB A SUIVI LA VALEUR : il etait sur l etoile, et
   « pas d etoiles dans le menu » (le retour). « Les couleurs ne bougent pas »
   se tient donc par le seul porteur qui reste. */
.notemenu-row--tmdb .notemenu-val {
  background: linear-gradient(45deg, #90CEA1 0%, #3CBEC9 55%, #00B3E5 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
.notemenu-row--tmdb .notemenu-src { color: #3CBEC9; }

/* ⚠⚠ V209 - LE MENU DU GROUPE REEMPLOIE `.notemenu`, IL NE LA REECRIT
   PAS. Les deux flottent au meme endroit, se bornent a la meme fenetre et se
   ferment sur les memes gestes : deux feuilles pour ca, ce seraient deux
   valeurs a tenir d accord au premier reglage (piege nº 6). `.grpmenu`
   n ajoute que ce qui differe - une liste de gens au lieu de deux lignes. */
/* ⚠ V224 - SA LARGEUR EST CELLE DE SON CONTENU (`max-content`), bornee a la
   fenetre par `.notemenu` : « pas trop large, proportionnel aux pseudos et
   au nom du groupe » (le retour). Le plancher descend donc a 9,5 rem, celui
   de `.notemenu` - un plancher plus haut elargirait un menu d un seul nom. */
.grpmenu {
  width: max-content; min-width: 9.5rem;
  max-height: min(52vh, 52dvh); overflow-y: auto; overscroll-behavior: contain;
}
/* ⚠ V224 - LA TETE : l embleme du groupe centre, le nom complet dessous, et
   le filet sous le RANG qui les porte (motif de `.nmenu-tete`, V197). Le nom
   se replie plutot que de s ellipser : c est le seul endroit ou il se lit
   en entier. */
.grpmenu-tete {
  display: flex; flex-direction: column; align-items: center; gap: .35rem;
  padding: .35rem .55rem .55rem; margin-bottom: .2rem;
  border-bottom: 1px solid var(--rule);
}
.grpmenu-groupe {
  max-width: 18rem; text-align: center; overflow-wrap: anywhere;
  font-family: var(--display); font-size: var(--fs-s); font-weight: 700;
  font-stretch: var(--ch-ui); line-height: 1.2; color: var(--text);
}
/* V373 - la moyenne du groupe, moi compris, sous le nom.
   V436 - et la note Snapline a sa droite, derriere un filet : chaque note, puis
   sous elle le nom de qui la donne. L etoile du groupe est doree, celle de la
   communaute blanche (la regle des bandeaux de carte). */
.grpmenu-notes { display: flex; align-items: stretch; justify-content: center; gap: .9rem; }
.grpmenu-notes > .grpmenu-moy:not([hidden]) + .grpmenu-com { border-left: 1px solid var(--rule); padding-left: .9rem; }
.grpmenu-moy {
  display: inline-flex; flex-direction: column; align-items: center; gap: .2rem;
  font-family: var(--display); font-weight: 700; font-stretch: var(--ch-chiffre);
  font-size: var(--fs-base); line-height: 1; color: var(--paper);
  font-variant-numeric: tabular-nums;
}
.grpmenu-moy-val { display: inline-flex; align-items: center; gap: .3rem; }
.grpmenu-moy-mot {
  font-size: var(--fs-xs); font-stretch: var(--ch-titre); font-weight: 600; color: var(--text-dim);
}
.grpmenu-moy .star-icon { width: 14px; height: 14px; }
.grpmenu-moy .star-icon path { fill: var(--star); }
.grpmenu-com .star-icon path { fill: var(--paper); }
.grpmenu-list { display: flex; flex-direction: column; gap: .15rem; }
.grpmenu-line {
  display: flex; align-items: center; gap: .5rem;
  padding: .35rem .55rem; min-height: 34px; box-sizing: border-box;
}
.grpmenu-nom {
  flex: 1 1 auto; min-width: 0; max-width: 20ch;
  font-family: var(--display); font-size: var(--fs-s); font-weight: 700;
  color: var(--text-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.grpmenu-val {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: .25rem;
  font-family: var(--display); font-weight: 700; font-stretch: var(--ch-chiffre);
  font-size: var(--fs-base); line-height: 1;
  color: var(--paper); font-variant-numeric: tabular-nums;
}
/* ⚠ L ETOILE PREND LA COULEUR DU MEMBRE, comme la pastille : « la couleur
   de chacun est celle de son profil » (V132). Elle est TOUJOURS PLEINE - une
   etoile creuse veut dire « pas encore notee, cliquez », et il n y a rien a
   cliquer ici. */
/* V371b - LES SIGNES DU MENU NE SUIVENT PLUS LE PROFIL (demande de l auteur) : l etoile est
   le jaune des notes, et la regle ne vise plus que l etoile (elle teintait aussi la coche). */
.grpmenu-val > svg { width: 13px; height: 13px; display: block; flex: 0 0 auto; }
.grpmenu-val > svg path { fill: var(--star); }
.grpmenu-vide {
  margin: .35rem .55rem .2rem; max-width: 22ch;
  font-family: var(--lecture); font-size: var(--fs-s); font-style: italic;
  color: var(--text-dim);
}

/* ⚠⚠ V209 - LE BOUTON DU GROUPE EMPRUNTE SA FORME A `.score--community`
   (V207) : meme reset de <button>, meme survol, meme anneau de focus. Ce qui
   lui est propre est son contenu - un EMBLEME la ou les autres ont une valeur
   et une etoile.
   ⚠ SA BOITE FAIT LA HAUTEUR D UNE VALEUR + ETOILE, pas plus : la ligne des
   notes est en `flex-wrap: nowrap` (V22), et une case plus haute que les
   autres aurait fait grandir tout le rang. */
.score--groupe {
  appearance: none; background: transparent;
  border-top: 0; border-right: 0; border-bottom: 0;
  font: inherit; cursor: pointer;
  transition: opacity .14s;
}
.score--groupe:hover { opacity: .78; }
.score--groupe:focus-visible { outline: 2px solid var(--paper); outline-offset: 3px; }
/* ⚠⚠ LA TAILLE N EST PAS ECRITE ICI, ELLE EST UN MODIFICATEUR DE
   `.profile-avatar` - VOIR PLUS BAS. Une `width` posee sur `.score-emb`
   pesait (0,1,0) exactement comme `.profile-avatar`, declaree APRES dans la
   feuille : elle perdait donc par ORDRE DE SOURCE, et l embleme restait a
   28 px. Mesure : la ligne des notes passait de 51 a 58 px de haut, ce que le
   commentaire ci-dessus disait justement ne pas vouloir. C est le piege nº 2,
   attrape a la mesure pour la dixieme fois. */
.score-emb svg { width: 100%; height: 100%; display: block; }

.favmenu-pop {
  position: absolute; z-index: 32;
  width: min(19.5rem, calc(100vw - 3rem));
  /* ⚠️ LES DEUX LIGNES SE LISENT COMME UN REPLI, ET `vh` RESTE AU-DESSUS
     (V142) : `dvh` suit la fenetre reellement visible, et un navigateur qui
     ne le connait pas ignore la seconde declaration.
     ⚠️ ET `overscroll-behavior: contain` : sans lui, arrive en bout de
     course, le geste CHAINE vers la page derriere. */
  max-height: min(62vh, 62dvh);
  overflow-y: auto; overscroll-behavior: contain;
  padding: .7rem .75rem .6rem;
  text-align: left;
  background: var(--ink-1); border: var(--frame-dim);
  border-radius: var(--r-3); box-shadow: var(--lift-2);
}

.favmenu-titre {
  margin: 0 0 .5rem;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 800;
  font-size: var(--fs-xs); letter-spacing: .01em;
  color: var(--text-dim);
}

.favmenu-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: .25rem;
}

.favmenu-line {
  appearance: none; width: 100%; padding: .32rem .42rem;
  display: flex; align-items: center; gap: .5rem;
  background: var(--ink-2); border: var(--frame-dim); border-radius: var(--r-1);
  color: var(--text); text-align: left; cursor: default;
  transition: border-color .16s, background .16s;
}
.favmenu-line:hover { border-color: var(--ink-6); }
.favmenu-line:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* La ligne en vol : elle monte d un cran d elevation et passe DEVANT ses
   voisines - une ligne qui glisse sous les autres ne se suit pas a l oeil. */
.favmenu-line.is-vol {
  position: relative; z-index: 2;
  border-color: var(--ink-6); background: var(--ink-3);
  box-shadow: var(--lift-1);
}

/* ⚠️ `touch-action: none` EST POSE ICI, ET NULLE PART AILLEURS. Sur la ligne
   entiere, il prendrait le geste de DEFILEMENT de la liste : au doigt, un
   classement de vingt titres serait alors impossible a parcourir. Sur la
   poignee seule, glisser reordonne et glisser ailleurs defile. */
.favmenu-grip {
  flex: 0 0 auto; width: 14px; height: 18px;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: grab; touch-action: none; color: var(--ink-7);
}
.favmenu-line.is-vol .favmenu-grip { cursor: grabbing; color: var(--text-2); }
.favmenu-grip svg { width: 12px; height: 18px; fill: currentColor; }

.favmenu-carre {
  flex: 0 0 auto; width: 1.5rem; height: 1.5rem;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-1);
  border: 1px solid color-mix(in srgb, var(--medal, var(--ink-6)) 55%, transparent);
  background: color-mix(in srgb, var(--medal, var(--ink-5)) 14%, transparent);
  font-family: var(--display); font-stretch: var(--ch-chiffre); font-weight: 800;
  font-size: var(--fs-xs); letter-spacing: .02em;
  color: var(--medal, var(--text-dim));
}
/* ⚠️ LE TOP 3 GARDE SON CODE COULEUR DANS LE MENU, le reste reste neutre :
   c est ce que la maquette demande, et c est le meme jeton qu au podium -
   jamais un second litteral (piege nº 6). */
.favmenu-carre--or     { --medal: var(--medal-or); }
.favmenu-carre--argent { --medal: var(--medal-argent); }
.favmenu-carre--bronze { --medal: var(--medal-bronze); }

.favmenu-nom {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--display); font-size: var(--fs-s); line-height: 1.3;
  /* ⚠️ UN ENFANT DE FLEX PORTE `min-width: auto` : sans la ligne du dessus,
     un titre long ferait deborder le panneau au lieu de s ellipser, et le
     debordement se ferait ROGNER par `overflow-y: auto` - un bug qui se
     tait (V85). C est le libelle qui cede, jamais la structure. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.favmenu-aide {
  margin: .55rem 0 0;
  font-family: var(--body); font-size: var(--fs-micro); line-height: 1.45;
  color: var(--text-dim);
}

@media (prefers-reduced-motion: reduce) {
  .favs-rank--btn, .favmenu-line { transition: none; }
}

/* La mosaique du reste : meme reglage que la grille d archives, pour la
   meme raison - des cellules de meme largeur en `flex-wrap` DONNENT des
   colonnes alignees, sans second systeme de carte a tenir a jour. */
.favs-grid {
  display: flex; flex-wrap: wrap;
  align-items: start;
  justify-content: safe center;
  column-gap: var(--gap);
  row-gap: calc(var(--gap) * 1.7);
}

/* ==================== L ONGLET COMPTE (V61) ==============================
   L onglet Compte quitte le panneau MON PROFIL et rejoint la rangee
   principale. Le BALISAGE est deplace tel quel : `.sync`, `.account`,
   `.sheet-label`, `.sheet-hint`, `.sheet-btn` (et alors `.methods`, partie en V65) sont deja des
   classes GLOBALES - elles n ont jamais ete scopees a `.sheet` - donc rien
   n a eu a etre recopie. Ce qui suit n est que l habillage de SECTION, la
   soeur de `.archive` et de `.favs`.

   ⚠️ LA COLONNE EST BORNEE, ET CE N EST PAS UN GOUT. La coquille va jusqu a
   1680 px ; un champ de code etale sur toute cette largeur donnerait une
   ligne de 210 signes qu on ne relit pas, et deux boutons perdus a ses deux
   bouts. La borne est celle d une colonne de lecture, comme le `72ch` du
   synopsis (V20). */
.account-tab { flex: 1; padding: var(--sect-pad); }

.account-inner { max-width: 44rem; margin-inline: auto; }

.account-title {
  margin: 0 0 .5rem; text-align: center;
  font-family: var(--display); font-stretch: var(--ch-affiche); font-weight: 800;
  font-size: clamp(1.3rem, 3vw, 1.9rem); letter-spacing: .02em;
  color: var(--paper);
}

/* ⚠️⚠️ V149 - `.account-note` EST PARTIE AVEC SON DERNIER PORTEUR. La V97
   l avait gardee en notant qu il ne lui en restait qu un, la section
   « Mon compte » : sa phrase (« Ce qui vous suit d un appareil a l autre »)
   decrivait le CODE DE PROGRESSION, disparu en V147. Une classe sans porteur
   part en entier (V59, V62, V97, V115, V116, V124, V136, V147, V148).
   ⚠️ ET LA RESPIRATION QU ELLE PORTAIT NE SE PERD PAS : `.account-inner`
   rejoint la liste ci-dessous, celle des sections dont le TITRE porte
   `--tete-gap`. **Le mecanisme existait deja ; on n ecrit pas une valeur de
   plus, on ajoute un selecteur.** */

/* ⚠️ LES ECRANS QUI N ONT PAS DE DESCRIPTION PORTENT LA MARGE SUR LEUR TITRE
   (V97 pour deux, V98 pour le troisieme, V149 pour le quatrieme). Elle ne
   peut PAS monter sur `.account-title` seule : la classe est partagee, et
   une section qui reprendrait une description l aurait DOUBLEE (titre +
   description). Elle se scope donc, section par section.
   ⚠️⚠️ V149 - LES QUATRE SONT DESORMAIS DANS LA LISTE, et c est le signe que
   la description etait l exception, pas la regle. Le jour ou une section en
   reprend une, c est SON selecteur qui sort d ici - pas la liste qui se
   defait.
   ⚠️ ET ELLE SE SCOPE AUSSI PARCE QUE LE PIEGE nº 2 MORD ICI : `.account-title`
   vaut (0,1,0) et est ecrite 60 lignes plus haut - une regle de meme
   specificite ne gagnerait que par l ORDRE DE SOURCE, c est-a-dire pas
   vraiment (V45, V52, V76). `> .account-title` vaut (0,2,0). */
/* ⚠️ V149 - `.account-inner` REJOINT LA LISTE. Elle en etait absente parce
   qu elle avait une DESCRIPTION qui portait la marge ; la description est
   partie, la marge revient au titre - exactement le geste de la V97, et le
   `>` reste indispensable (piege nº 2 : `.account-title` vaut (0,1,0) et est
   ecrite 60 lignes plus haut). */
.amis-inner    > .account-title,
.account-inner > .account-title { margin-bottom: var(--tete-gap); }

/* Ce qui n existe pas encore, sous ce qui marche : le site pose d abord ce
   qu il fait, l horizon ensuite. Le filet le SEPARE au lieu de l encadrer -
   un cadre en ferait un objet a part entiere, alors que c est la suite de la
   meme phrase. */
/* ⚠️ V106 (D7) - LA PORTE EST DEVENUE LA SECTION ELLE-MEME. Le formulaire
   de compte a quitte sa fenetre pour vivre ici ; la classe change de nom avec
   son objet - une classe qui nomme un mecanisme disparu est une fausse piste
   laissee au prochain lecteur (V59, V77). La geometrie, elle, ne bouge pas :
   le filet SEPARE au lieu d'encadrer, exactement comme avant.
   ⚠️ ET LE BLOC GARDE SON CADRE ICI. La fenetre le lui retirait, parce
   qu'un cadre pose dans un panneau qui a deja le sien est un cadre de trop ;
   dans une section il n'y a plus de panneau, donc plus rien dont il faille le
   detacher. */
.account-modif {
  margin-top: clamp(1.6rem, 4vw, 2.6rem);
  padding-top: clamp(1.1rem, 3vw, 1.7rem);
  border-top: var(--frame-dim);
}
/* Le titre reprend le rang des titres de bloc de l'ecran des amis (V94) :
   c'est le meme niveau de lecture, dans la meme colonne de 44 rem. */
.account-modif .amis-h { margin-bottom: .5rem; }
/* La section prend le focus a l'arrivee (tabindex=-1) : un anneau sur un bloc
   entier ne dit rien, la mise au point le dit deja. */
.account-modif:focus { outline: none; }

/* ⚠️ V149 - `.account-hint` EST PARTIE AVEC SA PHRASE. Elle expliquait le
   bouton Synchroniser depuis un ecran ou il n est pas. Dernier porteur, donc
   la classe part avec. */

/* ==================== L ONGLET AMIS (V85 - C1) ==========================
   ⚠️ SON PROPRE PREFIXE, ET C EST LA LECON DE LA V80. Le menu « Progression »
   avait ete ecrit en `.pmenu-*`, deja pris par le menu de PROFIL : le popover
   heritait `top`, `left` et une largeur, et se posait a 1653 px du bord sans
   qu aucune erreur ne se produise. Un prefixe de classe est un espace de
   noms - verifie avant d ecrire : aucun `.amis-` n existait.

   ⚠️ ET IL N Y A AUCUN FILET HORIZONTAL ICI, PAS PAR OUBLI. La V83 vient d en
   effacer cinq sur la page principale ; separer des lignes de liste par des
   traits aurait ete les rendre par la porte de derriere. Ce qui separe, c est
   la SURFACE : chaque ligne est une surface POSEE (`--ink-2` + `--lift-0`,
   l elevation de la V44), et l espace fait le reste. */

.amis-tab { flex: 1; padding: var(--sect-pad); }

/* ⚠️ 44 rem, LA MEME COLONNE QUE L ONGLET COMPTE, ET POUR LA MEME RAISON : la
   scene va jusqu au bord de l ecran depuis la V76, et une ligne de nom etalee
   sur 2 400 px ne se lit pas. C est la borne d une colonne de lecture, le
   geste du `72ch` du synopsis (V20). */
.amis-inner { max-width: 44rem; margin-inline: auto; }

/* Un seul des trois etats est a l ecran a la fois - c est js/amis.js qui
   tranche, et lui seul. */
.amis-state { text-align: center; }
.amis-state .soon-flag { margin-bottom: .5rem; }

/* ------------------------------ Ajouter ---------------------------------- */

.amis-add { margin-bottom: clamp(1.6rem, 4vw, 2.4rem); }

/* ⚠️ LE CHAMP ET LE BOUTON SUR UNE LIGNE, ET LE CHAMP PREND CE QUI RESTE.
   `.cpt-at` porte deja un `margin-bottom` : on l annule ici, la ligne a sa
   propre gouttiere. En dessous de ~420 px le bouton passe dessous - un
   « Envoyer » ecrase a 60 px est un bouton qu on rate au doigt. */
.amis-addline { display: flex; flex-wrap: wrap; align-items: stretch; gap: .5rem; }
/* ⚠️⚠️ `min-width: 0` EST OBLIGATOIRE, ET C EST UNE MESURE (V176). UN
   ENFANT DE FLEX PORTE `min-width: auto` : il REFUSE de descendre sous la
   largeur de son contenu, meme quand la ligne est plus etroite. Le champ
   `@handle` du panneau d invitation d un groupe vit dans une carte, donc
   dans une colonne plus etroite que la section : mesure a 320 px, la boite
   faisait 254 px dans une ligne de 247 - elle debordait de 7 px SANS que
   `docX` bouge, parce que la carte l absorbe. Un debordement qu on rogne est
   un bug qui se tait (V22, V85). La parade est celle que le projet ecrit
   depuis la V22, et elle se pose UNE FOIS, sur la regle partagee : les deux
   surfaces qui portent ce champ en heritent, il n y a pas une seconde
   valeur a tenir d accord.
   ⚠️ ET ELLE NE CHANGE RIEN A L ECRAN DES AMIS : la ligne y fait 280 px a
   320, donc au-dessus du minimum de contenu. Elle retire un piege, elle ne
   deplace pas un pixel. */
.amis-addline .cpt-at { flex: 1 1 12rem; min-width: 0; margin-bottom: 0; }
.amis-addline .sheet-btn { flex: 0 0 auto; }

/* -------------------------------- Blocs ---------------------------------- */

.amis-bloc { margin-bottom: clamp(1.5rem, 3.5vw, 2.2rem); }
.amis-bloc:last-child { margin-bottom: 0; }

.amis-h {
  margin: 0 0 .7rem;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  /* ⚠️ V94 - +15 % (.74 -> .85rem), demande du retour du 1er septembre.
     LES QUATRE INTITULES DE L ECRAN GRANDISSENT, PAS SEULEMENT CELUI QUE LA
     DEMANDE NOMME : « Mes amis », « Demandes recues » et « Demandes
     envoyees » sont le MEME RANG DE TITRE. En faire grandir un seul aurait
     donne deux echelles a une meme fonction sur un meme ecran, c est-a-dire
     un rang de plus que le document n en porte. */
  font-size: var(--fs-m); letter-spacing: .01em;
  color: var(--paper);
}
/* ⚠️ V94 - ET « AJOUTER UN AMI » SUIT, MAIS PAR UN SELECTEUR SCOPE.
   C est un `<label for>`, pas un titre : il doit le rester, sinon le champ
   perd son etiquette. Lui poser la classe `.amis-h` en plus de
   `.sheet-label` n aurait rien donne - les deux valent (0,1,0) et
   `.sheet-label` est ecrite 2 100 lignes PLUS BAS, donc elle gagnait par
   ORDRE DE SOURCE. C est le piege nº 2, et la parade est la meme qu ailleurs
   dans ce bloc : on scope. */
.amis-add .sheet-label { font-size: var(--fs-micro); }
/* Le compte est plus sourd que l intitule : il accompagne, il ne nomme pas.
   Meme partage que le titre et l annee d une carte (V5.1) - l echelle et la
   teinte, jamais une couleur de plus. */
.amis-n { font-weight: 500; letter-spacing: .08em; color: var(--text-dim); }

/* ------------------------ Chercher (V173 - 5g-2) ------------------------- */

/* ⚠️⚠️ LA SPECIFICITE EST LA REGLE, PAS L ORDRE DE SOURCE. `.search`
   porte `max-width: 260px` parce qu elle partage une colonne avec le bouton
   « Filtres » dans la bande figee ; ici le champ est seul sur sa ligne. Une
   `.amis-cherche` simple vaut (0,1,0) comme `.search` et ne gagnerait que
   parce qu elle est ecrite PLUS BAS - c est le piege nº 2, paye en V95 sur
   `.pmenu-pop`. `.search.amis-cherche` vaut (0,2,0) : elle gagne par une
   REGLE, et elle continuera de gagner si quelqu un deplace ce bloc.
   ⚠️ ET C EST LA SEULE CHOSE QU ON REDEFINIT : la boite, la loupe, la croix
   et le focus restent ceux de la recherche du site. Un second dessin aurait
   diverge au premier reglage (V45, V85). */
.search.amis-cherche { max-width: none; margin-bottom: .6rem; }

.amis-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }

/* ⚠️ UNE LIGNE EST UNE SURFACE POSEE (V44) : fond `--ink-2`, arete haute
   `--lift-0`. Sur fond sombre, un objet monte en s eclaircissant et en
   attrapant la lumiere sur son bord - c est ce qui separe les lignes, a la
   place du filet que la V83 vient de retirer partout ailleurs. */
/* ⚠️ `min-width: 0` EST OBLIGATOIRE, ET C'EST UNE MESURE : sans lui, une
   ligne debordait de 5 px a 375 px. Un enfant de grille porte
   `min-width: auto`, c'est-a-dire sa largeur MIN-CONTENT - il refuse donc de
   descendre sous la somme de ce qu'il contient, quelle que soit la largeur de
   sa colonne. C'est le pendant exact du `min-width: 0` de `.amis-qui`, un
   cran plus haut, et le meme defaut classique. */
/* ⚠️⚠️ V211 - `position: relative` EST LE SOCLE DU CLIC PLEINE LARGEUR :
   le bouton d ouverture etend sa cible par un pseudo-element en `inset: 0`,
   qui se cale sur CETTE boite. Sans elle il se calerait sur la premiere
   ancetre positionnee - la fenetre, la section, ou le document. */
.amis-item {
  position: relative;
  display: flex; align-items: center; gap: .7rem; flex-wrap: wrap;
  min-width: 0;
  padding: .55rem .75rem;
  background: var(--ink-2); border-radius: var(--r-2); box-shadow: var(--lift-0);
}

/* ⚠️ LA PASTILLE EST CELLE DU HUD (`.profile-avatar`), A UNE TAILLE DE LISTE
   - ET LA REGLE EST SCOPEE, JAMAIS `.amis-bulle` SEULE. C'est le PIEGE Nº 2,
   attrape a la mesure : `.profile-avatar { width: 28px }` vit 1 800 lignes
   PLUS BAS dans la feuille et vaut la meme specificite (0,1,0) - a egalite,
   c'est l'ordre de source qui tranche, donc la bulle restait a 28 px. « Une
   regle qui ne gagne que par ordre de source ne gagne pas, elle attend »
   (V45, V52, V59). */
.amis-item .amis-bulle { width: 32px; height: 32px; }

/* ⚠️ ELLE PREND TOUTE LA PLACE QUI RESTE, ET C EST CE QUI POUSSE LE
   POURCENTAGE ET LES ACTIONS A DROITE. `min-width: 0` est obligatoire : sans
   lui, un pseudo de 36 signes empeche la boite de retrecir et la ligne
   deborde - le defaut classique d une cellule flex. */
.amis-qui { flex: 1 1 8rem; min-width: 0; display: flex; flex-direction: column; }

.amis-nom {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-base); color: var(--paper);
}

/* ⚠️ C EST `.profile-handle` DU HUD (V73), REEMPLOYEE TELLE QUELLE - Archivo
   en italique, sourde, sous le pseudo. C est la forme que le backlog nommait
   pour cet ecran ; en ecrire une seconde aurait donne deux italiques a tenir
   d accord. On n ajuste ici que la borne de largeur : dans une liste, on a la
   place de lire un identifiant en entier. */
/* ⚠️ SCOPEE POUR LA MEME RAISON QUE LA BULLE : `.profile-handle` porte
   `max-width: 18ch` (V73) et vit plus bas dans la feuille. Dans le HUD cette
   borne protege une bande figee ; dans une liste on a la place de lire un
   identifiant en entier. */
.amis-qui .amis-handle { max-width: 100%; }

/* ⚠️⚠️ V216 - LE POURCENTAGE DE LIGNE N EXISTE PLUS, ET SA REGLE PART AVEC
   SON DERNIER PORTEUR (piege nº 4). Il a quitte la liste d amis en V211 et la
   liste des membres ici : dans les deux cas « une liste repond a QUI, la fiche
   repond a OU EN SONT-ILS ». La regle qu il portait - Orbitron ne sert QU AU
   pourcentage de la barre hero - ne se perd pas : elle a un autre porteur,
   `.amis-profil-pct`, ou elle est ecrite. */

/* ================= LA LIGNE REDESSINEE (V211, refaite en V445) ===========
   ⚠️⚠️ V445 - LE @IDENTIFIANT PASSE SOUS LE PSEUDONYME (retour de l auteur).
   Il vivait EN BAS A DROITE depuis la V211 : l oeil devait traverser la ligne
   pour rattacher un identifiant a un nom, et les deux se lisent ensemble. Le
   nom du groupe, lui, va AU CENTRE - il parle de la personne, pas de son
   identite, donc il ne se colle ni a l un ni a l autre. La marque Duo ne
   bouge pas : elle reste sur le rang du nom, a droite.
   ⚠️ TOUT SE POSE PAR LA GRILLE, AUCUNE MARGE CALCULEE (piege nº 6) : trois
   rangs, deux colonnes, et chaque morceau nomme sa case. */
.amis-qui .amis-handle { align-self: start; }
/* ⚠️ LE NOM DU GROUPE EST EN LITERATA ITALIQUE - la voix de la PROSE (V44) :
   c est une phrase sur la personne, pas un intitule qui se repere. Il ne
   prend aucune couleur de plus : `--text-dim`, comme le @handle. */
/* ⚠️⚠️ V491 - LA PUCE DEVIENT UN COUPLE : LE LOGO, PUIS LE NOM (retour de
   l auteur). L ELLIPSE DESCEND SUR LE NOM, et c est la seule facon de garder
   les deux : `overflow: hidden` pose sur le couple aurait coupe le LOGO en
   premier, puisqu il est a gauche - un nom long aurait donc mange le dessin
   qu on vient d ajouter. Le logo ne cede jamais (`flex: 0 0 auto`), le nom
   cede par l ellipse.
   ⚠️ « CENTRE VERTICALEMENT LES DEUX ELEMENTS, QUE LE HAUT ET LE BAS SOIENT
   EGAUX » : `align-items: center` pose le logo sur l axe du nom, et non sur la
   ligne de base - un dessin n a pas de ligne de base, et s aligner dessus lui
   fait porter l oeil plus bas que le mot. */
/* ⚠️⚠️ V500 - LE LOGO PASSE AU-DESSUS DU NOM (retour de l auteur : « le logo
   du groupe est au dessus du nom et les deux sont centres verticalement »).
   La V491 les avait mis cote a cote sur un troisieme rang, pleine largeur ;
   empiles, ils forment un bloc compact qui tient dans la hauteur des deux
   rangs existants - voir la grille plus bas. */
.amis-groupe {
  min-width: 0; display: inline-flex; flex-direction: column;
  align-items: center; justify-content: center; gap: .1rem;
  font-family: var(--read); font-style: italic;
  font-size: var(--fs-s); color: var(--text-dim); text-align: center;
}
/* ⚠️⚠️ V500 - « max-width: 100% » EST OBLIGATOIRE DEPUIS QUE LA PUCE EST UNE
   COLONNE, ET C EST UNE MESURE. Cote a cote, le nom cedait par l ellipse ;
   empile, il est seul sur son rang et s etale a sa taille MAX-CONTENT - 222 px
   pour une puce bornee a 75. Il debordait sans etre tronque, exactement le
   defaut que la V491 avait deja paye un cran plus haut. « overflow: hidden »
   ne borne pas une boite, il coupe ce qui depasse de sa largeur : encore
   fallait-il lui en donner une. */
.amis-groupe-nom {
  min-width: 0; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* ⚠️ SCOPEE, JAMAIS `.amis-groupe-logo` SEULE - c est le PIEGE Nº 2, le meme
   que la bulle de la ligne : `.profile-avatar { width: 28px }` vit 4 500
   lignes PLUS BAS dans la feuille et vaut la meme specificite (0,1,0). A
   egalite, c est l ordre de source qui tranche, donc le logo serait reste a
   28 px - deux fois trop haut pour la ligne qu il partage. */
.amis-groupe .amis-groupe-logo { width: 15px; height: 15px; }
/* ⚠️ LE COEUR EST ROSE, ET CE N EST PAS UNE SEPTIEME FAMILLE DE COULEUR :
   c est la couleur du signe lui-meme, repris exactement comme la maquette le
   dessine (demande explicite). Il ne sert QU A ce signe.
   ⚠️ `align-self: flex-start` LE POSE EN HAUT A DROITE DE LA LIGNE, sans une
   boite absolue : la ligne est un flex, donc « en haut » est une ligne de CSS
   et non une reserve de place a tenir a la main. */
.amis-duo {
  flex: 0 0 auto; align-self: flex-start;
  display: inline-flex; align-items: center; gap: .28rem;
  color: #FF6FA5;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-micro); letter-spacing: .08em; text-transform: uppercase;
}
.amis-duo svg { width: 15px; height: 15px; fill: currentColor; }
/* ⚠️ V303 - DANS LA LISTE, LA MARQUE EST SUR LE RANG DU NOM, DANS SA COLONNE.
   `.amis-qui` passe en grille a deux colonnes : le nom (qui cede par ellipse)
   et la marque, puis le groupe et le @handle sur toute la largeur. Toutes les
   lignes ont donc la MEME colonne, et les @handle le meme bord droit, avec ou
   sans duo (retour de l auteur : « attention aux alignements »). */
.amis-item .amis-qui {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto; column-gap: .5rem;   /* V500 - la 3e colonne est celle du groupe */
}
.amis-item .amis-qui > * { grid-column: 1 / -1; }
.amis-item .amis-qui > .amis-nom { grid-column: 1; grid-row: 1; }
.amis-item .amis-qui > .amis-duo { grid-column: 2; grid-row: 1; align-self: center; }
/* V445 - sous le pseudonyme, dans sa colonne, et cale a gauche comme lui */
.amis-item .amis-qui > .amis-handle { grid-column: 1; grid-row: 2; justify-self: start; }
/* ⚠️⚠️ V500 - LE GROUPE QUITTE SON TROISIEME RANG POUR UNE COLONNE, ET
   C EST CE QUI RETIRE LE VIDE (retour de l auteur : « la taille de la fenetre
   est proportionnelle, il ne doit pas y avoir d espaces vides inutiles »).
   Mesure avant : une ligne AVEC groupe faisait 73,3 px, une ligne sans 54,3.
   Ce troisieme rang pleine largeur ne portait qu une puce centree - deux
   grands vides a gauche et a droite - et il allongeait la fenetre entiere
   d autant de fois qu il y a d amis en groupe. Pose en colonne, le bloc tient
   dans la hauteur des deux rangs deja la : les lignes redeviennent toutes de
   la meme hauteur, et la fenetre se serre d elle-meme.
   ⚠️ ELLE PREND UNE TROISIEME COLONNE, elle ne partage pas celle du duo : le
   coeur *Duo* vit deja en colonne 2, rang 1. Les faire cohabiter dans la meme
   case aurait ete deux objets l un sur l autre le jour ou quelqu un est a la
   fois mon duo et dans un groupe - c est-a-dire le cas le plus courant.
   ⚠️ LA BORNE EST EN `ch`, COMME CELLE DE `.score-src` (V130) ET POUR LA MEME
   RAISON : un nom de groupe va jusqu a 24 signes, et la colonne du pseudo doit
   rester lisible. Ce qui cede est le NOM DU GROUPE (ellipse), jamais le
   pseudo. */
.amis-item .amis-qui > .amis-groupe {
  grid-column: 3; grid-row: 1 / span 2;
  align-self: center; justify-self: end;
  max-width: 12ch;
}

/* L EN-TETE DU PANNEAU : le champ prend la place qui reste, le bouton garde
   sa cible de 34 px. ⚠️ `min-width: 0` sur le champ - un enfant de flex
   refuse sinon de descendre sous la largeur de son contenu. */
.amis-tete {
  display: flex; align-items: center; gap: .5rem;
  margin-bottom: .8rem;
}
.amis-tete .search { flex: 1 1 auto; min-width: 0; margin-bottom: 0; }
.amis-ajout-btn {
  appearance: none; cursor: pointer; flex: 0 0 auto;
  display: grid; place-items: center;
  width: 34px; height: 34px; padding: 0;
  background: transparent; color: var(--text-2);
  border: var(--frame-dim); border-radius: var(--r-2);
  transition: color .16s, border-color .16s;
}
.amis-ajout-btn svg { width: 21px; height: 21px; }
.amis-ajout-btn:hover { color: var(--paper); border-color: var(--paper); }
.amis-ajout-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* Les deux etiquettes sur la FICHE : meme dessin, sur un rang centre. */
.amis-profil-tags {
  display: flex; justify-content: center; align-items: center;
  flex-wrap: wrap; gap: .55rem; margin-top: .3rem;
}

.amis-actions { flex: 0 0 auto; display: flex; align-items: center; gap: .9rem; }
/* `.cpt-out` porte une marge droite pour la fenetre de compte ; ici c est la
   gouttiere de la boite qui espace. */
.amis-actions .cpt-out { margin: 0; }
.amis-oui { color: var(--tier-must); }
.amis-oui:hover { color: var(--paper); }

/* Une demande envoyee n a ni bulle ni pseudo : on ne sait pas si le compte
   existe, et le site ne le demandera pas. La ligne est donc plus sourde. */
.amis-item--attente .amis-nom { color: var(--text-2); font-weight: 600; }
.amis-attente {
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 500;
  font-size: var(--fs-xs); letter-spacing: .07em; text-transform: uppercase;
  color: var(--text-dim);
}

/* ================= LE PROFIL D UN AMI (V88 - C1d) ========================
   Une SOUS-VUE de l onglet, pas une fenetre : elle ne touche donc ni
   `libererDefilement()`, ni l ordre d Echap, ni le `z-index` (voir
   index.html). Toute cette section est du POSITIONNEMENT : la bulle, la
   jauge et le podium sont empruntes ailleurs.
   ========================================================================= */

/* ⚠️ LE BOUTON GARDE SA GEOMETRIE : sans `flex: 1 1 8rem` ici, les actions
   ne seraient plus poussees a droite. Ce qui change en V211, c est sa CIBLE,
   pas sa boite. */
.amis-ouvre {
  appearance: none; cursor: pointer;
  flex: 1 1 8rem; min-width: 0;
  display: flex; align-items: center; gap: .7rem;
  margin: 0; padding: 0; text-align: left;
  background: transparent; border: 0; color: inherit; font: inherit;
}
/* ================= LE CLIC PLEINE LARGEUR (V211 - partie 5, V204) ========
   ⚠️⚠️ C EST UN RENVERSEMENT DE LA V88, ET IL SE PAIE DELIBEREMENT. Elle
   ecrivait : « le bouton d ouverture entoure la bulle et le nom, pas la LIGNE :
   une ligne cliquable contenant un geste destructeur est une occasion de
   retirer un ami en visant son profil. » LE GESTE DESTRUCTEUR EST TOUJOURS LA
   - c est la croix - donc la parade n est plus le PERIMETRE du bouton, elle
   doit etre l ISOLEMENT des actions.

   ⚠️⚠️ ON ETEND LA CIBLE PAR UN PSEUDO-ELEMENT, JAMAIS EN ENVELOPPANT LA
   LIGNE DANS UN `<button>` : un bouton dans un bouton n est pas du HTML
   valide, et cette ligne PORTE des boutons. Le `::after` en `inset: 0` couvre
   la boite de `.amis-item` sans rien deplacer - le DOM et la geometrie ne
   bougent pas d un pixel, donc aucune des trois autres surfaces qui
   reemploient `.amis-item` (le duo, les membres, la salle) n a rien a
   apprendre.

   ⚠️ ET LES ACTIONS SE POSENT AU-DESSUS PAR `z-index`, PAS PAR UN
   `stopPropagation()` : elles sont SOEURS du bouton, jamais ses enfants - leur
   clic ne le traverse donc a aucun moment. Une garde de propagation ici serait
   une garde qui ne garde rien (piege nº 4) ; ce qui protege est l ordre
   d empilement, et il vaut pour les QUATRE fichiers qui construisent une
   `.amis-actions`. */
.amis-ouvre::after {
  content: ""; position: absolute; inset: 0;
  border-radius: var(--r-2);
}
.amis-actions { position: relative; z-index: 1; }
/* Le survol ne deplace rien : c est le nom qui s eclaircit, comme partout
   ailleurs sur le site. Un fond qui s allume sous une ligne deja posee
   (`--ink-2` + `--lift-0`) aurait fait une seconde surface au-dessus d une
   surface. */
.amis-ouvre:hover .amis-nom { color: #FFFFFF; text-decoration: underline;
                              text-underline-offset: 3px; }
/* ⚠️ LE LISERE DE FOCUS SUIT LA CIBLE, PAS LA BOITE DU BOUTON : porte par le
   bouton seul, il aurait entoure la bulle et le nom pendant que le clic prend
   toute la ligne - un lisere qui ment sur ce qu on va cliquer. */
.amis-ouvre:focus-visible { outline: 0; }
.amis-ouvre:focus-visible::after { outline: 2px solid var(--brand);
                                   outline-offset: 3px; }

.amis-profil { text-align: center; }

/* =============== LE PROFIL D UN AMI EN FENETRE (V95) =================== *
 * ⚠️ LA COQUILLE EST CELLE DE LA FICHE DE FILM, PAS UNE SECONDE. `.modal`,
 * `.modal-backdrop`, `.modal-panel` et `.modal-close` sont reemployes tels
 * quels : ce qui suit ne redeclare QUE ce qui differe, c est-a-dire la
 * largeur et le corps. Une seconde coquille aurait diverge au premier
 * reglage (V45).
 *
 * ⚠️⚠️ ET LE Z-INDEX EST UNE REGLE, PAS UN ORDRE DE DOCUMENT. On ouvre une
 * fiche de film DEPUIS le podium d un ami : les deux fenetres sont alors a l
 * ecran, et `.modal` vaut 80 pour les deux. A specificite egale, c est l
 * ordre de source qui aurait tranche - c est-a-dire le piege nº 2, et une
 * fenetre qui passerait au-dessus de celle qui l a ouverte. `.modal.amis-win`
 * vaut (0,2,0) : elle gagne ou que vive le bloc dans la feuille.
 *
 * ⚠️ LE BOUTON DE RETOUR EST PARTI AVEC LA SOUS-VUE (piege nº 4). La croix
 * de `.modal-close` le remplace, et elle est deja placee, deja figee pendant
 * que le corps defile (V20) - c est tout ce que la fenetre achete en plus du
 * geste demande. */
.modal.amis-win { z-index: 78; }

/* ⚠️⚠️ LES DEUX REGLES QUI SUIVENT SONT SCOPEES A LA FENETRE, ET C EST LE
   PIEGE Nº 2 ATTRAPE A LA MESURE - POUR LA TROISIEME FOIS DANS CE BLOC.
   Ecrites `.amis-win-panel` / `.amis-win-body`, elles valaient (0,1,0)
   comme `.modal-panel`, ecrite MILLE LIGNES PLUS BAS : elle gagnait par ordre de
   source, et le panneau faisait 1 076 px au lieu de 560. Une regle qui ne
   gagne que par ordre de source ne gagne pas, elle attend. */

/* UNE COLONNE, PAS DEUX. Le corps de la fiche de film est une rangee flex
   (l affiche, puis les rubriques) ; ici tout est centre sur un axe, comme sur
   la maquette. On ne DEFAIT pas la disposition de la fiche - on ne la pose
   pas sur cette fenetre-ci. */
.modal.amis-win .modal-body { display: block; }

/* ⚠️ C EST UN `max-width`, PAS UN `width`, ET LA NUANCE A ETE MESUREE.
   `.modal-panel` porte `width: max-content` ET `min-width: min(600px, …)` :
   une largeur de 560 px etait donc RELEVEE a 600 par le plancher, et la regle
   disait une chose que le rendu ne faisait pas. On borne le PLAFOND sur la
   meme valeur que le plancher - la fenetre fait 600 px, ou l ecran en
   dessous. Sans ce plafond elle prenait la largeur du podium ETALE : 1 076 px.
   ⚠️ Une regle dont la valeur n est jamais celle qui s applique est pire
   qu absente : elle se lit comme une decision. */
.modal.amis-win .modal-panel {
  max-width: min(600px, calc(100vw - 2.2rem));
  /* ⚠️ V463 - L EXCEPTION EST PARTIE AVEC LA REGLE. Cette fenetre RETIRAIT le
     filet d accent que la fiche de film posait, parce qu un profil n a pas de
     phase et que le filet y aurait pris la teinte de `:root`. Le filet n
     existant plus nulle part, redire ici qu il n y en a pas serait une regle
     qui redit le defaut - deux endroits a tenir d accord pour rien. */
}

/* ⚠️⚠️ LE PODIUM SE REDIMENSIONNE POUR LA FENETRE, ET C EST LA CONTRAINTE
   QUE LA V88 AVAIT REEMPLOYEE AVEC LUI. Il porte `--poster-w: clamp(88px,
   25vw, 190px)`, mesure pour la COQUILLE DE LA PAGE ; dans un panneau de
   600 px il demandait 3 x 190 + 2 x 20,8 = 611,6 px pour 542 disponibles, et
   `.modal-body` porte `overflow-x: hidden` : la troisieme carte etait
   COUPEE, pas decalee. Un debordement qui se voit est un bug ; un
   debordement qu on ROGNE est un bug qui se tait.
   ⚠️ LA REGLE DE LA V60 NE CHANGE PAS - le podium ne se plie JAMAIS, trois
   marches sur deux lignes ne sont plus un podium - c est sa BORNE qui suit
   le contenant. Verifie aux trois largeurs : 1680 px -> 3 x 166 + 2 x 20,8 =
   539,6 pour 542 ; 375 px -> 263,5 pour 303 ; 320 px -> 244 pour 248.
   ⚠️ Et la borne basse descend de 88 a 76 px : la coquille d une FENETRE est
   plus etroite que celle de la page, de la largeur de son cadre et de son
   rembourrage. Le plancher suit le contenant, il ne le contredit pas. */
.modal.amis-win .podium { --poster-w: clamp(76px, 22vw, 166px); }

.amis-profil-tete { display: flex; flex-direction: column; align-items: center; }

/* ⚠️ TROISIEME TAILLE DE LA BULLE DU HUD, ET LA REGLE EST SCOPEE :
   `.profile-avatar { width: 28px }` vaut (0,1,0) et vit 1 800 lignes plus
   bas dans la feuille - a egalite, c est l ordre de source qui tranche. Le
   piege nº 2, attrape une troisieme fois dans ce bloc (V85 en avait pris
   deux). Le dessin suit en JS : 44 px, comme la bulle de 72 px de la V59 -
   sans quoi un embleme rendu a 22 px serait etire sur trois fois sa
   taille. */
.amis-profil .amis-grosse-bulle {
  width: clamp(64px, 16vw, 84px); height: clamp(64px, 16vw, 84px);
}

.amis-profil-nom {
  margin: .75rem 0 0;
  font-family: var(--display); font-stretch: var(--ch-affiche); font-weight: 800;
  font-size: clamp(1.2rem, 3vw, 1.7rem); color: var(--paper);
  overflow-wrap: anywhere;
}
/* ⚠️ LA BORNE DE 18ch DE `.profile-handle` (V73) PROTEGE UNE BANDE FIGEE ;
   ici on a la place de lire un identifiant en entier. Scopee pour la meme
   raison que la bulle. */
.amis-profil .amis-profil-handle { max-width: 100%; margin: .15rem 0 0; }

/* ⚠️⚠️ V155 (4c) - LE BOUTON D EDITION DE MA FICHE. C est `.sheet-btn`, la
   forme de bouton secondaire du site, REEMPLOYEE - une seconde forme aurait
   ete un troisieme dessin de bouton a tenir d accord. On ne pose ici que ce
   que le contexte impose : `.sheet-btn` porte `flex: 1 1 0` parce qu il vit
   d ordinaire dans une RANGEE de deux boutons (le panneau de profil) ; seul
   dans une colonne centree, il s etirerait sur toute la largeur du panneau.
   ⚠️ ET SA CIBLE FAIT PLUS DE 34 px par son padding : c est la cible du site
   (le coeur, la roue, le bouton Progression), pas une valeur choisie ici. */
.amis-profil-modif {
  flex: 0 0 auto; align-self: center;
  margin: clamp(.7rem, 2vw, 1rem) 0 0;
}

/* --------------------------------- La jauge ------------------------------
   ⚠️ CE N EST PAS LA BARRE HERO : pas de sortilege de Doom, pas
   d extremites en pointe, pas d Orbitron - le raisonnement complet est dans
   index.html, au-dessus de `#amisJauge`. Ce qui reste d elle, c est le VERT :
   la route d un ami mene a Doomsday comme la notre.
   ⚠️ ET SA BORNE DE LARGEUR EST CELLE D UNE COLONNE DE LECTURE, pas celle de
   la scene : `.amis-inner` la borne deja a 44 rem, la jauge s y pose pleine
   largeur. Une jauge de 1 600 px ne se lit pas mieux, elle se lit moins. */
.amis-jauge {
  margin: clamp(1.3rem, 3.5vw, 2rem) auto 0;
  width: 100%; height: 14px;
  background: var(--ink-1); border: var(--frame-dim); border-radius: var(--r-pill);
  overflow: hidden;
}
/* ⚠️ UN APLAT, PAS LE DEGRADE DE LA BARRE HERO - et c est une mesure, pas un
   gout. Celle-ci va de `--doom-deep` (#013A01, presque noir) a `--doom` sur
   la largeur du REMPLISSAGE : a 3 %, il ne reste que l extremite sombre,
   donc une jauge qui parait vide alors qu elle ne l est pas. La barre hero
   porte le meme defaut, mais elle est unique et enorme ; ici la valeur vient
   de quelqu un d autre et se lit en petit. */
/* ⚠️ V96 - `display: block` EST OBLIGATOIRE, ET C EST UNE MESURE. Cette
   classe sert desormais DEUX ecrans (le profil d un ami, le duo) : posee sur
   un element inline - un `span` - `width` NE S APPLIQUE PAS, et le
   remplissage fait ZERO pixel quel que soit le pourcentage. La jauge parait
   alors vide en permanence, SANS qu aucune erreur ne se produise. Le premier
   porteur etait un `div`, donc le defaut dormait ; le second l a reveille.
   Une classe partagee ne doit pas dependre du TYPE de l element qui la
   porte - c est la meme famille que le piege nº 2, ou une regle ne gagnait
   que par l ordre de source. */
/* ⚠️ V336 (S8) - LA JAUGE S ANIME PAR `transform`, PAS PAR `width` : animer
   une largeur refait la mise en page a chaque image ; une transformation est
   composee par le GPU. Ici c est sans risque - c est un APLAT : on pose la
   largeur PLEINE et on la comprime. `transform-origin: left` sinon elle
   grandirait depuis son centre. La valeur reste ecrite dans `width` par
   js/amis.js : c est `--pct` qui porte desormais le nombre. */
.amis-jauge-fill {
  display: block;
  height: 100%; width: 100%; min-width: 0;
  background: var(--doom);
  border-radius: var(--r-pill);
  transform: scaleX(var(--pct, 0)); transform-origin: left center;
  transition: transform .35s ease;
}
@media (prefers-reduced-motion: reduce) { .amis-jauge-fill { transition: none; } }

/* ⚠️⚠️ IL N EST PAS EN `--cosmic`, ET DEPUIS LA V216 IL EST LE SEUL PORTEUR
   DE CETTE REGLE : Orbitron ne sert QU AU pourcentage de la barre hero (V14,
   reaffirme en V44 et en V84). Un second nombre « spatial » etendrait son
   domaine. Le pourcentage de LIGNE la portait aussi ; il est parti avec les
   deux listes (V211, V216), donc c est ici qu elle se lit desormais. */
.amis-profil-pct {
  margin: .5rem 0 0;
  font-family: var(--display); font-stretch: var(--ch-chiffre); font-weight: 800;
  font-size: clamp(1.1rem, 2.6vw, 1.5rem); color: var(--doom-soft, var(--doom));
  font-variant-numeric: tabular-nums;
}

/* L intitule « Favoris » : le meme que ceux de la liste, centre. */
.amis-profil .amis-profil-h {
  margin-top: clamp(1.6rem, 4vw, 2.4rem); text-align: center;
}

/* ⚠️ LE PODIUM EST CELUI DE LA V60, ET IL GARDE SA BORNE BASSE - c est une
   mesure, pas un gout : trois cartes plus deux gouttieres doivent tenir dans
   la coquille au pire cas (3 x 88 + 16 = 280 a 320 px de fenetre). Rien n est
   redeclare ici : `.podium` et les `.favs-*` n ont jamais ete scopees a
   `#tabFav`, ce qui rend le reemploi gratuit - exactement le geste de la
   V61. */
.amis-profil .podium { margin-top: clamp(.8rem, 2vw, 1.2rem); }

/* ⚠️ UNE CARTE D AMI N A NI COEUR, NI BANDEAU, NI PASTILLE - et aucun des
   trois n est masque ici : ils ne sont PAS CONSTRUITS (voir le drapeau
   `card--ami` dans buildCard). Masquer aurait laisse des boutons qui ont
   existe, avec leurs ecouteurs : le geste que la V56 a refuse pour la
   pastille de la vitrine et la V84 pour le decompte. Il ne reste donc rien
   a declarer ici, et c est le signe que le drapeau est au bon endroit. */

/* ⚠️⚠️ V148 - LES CINQ REGLES DE « QUI PEUT ME VOIR » SONT PARTIES AVEC LEUR
   BALISAGE : `.amis-vis .sheet-hint`, `.account-inner .amis-vis`,
   `.amis-radios`, `.amis-radio` et ses trois etats. **Une classe sans
   porteur part en entier** - V59, V62, V97, V115, V116, V124, V136, V147.
   ⚠️ `.amis-bloc` et `.amis-h` RESTENT : elles servent aux autres blocs de
   l'onglet Amis, et elles n'ont jamais ete scopees a `.amis-tab` - c'est
   precisement ce qui avait rendu gratuit le demenagement de la V87.
   ⚠️ ET LA PAIRE `input[type=radio]` DISPARAIT DU SITE ENTIER avec elles :
   c'etait le seul endroit qui en portait. Le jour ou un choix binaire
   reviendra, le raisonnement se relit dans `docs/historique.md` - des radios
   NATIFS, jamais un interrupteur dessine, par le meme argument que le
   `<select>` de notation (V8). */

/* Pendant qu un appel vole, la liste ne se clique plus : deux gestes en vol
   sur le meme lien, c est la seconde reponse qui decide - et elle peut etre
   la premiere. */
.amis-tab.is-busy .amis-list { opacity: .55; pointer-events: none; }
/* ⚠️ V193 - LA MEME REGLE POUR LA FENETRE DE CREATION D UN DUO. Elle valait
   pour `.duo-tab` depuis la V170 ; l onglet est parti, la fenetre a pris sa
   liste, et elle herite du meme verrou. Un selecteur groupe, jamais une
   seconde valeur. */
#duoCreer.is-busy .amis-list { opacity: .55; pointer-events: none; }

/* --------------- LE PANNEAU D INVITATION (V176 - 5g-5) ------------------- */

/* ⚠️⚠️ IL VIT DANS LA CARTE, DONC IL SE DETACHE PAR UN FILET, PAS PAR UNE
   SURFACE. Un aplat de plus a l interieur d une carte qui en a deja un ferait
   trois niveaux d elevation pour deux niveaux d information (regle du bandeau
   de saga, V46). Le filet est celui du site - `--rule` - et la marge le
   separe des deux boutons juste au-dessus.
   ⚠️ ET IL ANNULE LE `margin-bottom` DE `.amis-add` : cette marge existe
   pour ecarter deux formulaires empiles dans une section, pas pour laisser un
   trou au bas d une carte. */




/* ⚠️ LE [+] D INVITATION (V170) : un carre, pas une gelule - c est un SIGNE,
   pas un mot, et c est l affordance unique du site (tiroirs, plis de route,
   rubriques de fiche). Il reprend `.cpt-out`, le bouton sourd des listes, et
   lui pose la cible de 34 px : celle du coeur de favori (V6), de la roue
   (V59), du bouton Progression (V82) et de la cloche (V167). */
/* ⚠️⚠️ V194 - `.duo-plus` REVIENT, ET SA DISPARITION ETAIT LA SECONDE FAUTE
   DE LA V193. Celle-ci l avait RENOMMEE en `.duo-prop`, en croyant qu elle
   n avait qu un porteur - l ecran du duo qu elle supprimait. Elle en avait
   TROIS AUTRES : le `[+]` du duo dans l ecran Amis (V174) et les deux `[+]`
   de l ecran Groupes (V180). Les trois sont retombes sur le style natif d un
   `<button>` sans qu aucune erreur ne se produise.
   ⚠️ LES DEUX COEXISTENT DESORMAIS, ET LA GEOMETRIE EST ECRITE UNE FOIS : un
   selecteur groupe, jamais deux jeux de valeurs (piege nº 6). Ce qui les
   distingue est leur CONTENU - un signe `+` pour l une, un dessin pour
   l autre. */
.duo-plus, .duo-prop {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; min-width: 34px; height: 34px; padding: 0;
}
.duo-plus { font-size: var(--fs-l); line-height: 1; }
/* ⚠️ « UN PETIT BOUTON BLANC », comme le retour le dit : le trace prend
   `--text`, pas une teinte de plus. */
.duo-prop svg { width: 20px; height: 20px; color: var(--text); }

/* ---- LE `[+]` DE CREATION, DANS LE RUBAN (V193) ----
   ⚠️ LA TAILLE EST PROPORTIONNELLE A CELLE DE L EMBLEME, comme le retour le
   demande : c est la MEME cible que `.ruban-bulle` - donc elle grandit avec
   elle au-dela de 1200 px, par le meme selecteur groupe. Un second jeu de
   valeurs aurait diverge au premier reglage.
   ⚠️ UN CERCLE FIN, UNE CROIX CENTREE : le `+` est l affordance unique du
   site (tiroirs, plis de route, rubriques de fiche) - ici, ajouter c est
   proposer. */
.ruban-plus {
  appearance: none; cursor: pointer;
  background: transparent; border: 0; padding: 4px;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 40px; min-height: 40px;
  border-radius: var(--r-pill);
  color: var(--ink-7);
  transition: color .16s, background-color .16s;
}
.ruban-plus svg { width: 31px; height: 31px; }
.ruban-plus:hover { color: var(--text-2); background: var(--ink-3); }
.ruban-plus:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

/* ⚠️ LE MESSAGE DE LA ROUTE (V193) SE CENTRE SOUS LES DEUX BOUTONS, comme la
   chose dont il parle. Il vaut 0 px la plupart du temps - `[hidden]`. */
.route-msg { margin: .35rem auto 0; max-width: 30rem; text-align: center; }

/* ⚠️ SOUS 560 px, LES ACTIONS PASSENT SOUS LE NOM. A deux boutons sur la meme
   ligne qu un pseudo, la ligne se coupait n importe ou : elles prennent leur
   propre rang, alignees a droite. Le pourcentage, lui, reste avec le nom -
   c est ce qu on vient lire. */
@media (max-width: 560px) {
  .amis-item .amis-actions { flex: 1 0 100%; justify-content: flex-end; }
}

/* ------------------------------- Phase --------------------------------- */

/* ⚠️⚠️ V195 - LES DEUX SECTIONS QUI SUIVENT LA BARRE HERO N AJOUTENT PLUS
   LEUR PROPRE RESERVE EN HAUT, ET C EST UN ECART QUI CHANGE DE PROPRIETAIRE,
   PAS UNE VALEUR DE PLUS. La barre hero porte deja une reserve BASSE
   (`clamp(.9rem, 2vw, 1.4rem)`) ; `--sect-pad` en posait une SECONDE, de
   36 px, juste dessous. Deux reserves pour un seul ecart faisaient 58 px de
   vide entre le ruban et le premier bandeau de saga - releve au DOM, et c est
   ce que le retour signale.
   ⚠️ CE SONT LES DEUX SEULES CONCERNEES, ET LA TABLE `ENTETE` LE DIT : `road`
   et `archives` sont les seuls onglets qui montrent la barre (`barre: true`).
   Les autres sections gardent `--sect-pad` entier - elles n ont rien au-dessus
   d elles.
   ⚠️ LE JETON NE BOUGE PAS D UN CHIFFRE : le toucher aurait remonte SIX
   sections pour un ecart qui n en concerne que deux.

   ⚠️⚠️ ET LA LONGHAND EST DANS LE BLOC, PAS DANS UN SELECTEUR GROUPE POSE
   PLUS BAS. `.roadmap, .archive { padding-top: 0 }` aurait valu (0,1,0) -
   exactement le poids des deux regles qu elle corrige - et n aurait donc gagne
   que par ORDRE DE SOURCE : « une regle qui ne gagne que par ordre de source
   ne gagne pas, elle attend » (piege nº 2). Une longhand qui SUIT sa shorthand
   dans le MEME bloc gagne sans poser la question.
   ⚠️ LES DEUX ZEROS NE SONT PAS LE PIEGE Nº 6 : ce n est pas une mesure tenue
   a la main, c est l ABSENCE d une reserve. Le jour ou il en faudrait une, elle
   deviendrait un jeton - pas deux nombres. */
.roadmap { flex: 1; padding: var(--sect-pad); padding-top: 0; }

.phase { margin-bottom: clamp(2rem, 5vw, 3.5rem); }

/* Le titre garde la gouttiere du site ; la RANGEE, elle, prend toute la
   largeur de la fenetre - sinon une phase un peu longue force une barre de
   defilement alors qu il reste de la place a l ecran. */
.phase-header {
  /* Le bandeau n est plus centre : c est une BARRE pleine largeur.
     Un titre centre flottait ; une barre s empile. */
  text-align: left; margin-bottom: clamp(.2rem, .6vw, .5rem);
  width: var(--shell); margin-inline: auto;
}
/* Plus d'aplat. Le titre de rangee et son embleme portent la couleur du JALON
   (accentSoft, la variante lisible sur noir) : une route qui s'appelle "Road to
   Ultron" doit etre rouge Ultron, pas jaune. Le halo et la pulsation restent
   ceux des liaisons. */
/* ⚠️⚠️ V331 - VISUEL_B (S3, B4) : DEUX RENVERSEMENTS ECRITS.
   ① LES TITRES DE ROUTE PASSENT EN CAPITALES, serres (chasse 58 % de Saira).
   La regle du Visuel_A etait « Casse normale : "Road to Avengers", pas
   "ROAD TO AVENGERS" » : l auteur a choisi le generique de cinema.
   ② LE HALO PULSE (`phaseglow`) EST PARTI, decision de l auteur effet par
   effet : il ne portait aucune information. La couleur du jalon reste. */
.phase-title {
  margin: 0; display: flex; align-items: center; gap: .55rem;
  padding: 0;
  font-family: var(--display); font-weight: 600; font-stretch: var(--ch-route);
  font-size: clamp(1.7rem, 3.6vw, 2.5rem);
  text-transform: uppercase; letter-spacing: .1em; line-height: 1.1;
  color: var(--phase-accent-soft);
  background: none;
}

/* ==================== L'AVERTISSEMENT SUR L'AFFICHE (V32) ================
   Il est POSE SUR L'AFFICHE, en position absolue : il ne coute pas un pixel
   de hauteur, donc les pastilles restent alignees par construction - c'est
   l'invariant de la rangee.
   ⚠️ `.scene-band` EST SUPPRIME (V48) avec le `type: "scene"` : il vivait
   HORS de la pile `.card-flags` alors que c'etait la meme famille, aux
   memes coordonnees, avec un ordre d'empilement emergent. Sa seule fiche a
   disparu - Black Widow est complet et pose au bon moment. */
/* LA PILE D'ETIQUETTES (V39). Statut et avertissement partagent le meme
   coin, la meme forme et le meme corps : ce sont deux etiquettes, pas deux
   objets. La pile est en position absolue, donc elle ne coute toujours PAS
   un pixel de hauteur - les pastilles restent alignees par construction. */
/* ⚠️ V454 - LA PILE CEDE LE COIN AU BANDEAU VERTICAL et se decale de sa
   largeur : les deux vivaient en haut a gauche, elles se seraient recouvertes.
   Il ne reste dans la pile que « À revoir ». */
.card-flags {
  /* ⚠️ V480 - LA PILE RETROUVE LE COIN : le bandeau vertical qui l en avait
     chassee (V454) est descendu sous l affiche. Plus aucune largeur a lire. */
  position: absolute; z-index: 3; top: 6px; left: 6px;
  /* align-items: flex-start est PORTANT : sans lui une colonne flex etire
     les deux etiquettes a une largeur commune, la plus longue imposant sa
     taille a l'autre. */
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
}
/* ⚠️⚠️ V136 - « .card-warning » ET « .modal-warning » SONT PARTIES AVEC LE
   CHAMP « warning », qui ne designe plus AUCUNE fiche depuis la V48 : Black
   Widow, seul titre a cheval, a ete DEPLACE au lieu d etre scinde. La forme
   commune que ces deux classes partageaient avec « .status-chip » descend donc
   sur « .status-chip » seule, plus bas.
   ⚠️ « .card-flags » NE PART PAS : la pile porte aussi l etiquette de STATUT
   (V34), et c est elle qui garde l invariant de la rangee - en position
   absolue, elle ne coute pas un pixel de hauteur. */

/* ==================== L'ENCART (V49) ================================
   ⚠️ IL REMPLACE LE DOSSIER, ET LE DOSSIER N'EXISTE PLUS. La regle de la
   V49 - « un bouton ne range que des BONUS » - vide le dernier dossier du
   site : les cinq Spider-Man de Sony sont tous `recommande`. Son bouton,
   son compte, sa fenetre, son voile et ses sous-groupes depliants sont
   partis avec lui, soit ~210 lignes de CSS pour zero donnee.

   ⚠️ CE QUE LE NOM ET LA PHRASE DISAIENT, LE CADRE LE MONTRE. Un dossier
   devait s'expliquer parce qu'il etait FERME - on ne comprend pas pourquoi
   Tobey Maguire apparait au milieu du MCU si on ne l'ouvre pas. Un encart
   est ouvert : les cinq affiches sont la, et le cadre dit simplement
   « celles-ci ne sont pas de la meme maison ».

   ⚠️ LA GEOMETRIE EST CELLE DU CADRE DE JALON, AU PIXEL. Deux marges
   negatives SYMETRIQUES annulent exactement le padding : les cartes de
   l'encart retombent sur l'axe de la rangee par CONSTRUCTION, comme la
   carte d'un jalon. C'est l'invariant de la rangee, et il ne se verifie pas
   a l'oeil.

   ⚠️ ET LE CADRE NE TRANCHE PAS. Aplat interieur a 13 %, filet a 30 % : un
   encart plus fort que les cartes qu'il contient ferait de la parenthese le
   sujet de la ligne. Le rouge et le bleu sont ceux du costume - c'est la
   seule identite que ces cinq films aient en commun. */
.encart {
  --enc-pad: var(--mf-pad);
  --enc-off: calc(-2px - var(--enc-pad));
  --encart-accent: var(--text-dim);
  flex: 0 0 auto; align-self: stretch;
  display: flex; flex-direction: column; align-items: center; gap: .34rem;
  padding: var(--enc-pad);
  margin-top: var(--enc-off);
  margin-bottom: var(--enc-off);
  border-radius: var(--r-3);
  border: 2px solid color-mix(in srgb, var(--encart-accent) 30%, transparent);
  background: color-mix(in srgb, var(--encart-accent) 13%, transparent);
}
/* ⚠️ DEUX TEINTES : le rouge et le bleu du costume. Le filet les prend en
   diagonale - haut/gauche a la premiere, bas/droite a la seconde - et
   l'aplat les interpole. Un encart sans `accent2` reste en aplat d'une
   seule teinte : la classe n'est posee que si la donnee la fournit. */
.encart--duo {
  border-color: color-mix(in srgb, var(--encart-accent) 30%, transparent)
                color-mix(in srgb, var(--encart-accent-2) 30%, transparent)
                color-mix(in srgb, var(--encart-accent-2) 30%, transparent)
                color-mix(in srgb, var(--encart-accent) 30%, transparent);
  background: linear-gradient(100deg,
    color-mix(in srgb, var(--encart-accent) 13%, transparent),
    color-mix(in srgb, var(--encart-accent-2) 13%, transparent));
}
.encart-body {
  display: flex; align-items: flex-start; gap: calc(var(--gap) * 1.5);
}
.encart-group { display: flex; flex-direction: column; gap: .3rem; }
.encart-row { display: flex; align-items: stretch; gap: var(--gap); }
/* ⚠️ L'INTERTITRE PASSE SOUS SES CARTES (V49) et non plus au-dessus. Une
   ligne de texte posee au-dessus des affiches decalerait l'encart entier
   vers le bas : les cinq cartes ne seraient plus sur l'axe de la rangee, et
   l'alignement des pastilles - l'invariant du site - sauterait. */
.encart-group-label {
  margin: 0; text-align: center;
  font-size: var(--fs-micro); font-weight: 700;
  text-transform: uppercase; letter-spacing: .1em;
  color: var(--text-dim);
}
.encart-label {
  margin: 0; text-align: center;
  font-size: var(--fs-xs); font-weight: 800;
  text-transform: uppercase; letter-spacing: .14em;
  color: var(--encart-accent, var(--text-dim));
}
.encart--duo .encart-label {
  background-image: linear-gradient(100deg, var(--encart-accent), var(--encart-accent-2));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* Rangee unique : prerequis puis JALON, de gauche a droite */

/* ====================== LE BANDEAU DE SAGA (V38, deplie en V52) =========
   Le MCU se lit en deux sagas : l'Infini (phases 1-3) et le Multivers
   (phases 4-6).

   ⚠️ IL NE SE PLIE PLUS (V52), ET C EST UN RENVERSEMENT ASSUME DE LA REGLE
   "UNE SEULE AFFORDANCE" (V38). Le pli de saga etait un pli DE PLI : replier
   une saga, c'etait cacher trois routes qui ont deja chacune leur `+`/`-`.
   Deux niveaux de pli imbriques posent la question que la V38 posait deja
   pour les sous-groupes - on ne sait plus ce qu'on ferme - et la maquette B
   la tranche : seules les ROUTES gardent le signe. La saga redevient ce
   qu'elle a toujours voulu dire : un intertitre entre deux filets.

   ⚠️ ET CE QUI DISPARAIT AVEC LUI EST REEL : le signe, l'etat
   `mcu-roadmap:sagas:v1`, `sagaOpen`/`setSagaOpen`, la classe
   `is-folded` sur la saga, la regle qui la forcait ouverte sous filtre et
   sa correction de specificite en vue colonnes. Un pli qui ne plie plus
   n'est pas un pli desactive, c'est du code mort - piege nº 4.

   ⚠️ PAS de defile d'affiches, et surtout PAS D'ACCENT SUR LA SURFACE. La
   regle de la V38 tient toujours : sept teintes de route sont prises, une
   huitieme entrerait en collision, et une barre de saga PEINTE ressemble a
   une barre de route. ⚠️ L argument "on ne saurait plus lequel des deux plis
   on ferme" est PERIME depuis la V52 - il n y a plus qu un pli - mais la
   regle tient par les deux autres : la collision de teintes, et le fait
   qu une saga n est pas une route.

   ⚠️ LA V46 N'A PAS LEVE CETTE REGLE, ELLE EN A PRECISE L'OBJET : c'est la
   SURFACE qui ne prend pas de couleur, pas le bandeau. La marque de saga en
   porte, parce qu'un dessin de 22 px n'est pas une surface. Ce qui separe les
   deux niveaux est l'ELEVATION, et c'est la V44 qui l'a rendu possible : la
   barre de ROUTE est une surface POSEE (--ink-1, bordure, --lift-0), la barre
   de SAGA reste TRANSPARENTE. Une teinte ne peut pas transformer la seconde
   en variante de la premiere - elle n'a rien a peindre.

   ⚠️⚠️ V83 - ELLE N'EST PLUS "ENTRE DEUX FILETS", ET C'EST L'ECHELLE QUI
   REPREND SEULE LA CHARGE. Les deux filets etaient le second signe de la
   surface absente ; le menage de B5 les retire, et le titre grandit dans le
   MEME geste - ce n'est pas une coincidence de version, c'est la
   compensation. Verifie a l'oeil : le couple marque + titre en capitales,
   pose entre 2,2 rem de marge haute et 1,1 rem de respiration basse, se lit
   toujours comme un palier au-dessus des routes. ⚠️ Si quelqu'un trouve un
   jour la saga trop discrete, la reponse n'est PAS de remettre les filets -
   c'est l'echelle ou l'espacement, les deux leviers qui restent.

   ⚠️ Elle se distingue donc par l'echelle SEULE : plus grande, en capitales.
   Meme raisonnement que "le titre est blanc, l'annee est petite" (V5.1).

   ⚠️ ELLE N A PLUS DE SIGNE (V52) : voir l en-tete de section. Le `+`/`-`
   reste l affordance unique du site pour tout ce qui SE PLIE - routes,
   tiroirs, rubriques de fiche. La saga ne se plie plus, donc elle n en porte
   plus ; la regle n a pas d exception, elle a un objet de moins. */
.saga { margin: 0 0 1.6rem; }
.saga + .saga { margin-top: 2.2rem; }

/* ⚠️ PLUS DE `padding` LATERAL DE 2.6rem : il reservait la place du signe
   `+`/`-`, qui n'existe plus. Le garder aurait laisse une gouttiere vide de
   chaque cote - la trace d'un objet retire. */
.saga-band {
  position: relative;
  width: 100%;
  display: flex; align-items: center; justify-content: center;
  min-height: 58px;
  padding: .5rem 1rem;
  background: transparent;
  color: inherit; text-align: center;
}

/* ------------------- LA MARQUE DE SAGA (V46) --------------------------
   L'element OFFICIEL de la saga - les six Pierres, l'incursion - dessine
   en SVG avec ses propres couleurs (SAGA_MARKS dans app.js).

   ⚠️ ELLE FAIT BLOC AVEC LE TITRE - `.saga-lockup`, un RANG a elle deux.
   L'embleme appartient au titre, pas a la barre : pose a un bord il
   redeviendrait une decoration d'angle. C'est le seul centrage du site qui
   porte sur un couple plutot que sur le seul texte. ⚠️ Depuis la V52 rien
   d autre ne vit dans la barre : le couple est centre sur la page sans avoir
   a compenser quoi que ce soit.

   ⚠️ ET C'EST UN RANG, PAS UNE SOEUR DU BLOC. Posee a cote de
   `.saga-band-body`, la marque se centrait sur le couple titre + compte,
   c'est-a-dire 6,3 px SOUS l'axe du titre (mesure). Un embleme qui appartient
   au titre se pose sur sa ligne, jamais sur la moyenne de deux lignes.

   ⚠️ ELLE NE S'ALLUME PAS, contrairement au cadre d'un jalon ou a sa
   pastille. Ce n'est pas une recompense : il n'existe aucune carte "saga" a
   cocher. C'est une identite, au meme titre que le defile d'une route. */
.saga-lockup {
  display: flex; align-items: center; justify-content: center;
  gap: var(--sp-3); min-width: 0;
}
.saga-mark { flex: 0 0 auto; display: block; line-height: 0; }
/* ⚠️ LA MARQUE SUIT LE TITRE (22 -> 24 px, V83). Ils forment un RANG : un
   titre qui grandit seul deseqilibre le couple, et l'embleme redeviendrait
   une vignette a cote d'un mot. Le repli 560 px, lui, NE BOUGE PAS - a cette
   largeur le titre ne grandit pas non plus. */
.saga-mark svg { display: block; height: 24px; width: auto; }

.saga-band-body {
  display: flex; flex-direction: column; align-items: center; gap: .1rem;
  min-width: 0;
}
/* ⚠️ V83 - +8 % SUR LE HAUT DE LA FOURCHETTE, PAS SUR LE BAS, ET C'EST UNE
   MESURE. Le plancher reste a 1.05rem : sous 730 px c'est lui qui s'applique
   (2.3vw n'atteint 1.05rem qu'a cette largeur), donc le couple marque + titre
   ne bouge pas d'un pixel la ou il tenait deja au plus juste - 294 px pour
   304 disponibles a 375 px, mesure en V46. Faire grandir le plancher aurait
   repasse le titre a deux lignes sur telephone, ce que le repli 560 px avait
   precisement ete ecrit pour eviter. C'est en haut de la fourchette, ou il y
   a de la place, que l'agrandissement se paie. */
/* V331 - Visuel_B : le bandeau de saga se tait d un cran (graisse 600, gris
   d intitule) - ce sont les routes qui portent la couleur et la voix. */
.saga-title {
  font-family: var(--display); font-stretch: var(--ch-affiche);
  font-size: clamp(1.05rem, 2.3vw, 1.62rem); font-weight: 600;
  text-transform: uppercase; letter-spacing: .08em;
  color: var(--text-dim);
}
.saga-progress {
  font-family: var(--display); font-stretch: var(--ch-ui);
  font-size: var(--fs-micro); letter-spacing: .07em; color: var(--text-dim);
}
.saga-progress.is-done { color: var(--doom); }

.saga-body { padding-top: 1.1rem; }

@media (max-width: 560px) {
  .saga-band { min-height: 52px; padding: .45rem .8rem; }
  /* ⚠️ La marque suit le titre a la baisse. Mesure a 375 px : le couple
     faisait 294 px pour 304 disponibles - dix pixels de marge, c'est-a-dire
     aucune. Elle descend a 18 px et le couple a 287 px. En dessous de 375 le
     titre passe a deux lignes et la barre grandit : elle ne deborde pas, elle
     respire, ce qui est le bon echec. */
  .saga-lockup { gap: .58rem; }
  .saga-mark svg { height: 18px; }
}

/* ==================== LE PLI DE ROUTE (V33) ==========================
   Le bandeau de route est un BOUTON pleine largeur. Repliees, les routes
   s'empilent comme une pile de livres.

   ⚠️ Une barre repliee n'est pas vide : elle porte son DEFILE (V35), une
   bande d'affiches reduites a des tranches verticales. C'est LUI qui
   l'empeche d'etre un menu. La vignette de la V33 et le compte de la V34
   etaient deux reponses a ce meme probleme, chacune retiree une fois que le
   defile l'avait resolu - l'une en V34, l'autre en V46. */
/* V228 - le <h3> qui enveloppe la barre (motif d'accordeon) : il ne dessine
   rien, il nomme. */
.phase-heading { margin: 0; font: inherit; }
/* V261 - LA COCHE « TOUTE LA ROUTE », au bord GAUCHE de la barre (le +/- tient
   la droite). Absolue sur l en-tete : elle ne pese pas sur le titre centre,
   que le padding de la barre protege deja. Cible de 34 px (regle du site).
   Faite : verte (le vert de « vu »), et inerte. */
.phase-header { position: relative; }
/* ⚠️⚠️ V401 - LE COMPTE A DROITE, LE BOUTON A GAUCHE (renversement de la
   V371b, « le compteur est le bouton »). Le compte « 2/6 » est un TEXTE pres du
   +/- : il n a ni cadre ni survol, rien qui invite le doigt. « Tout cocher »
   est un bouton NOMME, au bord oppose - lire et agir ne se touchent plus. Le
   padding de la barre (7.6rem de chaque cote) garde le titre centre. */
/* ⚠️⚠️ V464 - LES DEUX BORDS ECHANGENT (retour de l auteur : « les [+] [...]
   passent a gauche », « Tout cocher doit se trouver a droite a la place du
   [+] »). Le compte suit le signe, comme avant : il est un TEXTE pose pres de
   lui, pas une commande (V401). Ce qui ne change pas : lire et agir restent
   aux deux bords opposes de la barre. */
.phase-compte {
  position: absolute; z-index: 3; left: 2.6rem; top: 50%; transform: translateY(-50%);
  pointer-events: none;
  font-family: var(--display); font-weight: 700; font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums; color: var(--text-dim);
}
.phase-compte.is-fait { color: var(--tier-must); }
.phase-compte-sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
/* ⚠️⚠️ V464 - LE BOUTON EST SON DESSIN (retour de l auteur : « le logo des
   2 checks devient le bouton, l encadrement disparait et les logos sont plus
   imposants »). La capsule et le mot etaient la reponse de la V401 a un vrai
   probleme - « le compteur est le bouton » cochait une route entiere par
   accident - mais ce probleme est regle par la SEPARATION des deux objets,
   pas par le cadre. Le mot part donc, et le dessin grandit de 16 a 24 px.
   ⚠️ LA CIBLE NE RETRECIT PAS : 34 px de haut et de large, le plancher du
   site, meme si le dessin n en occupe que 24. Un bouton qui commande six a
   douze titres d un coup ne se paie pas en surface.
   ⚠️ ET IL RESTE NOMME POUR QUI NE VOIT PAS LE DESSIN : `aria-label` dit
   « Tout cocher : {route}, {n} titres requis » et ne bouge pas d un mot. */
.phase-tout {
  position: absolute; z-index: 3; right: .7rem; top: 50%; transform: translateY(-50%);
  height: 34px; min-width: 34px; display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  background: transparent; border: 0; border-radius: var(--r-pill);
  color: var(--text-dim); cursor: pointer;
  transition: color .14s;
}
.phase-tout svg { width: 24px; height: 24px; flex: 0 0 auto; }
.phase-tout-mot { display: none; }   /* V464 - le dessin porte le geste */
.phase-tout:hover { color: var(--paper); }
.phase-tout:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.phase-fold {
  position: relative; overflow: hidden;
  width: 100%;
  /* ⚠️ TOUT EST CENTRE (V35) : le titre et le compte sur l'axe de la page,
     le signe sur l'axe VERTICAL de la barre. Le signe est en position
     absolue precisement pour ne pas peser sur ce centre - meme raison que
     l'identite dans le HUD. */
  display: flex; align-items: center; justify-content: center;
  /* V331 - Visuel_B : le format de bandeau de Premium - 68 px, coins de
     16 px, surface POSEE (--ink-2) sans filet visible au repos. */
  min-height: 68px;
  /* V261 - 3.4rem (etait 2.6) : la coche « toute la route » tient la gauche,
     le +/- la droite, et le titre reste centre entre les deux. */
  padding: .62rem 7.6rem;          /* V371b - le compteur tient la droite */
  /* ⚠️ Ecrit "var(--frame) solid #1C1C23" jusqu'a la V43, c'est-a-dire
     "1px solid X solid Y" : un raccourci INVALIDE, donc une declaration
     entierement jetee par le navigateur. La barre de route n'avait aucune
     bordure depuis la V33, et personne ne l'a vu parce que rien ne casse -
     une bordure absente ne leve pas d'erreur.
     ⚠️ V331 : le filet est TRANSPARENT, pas retire - le survol le teinte,
     et une bordure qui apparait au survol ferait bouger la barre d un pixel. */
  border: 1px solid transparent;
  border-radius: var(--r-bande);
  background: var(--ink-2);                  /* ⚠️ un APLAT, pas un degrade */
  box-shadow: var(--lift-0);
  color: inherit; text-align: center; cursor: pointer;
  transition: border-color .25s ease, transform .12s ease;
}
.phase-fold:hover {
  border-color: color-mix(in srgb, var(--phase-accent-soft, var(--phase-accent)) 45%, var(--ink-3));
}
.phase-fold:active { transform: translateY(1px); }

/* ---------------------------- LE DEFILE -------------------------------
   Les affiches de la route en bande, chacune reduite a une tranche
   verticale : le geste du generique Marvel. Noir et blanc, tres sombre,
   et un voile central pour que le titre reste lisible par-dessus.
   ⚠️ Rien ne bouge : une bande qui defile en permanence derriere trois
   titres tirerait l'oeil hors de la rangee, qui est le sujet. */
.phase-reel {
  position: absolute; inset: 0; z-index: 0;
  display: flex; align-items: stretch;
  pointer-events: none;
  /* V331 - Visuel_B : les tranches gardent leur COULEUR, eteinte (Premium).
     La regle de la V35 tient : c est une texture, pas une vignette - le voile
     central garde le titre lisible. */
  opacity: .28;
  filter: saturate(.7) brightness(.8);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
  transition: opacity .25s ease;
}
.phase-reel img { flex: 1 1 0; min-width: 0; height: 100%; object-fit: cover; display: block; }
/* Ouverte, la route se lit dans la rangee : la barre s'efface. */
.phase:not(.is-folded) .phase-reel { opacity: .14; }
.phase-fold:hover .phase-reel { opacity: .36; }

/* Le voile : sans lui, le titre tombe sur une affiche claire une fois sur
   deux. Il est centre parce que le titre l'est. */
.phase-fold::before {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: radial-gradient(ellipse 46% 130% at 50% 50%,
              var(--ink-2) 0%, color-mix(in srgb, var(--ink-2) 80%, transparent) 45%, transparent 80%);
}

.phase-fold-body {
  position: relative; z-index: 2;
  flex: 0 1 auto; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: .14rem;
}
/* ⚠️ `.phase-progress` EST SUPPRIME (V46). Le compte etait la reponse de la
   V34 a une barre vide ; la V35 a resolu ce probleme autrement, avec le
   defile. Il ne restait qu'une seconde ligne de gris, identique d'une barre a
   l'autre, qui donnait aux sept le meme gabarit. Le nombre remonte d'un
   cran : le HUD dit le total, le bandeau de SAGA dit ou l'on en est. */

/* Le meme signe que les tiroirs : une seule affordance sur tout le site. */
/* ⚠️ V464 - A GAUCHE, ET BLANC (retour de l auteur). Il etait a droite depuis
   la V35 et prenait la couleur de la route une fois la rangee repliee ; les
   deux bords ayant echange, et l auteur voulant ces signes blancs, il ne reste
   qu une seule couleur pour les deux etats - donc plus rien a tenir d accord
   entre replie et deplie. */
.phase-sign {
  position: absolute; z-index: 2;
  left: .95rem; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--paper);
}
.phase-sign::before, .phase-sign::after {
  content: ""; position: absolute; background: currentColor; border-radius: 2px;
}
.phase-sign::before { left: 0; right: 0; top: 7px; height: 2px; }
.phase-sign::after  { top: 0; bottom: 0; left: 7px; width: 2px; }
.phase:not(.is-folded) .phase-sign::after { display: none; }

/* Repliee : la rangee disparait, la barre reste. */
.phase.is-folded .phase-chain { display: none; }
.phase.is-folded { margin-bottom: .35rem; }

/* V331 - a 320 px, « UN MULTIVERS FRAGMENTE » en capitales espacees passait
   sur deux lignes (208 px pour 206) : l interlettrage cede avant le corps. */
@media (max-width: 380px) {
  .phase-title { letter-spacing: .04em; font-size: 1.55rem; }
}
@media (max-width: 560px) {
  .phase-fold { min-height: 62px; padding: .4rem 2.2rem 1.45rem; }
  .phase-sign { left: .6rem; }
  /* V371b - le compteur se range dans le coin bas droit, sans cadre : le titre
     garde toute sa largeur (« UN MULTIVERS FRAGMENTE » tient a 320 px).
     V401 - le compte et le bouton se partagent cette ligne du bas : le compte
     a droite, « Tout cocher » a gauche. */
  .phase-compte { top: auto; bottom: 0; left: .55rem; transform: none; line-height: 22px; }
  .phase-tout { top: auto; bottom: 0; right: .2rem; transform: none; padding: 0; height: 22px; }
  .phase-tout svg { width: 19px; height: 19px; }
  /* ⚠️⚠️ V392 - 22 px A L OEIL, 34 px SOUS LE DOIGT. Mesure a 390 x 844 : le
     bouton qui coche TOUTE une route etait la plus petite cible du telephone
     (49 x 22), sous le plancher de 34 px que le site tient partout ailleurs -
     et il porte la seule action du site qui touche six a douze titres d un
     coup. La zone invisible est celle des pastilles « vu » (`inset` negatif) :
     elle ne deplace rien, ne redessine rien, et ne coute pas un pixel au
     titre - ce que la V371b protegeait.
     ⚠️ ELLE S ETEND VERS LE HAUT, JAMAIS VERS LE BAS. Mesure : le bouton est
     pose a `bottom: 0` de la barre, donc six pixels sous lui tombent DEHORS,
     sur la premiere ligne de la rangee - qui gagne le point et rend la cible a
     28 px. Douze pixels au-dessus restent dans la barre, sur du vide. */
  .phase-tout::after { content: ""; position: absolute; inset: -12px -3px 0; }
}

/* La rangee est une PILE DE LIGNES (V32). Une route longue ne scrolle plus :
   elle se replie tous les 5 films, et la coupure est SILENCIEUSE (V34). Le defilement horizontal reste le filet de securite -
   il ne doit plus jamais servir. */
.phase-chain {
  display: flex; flex-direction: column; align-items: center;
  overflow-x: auto;
  scrollbar-width: thin;
}
.chain-line {
  display: flex; flex-wrap: nowrap;
  align-items: stretch;                 /* toutes les cartes a la meme hauteur */
  justify-content: safe center;
  gap: var(--gap);
  padding: calc(10px + var(--mf-pad)) .875rem;
}
/* ⚠️ LA COUPURE DE LIGNE EST SILENCIEUSE (V34). Elle portait une colonne de
   points - le "virage" de la V32. Une colonne verticale de points au milieu
   du noir ne ressemble a rien, et elle SAUTE d'un endroit a l'autre chaque
   fois qu'un tiroir s'ouvre et redistribue la rangee : un objet mobile que
   l'oeil suit sans rien y gagner.
   On lit de gauche a droite puis on descend - personne n'a besoin qu'on le
   lui dise. C'est le vide qui porte l'information, comme pour l'ordre libre. */
.phase-chain > .chain-line + .chain-line { margin-top: calc(var(--gap) * -.3); }

/* ================== LES LIAISONS ONT DISPARU (V48) ======================
   `.link-node`, `.link-dot`, `@keyframes flow` et `--link-len` sont
   retires. Les points racontaient une progression vers le jalon que la
   rangee dit deja par la position, et ils coutaient `--poster-w * 0.5` par
   liaison - la place de trois cartes sur une rangee de six. Le concept est
   archive dans docs/historique.md.
   ==================================================================== */

/* ------------------------------- Carte film ---------------------------- */

.card {
  flex: 0 0 auto;
  width: var(--poster-w);
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
}

/* Conteneur de l'affiche : porte les deux commandes en surimpression
   (coeur en haut a droite, "i" en bas a droite - diagonales opposees, elles
   ne peuvent donc jamais se chevaucher). */
/* ⚠️ V480 - DEUX ETAGES : `.poster-wrap` est l enfant de carte (les grilles
   de la vue liste et de la vue colonnes le nomment), `.poster-cadre` est la
   boite de l IMAGE. Tout ce qui se pose sur l affiche - le coeur, le « i »,
   la pile d etiquettes, les deux voiles d angle - se cale sur le cadre : cale
   sur le wrap, il se serait pose sur la languette le jour ou elle est
   apparue, sans qu aucune erreur ne se produise. */
.poster-wrap { width: 100%; display: block; }
.poster-cadre { position: relative; width: 100%; display: block; }

.poster-btn {
  appearance: none; padding: 0; cursor: pointer;
  width: 100%; display: block; position: relative; overflow: hidden;
  background: var(--panel);
  /* ⚠️⚠️ V474 - PLUS AUCUN CADRE (retour de l auteur : « supprime totalement
     l encadrement pour les affiches »). Il a ete, en trois versions, la teinte
     de la route (V463 : blanc discret), puis rien : une affiche est deja un
     rectangle plein sur un fond noir, c est SON BORD qui la pose. Un cadre
     n ajoutait qu une seconde ligne a un pixel de la premiere.
     ⚠️ LE SURVOL ET LE FOCUS RESTENT, EN OMBRE : sans cadre, il faut toujours
     que le doigt et le clavier sachent ou ils sont. Une ombre portee ne
     deplace aucun pixel, la ou une bordure qui apparait au survol ferait
     sauter l affiche d un pixel (elle est DANS la boite). */
  border: 0;
  border-radius: var(--r-affiche);   /* V479 - un rectangle, pas une pastille */
  /* ⚠️ AUCUNE ombre au repos, et ce n'est pas un oubli : l'ancienne posait
     du noir a 60% sur un fond noir, elle ne rendait aucun pixel. Une
     affiche est SEULE sur la page - c'est son cadre qui la pose, pas une
     elevation qu'elle n'a pas. L'elevation arrive AU SURVOL, la ou un
     changement de hauteur veut dire quelque chose. */
  transition: border-color .16s ease-out, box-shadow .16s ease-out;
}
.poster-btn:hover, .poster-btn:focus-visible {
  box-shadow: 0 0 0 2px var(--cadre-survol), var(--lift-1);   /* V474 - l anneau remplace le cadre */
  outline: none;
}

.poster-btn img {
  display: block; width: 100%; aspect-ratio: var(--poster-ratio);
  object-fit: cover; background: var(--panel-2);
  transition: filter .25s;
}

/* Affiche desaturee tant que le film n'est pas vu */
.card:not(.is-watched) .poster-btn img { filter: grayscale(.75) brightness(.8) contrast(1.05); }
.card.is-watched .poster-btn img { filter: none; }

/* ------------- Commandes posees sur l'affiche : coeur et "i" ------------ */
/* Regle de lisibilite : chaque commande est posee sur un VOILE d'angle (un
   degrade sombre qui s'eteint en diagonale) ET porte une ombre.  Sans cela,
   un coeur rose sur l'affiche d'Iron Man ou le ciel clair de Captain America
   disparait.  Le voile est assez discret pour ne pas se remarquer. */

.fav-btn, .info-btn {
  position: absolute; z-index: 3;
  appearance: none; border: 0; background: transparent; padding: 0;
  width: 34px; height: 34px; cursor: pointer;
  display: grid; place-items: center;
  transition: opacity .16s, color .16s;
}
.fav-btn svg, .info-btn svg {
  width: 19px; height: 19px;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .95));
}

.fav-btn  { top: 0; right: 0; }
.info-btn { bottom: 0; left: 0; color: #FFFFFF; }   /* V480 - dans le cadre, donc sur l image */

/* ============ V480 - LA LANGUETTE SOUS L AFFICHE ==========================
   Retour de l auteur : « le bandeau du statut, des notes communautaire et
   personnelle est place sous l affiche du film, colle au film (comme une
   languette), les informations en horizontal et centrees a l affiche ».
   ⚠️⚠️ C EST UN RENVERSEMENT DE LA V454, ET IL FAUT LE DIRE EN ENTIER. Elle
   avait pose une colonne de 24 px SUR l affiche, et elle avait deux arguments
   qui tombent tous les deux ici :
     - « elle ne coute pas un pixel de hauteur » : vrai, mais elle en coutait
       24 de LARGEUR a chaque affiche, sur toute sa hauteur ;
     - « elle ne clique rien, une cible posee sur l affiche se touche sans le
       vouloir » : vrai TANT QU ELLE EST DESSUS. Sous l image, elle est a cote
       du bouton et non par-dessus - l argument tombe avec la position, et les
       deux notes peuvent enfin etre les boutons que le retour demande.
   ⚠️ ELLE EST COLLEE, PAS POSEE A COTE : aucune marge entre l image et elle,
   et le meme rayon (zero, V479). Un ecart de 2 px en aurait fait un second
   objet ; c est la meme etiquette que l affiche porte.
   ⚠️ SA HAUTEUR EST UN JETON : le voile d angle du « i » la lit pour ne pas
   deborder dessus. Une valeur recopiee serait une variable manquante. */
/* ⚠️⚠️ V485 - MOINS OPAQUE, MOINS LARGE, CENTREE (retour de l auteur). La
   V480 l avait posee pleine largeur et pleine encre : elle se lisait comme un
   SECOND objet sous l affiche, presque aussi present qu elle. Elle se serre
   maintenant sur ce qu elle porte et se centre dessous, sur un voile.
   ⚠️ `width: fit-content` + `margin-inline: auto` : la largeur vient du
   CONTENU, jamais d un nombre. Une note qui apparait l elargit d elle-meme,
   et elle reste centree sans qu une seule valeur soit tenue a la main.
   ⚠️ ELLE RESTE COLLEE : aucune marge entre l image et elle. C est la
   languette d une etiquette, pas une legende posee plus bas.
   ⚠️ `position: relative` NE PORTE PLUS QUE LE `z-index` DEPUIS LA V489 : le
   panneau de legende a quitte la languette pour `document.body`, mais un
   `z-index` sur une boite statique ne veut rien dire - la languette doit donc
   rester positionnee pour passer devant l affiche. */
.aff-ruban {
  position: relative; z-index: 4;
  display: flex; flex-direction: row; align-items: center; justify-content: center;
  /* ⚠️⚠️ V498 - PLUS AUCUN ECART NI AUCUNE RESERVE (retour de l auteur : « la
     languette doit etre ajustee automatiquement mais surtout reduire
     l espacement entre les elements et l encadrement »). Mesure avant : 112,5
     px de large sous une affiche de 138, dont 11,2 px d air pur - deux ecarts
     de 2,4 et deux reserves de 3,2. Ce sont les trois boutons qui portent leur
     propre respiration, dans leur padding ; l en donner une seconde ici, c est
     l ecrire deux fois. */
  gap: 0;
  width: fit-content; max-width: 100%; margin-inline: auto;
  min-height: var(--languette-h); padding: 0;
  background: color-mix(in srgb, var(--ink-2) 62%, transparent);
  border-radius: 0 0 var(--r-2) var(--r-2);
}
/* ⚠️⚠️ V485 - LE FANION EST DESORMAIS DANS UN BOUTON, ET C EST UN
   RENVERSEMENT DE LA V480 (qui ecrivait « le fanion ne clique rien : c est un
   signe, pas une commande »). Le retour de l auteur en fait une commande :
   « le statut devient un bouton aussi, il affiche la legende a la place du
   (i) ». Le signe reste inerte DANS son bouton - c est le bouton qui prend le
   clic, pas le dessin. */
.aff-ruban .status-fanion { width: 15px; height: 15px; flex: 0 0 auto; pointer-events: none; }
.aff-statut {
  appearance: none; border: 0; background: none; margin: 0; padding: 0 .15rem;
  display: flex; align-items: center; justify-content: center;
  min-height: var(--languette-h); cursor: pointer;   /* V498 */
}
.aff-statut:hover { background: var(--ink-3); }
.aff-statut:focus-visible { outline: 2px solid var(--phase-accent); outline-offset: -2px; }

/* ⚠️⚠️ V485 - ALLUME / ETEINT (retour de l auteur : « le statut actif sur le
   film doit etre comme allumé et les deux autres comme éteints, ca permettra
   de donner l information directement »). La legende cesse d etre un
   dictionnaire et devient une REPONSE : on l ouvre depuis une affiche, elle
   designe le statut de CE titre.
   ⚠️ OUVERTE DEPUIS LE « i » D UNE SAGA, ELLE NE DESIGNE RIEN : aucune des
   deux classes n est posee, et les trois lignes se lisent a egalite. C est la
   meme liste, avec ou sans sujet.
   ⚠️ L ETEINT NE DISPARAIT PAS - il doit rester LISIBLE, sinon la legende ne
   remplit plus son premier role, qui est d expliquer les trois mots. */
.legende-item--eteint { opacity: .38; }
.legende-item--allume .legende-mot { color: var(--paper); }
.legende-item--allume .status-fanion { transform: scale(1.18); }
.aff-notes { display: contents; }
/* ⚠️⚠️ V474 - LES DEUX NOTES DEVIENNENT ATTRAYANTES (retour de l auteur) :
   .62 -> .78 rem et un gras franc. Elles etaient a la limite du lisible sur
   une affiche de 150 px, et une note qu on doit approcher pour lire ne sert
   qu a encombrer.
   ⚠️ V475 - ET ELLES REDESCENDENT A .68 rem (retour de l auteur : « reduit la
   taille de police des notes »). .62 etait illisible, .78 mangeait l affiche :
   ce cran-ci se lit sans s approcher et laisse la bande sous les 30 px. */
/* ⚠️ V480 - LES DEUX NOTES SONT DES BOUTONS. Elles remplissent la hauteur de
   la languette : la cible utile est celle qu on touche, pas celle qu on voit,
   et un chiffre de 10 px dans une bande de 30 en laisserait 20 inertes.
   ⚠️ L OMBRE PORTEE EST PARTIE AVEC LE FOND : elle servait a detacher un
   chiffre blanc pose SUR une affiche. Sur un aplat d encre, elle ne fait que
   salir le trait. */
.aff-note {
  appearance: none; border: 0; background: none; padding: 0 .16rem; margin: 0;
  display: flex; align-items: center; justify-content: center; gap: 2px;
  min-height: var(--languette-h); cursor: pointer;   /* V498 */
}
/* ⚠️⚠️ V488 - LE CORPS DES DEUX NOTES EST ECRIT ICI, SCOPE, ET C EST LA
   CORRECTION D UNE PANNE MESUREE, PAS UN REGLAGE (retour de l auteur : « la
   taille des polices de notes sur le bandeau doit etre egale pour les deux
   notes »). Elles N ETAIENT PAS egales : mesure a 1440 px, la communautaire
   sortait en 16 px / graisse 400 / chasse 100 % / chiffres proportionnels,
   la mienne en 11,52 px / 800 / 108 % / tabulaires.
   ⚠️⚠️ LA CAUSE EST L ORDRE DE SOURCE, LE PIEGE Nº 2 A L IDENTIQUE. La V485 a
   donne a la note communautaire la classe `.score--community` pour qu elle
   ouvre le menu Snapline + TMDB - et cette classe declare `font: inherit`,
   mille cent lignes PLUS BAS dans la feuille, a la meme specificite (0,1,0).
   A egalite, c est l ordre de source qui tranche : `font` etant une
   abreviation, elle remettait d un coup la famille, le corps, la graisse, la
   chasse ET les chiffres du parent. La note communautaire ne portait donc
   aucune des cinq lignes ecrites pour elle, en silence.
   « Une regle qui ne gagne que par ordre de source ne gagne pas, elle
   attend » (V45, V52, V59) : on la fait gagner par la SPECIFICITE, jamais en
   deplacant le bloc - un deplacement se defait au prochain ajout, et il aurait
   fallu le refaire pour chaque nouvelle classe empruntee a la fiche.
   ⚠️ UN CRAN PLUS BAS, ET SUR L ECHELLE (« et meme reduit de 1 ou 2 taille ») :
   `.72rem` etait une valeur recopiee a la main, entre deux crans. Elle devient
   `--fs-micro`, le plancher d etiquette de la charte (V230) - deux crans plus
   bas que ce qui etait ecrit, et on ne descend pas sous le plancher : ces
   chiffres doivent rester lisibles sur une affiche. */
.aff-ruban .aff-note {
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 800;
  font-size: var(--fs-note-carte); line-height: 1; font-variant-numeric: tabular-nums;   /* V497 */
}
/* ⚠️⚠️ V498 - LES TROIS RESERVES DE LARGEUR PARTENT, ET LA CIBLE TACTILE
   REVIENT PAR UN CALQUE. `min-width: 2.2rem` (35 px) et `min-width: 1.7rem`
   etaient la RESERVE que le retour demande de retirer : c est elle qui
   ecartait les chiffres les uns des autres, bien plus que les ecarts. Les
   boutons se serrent donc sur leur contenu, et un `::after` en debord leur
   rend une cible - le geste de `.watch-skip::after`, repris tel quel.
   ⚠️⚠️ ET LE PRIX EST NOMME, PARCE QUE C EST UN RENVERSEMENT DE REGLE :
   PRODUCT.md demande 34 px de cible, la V480 citait ce plancher pour justifier
   ces 35 px de large. Apres cette version la cible fait bien 34 px de HAUT
   (30 declares + 2 de debord de chaque cote), mais le bouton de statut tombe
   a environ 24 px de LARGE. Le plancher etait DEJA manque en hauteur depuis la
   V480 - une languette de 30 px - donc ce qui change n est pas qu on le
   franchit, c est de quel cote. L auteur l a demande en toutes lettres ; une
   ligne suffit a revenir en arriere.
   ⚠️ LE DEBORD NE MONTE QUE DE 2 px SUR L AFFICHE, et la languette passe
   devant elle (`z-index: 4`) : deux pixels du bas de l image ouvrent donc la
   note au lieu de la fiche. C est le meme echange que le skip fait deja sur
   la carte, a la meme profondeur. */
.aff-note, .aff-statut { position: relative; }
.aff-note::after, .aff-statut::after { content: ""; position: absolute; inset: -2px; }
.aff-note:hover { background: var(--ink-3); }
.aff-note:focus-visible { outline: 2px solid var(--phase-accent); outline-offset: -2px; }
/* ⚠️⚠️ V485 - LE CHIFFRE EST BLANC DANS LES DEUX CAS, L ETOILE PORTE LA
   DISTINCTION (retour de l auteur : « etoile blanche communautaire et etoile a
   la couleur du profil pour la note perso - et la note elle reste blanche »).
   ⚠️⚠️ ET C EST CE QUI REND L ETOILE PAYABLE, apres l avoir retiree en V475.
   Elle etait partie parce qu elle « redisait ce que la couleur dit deja » : a
   l epoque le CHIFFRE portait la couleur, donc l etoile etait un second
   signal pour une seule information. Les deux chiffres etant desormais de la
   meme encre, l etoile est le SEUL signal - elle ne redit plus rien. */
.aff-note { color: var(--paper); }
.aff-com .star-icon { color: var(--paper); }
/* ⚠️ `--star-mine` vaut l or tant qu aucun profil ne l a changee - aucun
   litteral ici (piege nº 6). */
.aff-moi .star-icon { color: var(--star-mine); }
/* ⚠️⚠️ V497 - L ETOILE SE CONSTRUIT SUR LA HAUTEUR DES CHIFFRES, elle n est
   plus une taille en pixels (retour de l auteur : « les etoiles doivent etre
   centrees a la note »). `1cap` EST la hauteur des capitales de la police
   courante - donc celle d un chiffre - et diviser par la part que le dessin
   occupe dans son cadre donne un cadre dont L ENCRE mesure exactement `1cap`.
   ⚠️ MESURE AVANT : l etoile de la languette faisait 7,57 px d encre pour des
   chiffres de 8 px, celle du bandeau 10,6 px pour des chiffres de 9,4 -
   l une trop courte, l autre trop haute, et les deux ecrites a la main
   (10 px, 14 px). Deux litteraux pour une seule question.
   ⚠️ LE REPLI EST SUR LA LIGNE D AU-DESSUS et il ne sert qu aux navigateurs
   sans `cap` : 0,688 cap par em, mesure sur la police d affichage, divise par
   la meme part - soit 0,91 em. Une propriete declaree deux fois laisse la
   premiere aux navigateurs qui ne comprennent pas la seconde. */
.aff-note .star-icon,
.band-note .star-icon {
  height: .91em;
  height: calc(1cap / var(--etoile-cadre));
  width: auto; aspect-ratio: 1; flex: 0 0 auto; fill: currentColor;
}

/* Voiles d'angle - V480 : sur le CADRE, donc sur l image seule. */
.poster-cadre::before, .poster-cadre::after {
  content: ""; position: absolute; z-index: 2;
  width: 52px; height: 52px; pointer-events: none;
  border-radius: var(--r-affiche);
}
.poster-cadre::before {
  top: 1px; right: 1px;
  background: radial-gradient(circle at 100% 0%, rgba(0, 0, 0, .62), transparent 72%);
}
.poster-cadre::after {
  bottom: 1px; left: 1px;
  background: radial-gradient(circle at 0% 100%, rgba(0, 0, 0, .62), transparent 72%);
}

/* Coeur : contour BLANC tant qu'il est inactif (un contour rose se lit comme
   un decor, un contour blanc se lit comme un bouton), rose plein une fois
   active. */
.fav-btn svg path {
  fill: none; stroke: #FFFFFF; stroke-width: 1.7px;
  transition: fill .16s;
}
.fav-btn:hover svg path { stroke: var(--fav); }
.card.is-favorite .fav-btn svg path { fill: var(--fav); stroke: var(--fav); }

/* Sur desktop le coeur n'apparait qu'au survol - sauf s'il est actif.
   Sur tactile (pas de survol fin) il reste toujours visible. */
@media (hover: hover) and (pointer: fine) {
  .fav-btn { opacity: 0; }
  .card:hover .fav-btn,
  .fav-btn:focus-visible,
  .card.is-favorite .fav-btn { opacity: 1; }
}
.info-btn:hover { color: var(--phase-accent-soft); }
.fav-btn:focus-visible, .info-btn:focus-visible {
  outline: 2px solid var(--phase-accent); outline-offset: -2px; border-radius: var(--r-2);
}

/* ------------------- Bandeau de lecture rapide -------------------------- */
/* V373 - vue duo seule : [ma note] [sa note]. Hors duo, le bandeau reste vide. */

/* Le bandeau lui-meme est INVISIBLE : ni fond, ni bordure. Seule sa
   structure demeure, collee sous l'affiche. */
.card-band {
  width: 100%;
  margin-top: .22rem;
  display: flex; align-items: center; justify-content: center; gap: .7rem;
  padding: 0;
  font-size: var(--fs-note-carte); font-weight: 700;   /* V497 */
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.card-band:empty { margin-top: 0; }

.band-note {
  appearance: none; border: 0; background: transparent; padding: 0;
  display: inline-flex; align-items: center; gap: .22rem;
  color: var(--text-dim);
  font: inherit; letter-spacing: 0;
}


/* Note personnelle : etoile a la COULEUR DU PROFIL (V63), CREUSE tant qu'on
   n'a pas note, pleine une fois la note posee.
   ⚠️ Ne PAS mettre `stroke` dans la transition (cf. le coeur) : une transition
   sur `stroke` fige la valeur calculee et l'etat actif ne s'applique jamais. */
.band-mine { cursor: pointer; }
.band-mine .star-icon path {
  fill: none; stroke: var(--star-mine); stroke-width: 1.7px; transition: fill .16s;
}
.band-mine.is-rated { color: var(--paper); }
.band-mine.is-rated .star-icon path { fill: var(--star-mine); }
.band-mine:hover .star-icon path { fill: color-mix(in srgb, var(--star-mine) 40%, transparent); }
.band-mine:focus-visible { outline: 2px solid var(--phase-accent); outline-offset: 2px; border-radius: var(--r-1); }

/* ⚠️⚠️ V132 (C2b) - L ETOILE DU DUO, TROISIEME ET DERNIERE DU BANDEAU.
   Elle prend la couleur du profil DU PARTENAIRE (`--star-duo`, posee sur la
   racine par `poserNotesDuo`), et c est le point 2 du retour du 1er septembre
   2026 : « la couleur de chacun est celle de son profil ». Ce n est donc PAS
   la couleur du duo (V131), qui appartient a la relation et peint la barre.

   ⚠️ ELLE EST TOUJOURS PLEINE, contrairement a la mienne : une etoile creuse
   veut dire « pas encore notee, cliquez » - un appel a l action. Ici il n y a
   rien a cliquer, et la case n existe que s il y a une note.

   ⚠️ ET ELLE N A NI CURSEUR, NI SURVOL, NI ANNEAU DE FOCUS : c est un
   `<span>`, pas un bouton. Ce qui montre quelqu un d autre ne s ecrit pas
   (regle `card--ami`, V88). */
/* ⚠ V224 - LE BOUTON DU GROUPE, route solo : l embleme seul, a la couleur
   du groupe. Il emprunte le reset de `.band-note` ; curseur, survol par
   l opacite, anneau de focus. */
.band-grp { cursor: pointer; transition: opacity .14s; }
.band-grp:hover { opacity: .72; }
.band-grp:focus-visible { outline: 2px solid var(--phase-accent); outline-offset: 2px; border-radius: var(--r-1); }
.band-grp .profile-avatar svg { width: 100%; height: 100%; display: block; }

/* V371 - l etoile du duo est la meme sans note : vide et cernee, comme la mienne */
.band-duo { color: var(--text-dim); }
.band-duo .star-icon path { fill: none; stroke: var(--star-duo); stroke-width: 1.7px; }
.band-duo.is-rated { color: var(--paper); }
.band-duo.is-rated .star-icon path { fill: var(--star-duo); }

/* ⚠️⚠️ V159 (5c) - EN ROUTE DUO, LA CARTE MONTRE LES DEUX NOTES ET PLUS LA
   MOYENNE. Le retour le demande en ces termes ; la raison est que la question
   change de sujet. Sur la route partagee, ce qu on compare n est plus « ce
   film, et le site » mais « ce film, lui et moi » - et une troisieme valeur
   collective dans 122 px pousse les deux qui comptent hors de la ligne.

   ⚠️ LA MOYENNE N EST PAS PERDUE, ELLE EST AILLEURS : la fiche garde ses
   QUATRE cases (TMDB, communaute, ma note, la sienne) et l invariant de la
   V22 les tient sur une ligne. C est la que la comparaison complete vit -
   la carte, elle, se lit d un coup d oeil.

   ⚠️⚠️ ET ELLE N EST PAS MASQUEE, ELLE N EST PAS CONSTRUITE. Une regle
   `:root.route-duo .band-avg { display: none }` a ete ecrite puis retiree :
   elle pesait (0,3,0), exactement comme `.card-band.is-nomme .band-note`
   ci-dessous, donc elle ne gagnait QUE par ordre de source - et elle le
   perdait. C est le piege nº 2 du projet, mot pour mot : « une regle qui ne
   gagne que par ordre de source ne gagne pas, elle attend ». La reponse
   n etait pas un `!important` de plus (le site n en a qu UN, sur `[hidden]`)
   mais le geste que la V132 avait deja choisi pour l etoile du partenaire :
   `refaireBandeaux()` POSE ET RETIRE le noeud. Un noeud construit puis
   masque est un noeud qui a existe (V56, V88). */

/* ⚠️⚠️ LE NOM SE POSE SOUS L ETOILE PAR UNE GRILLE NOMMEE, ET LE DOM NE BOUGE
   PAS. C est le geste de la carte de destination (V49) et de la vue colonnes
   (V45) : l etoile et la valeur gardent leur place dans le document, la
   feuille les range sur deux rangs. Envelopper le couple dans un noeud de
   plus aurait ete un enfant a declarer dans les deux autres vues.
   ⚠️ ET LE BANDEAU RESTE L UN DES CINQ ENFANTS DE LA CARTE : il grandit d une
   ligne, il ne se dedouble pas - le piege du sixieme enfant (V84) ne se pose
   pas ici. */
/* ⚠️⚠️ LA LARGEUR DU NOM VIENT DE L AFFICHE, PAS D UN NOMBRE. Premiere
   ecriture : `max-width: 9ch` sur le libelle, comme `.score-src` (V130). Elle
   est FAUSSE ici, et la mesure l a dit - un enfant de grille qui enjambe les
   deux colonnes ne les elargit pas quand il porte `min-width: 0`, donc le nom
   se rognait a la largeur d ETOILE + VALEUR : 36 px, « Lea la Lucide » coupe a
   « Lea l… » sur une carte de 152.
   La case prend donc LA MOITIE du bandeau, qui fait lui-meme 100 % de la
   carte, donc `--poster-w`. Toute l echelle en derive deja (V45) : il n y a
   pas une seconde valeur a tenir d accord, et le nom s allonge tout seul le
   jour ou l affiche grandit. C est le geste de `--bar-tip` et de `--orb`
   (V77) - une variable, des calculs, jamais des litteraux jumeaux. */
/* ⚠️⚠️ C EST UN `flex-wrap`, PAS UNE GRILLE, ET LA MESURE A TRANCHE. Premiere
   ecriture : une grille nommee « s v » / « q q », comme la carte de
   destination (V49). Elle est FAUSSE ici - un enfant qui enjambe deux colonnes
   `auto` n en recoit que la largeur du CONTENU de ces colonnes : le nom se
   rognait a 36 px (etoile + valeur) dans une case qui en faisait bien 72.
   Mesure faite avant de conclure, comme toujours dans ce projet.
   En flex, le libelle prend `flex: 0 0 100%` : il passe a la ligne et occupe
   la case ENTIERE. Le DOM ne bouge toujours pas - c est ce qui compte, un
   noeud enveloppant aurait ete un enfant a declarer dans les deux autres
   vues (V45). */
.card-band.is-nomme { gap: .5rem; }
.card-band.is-nomme .band-note {
  flex-wrap: wrap;
  justify-content: center;
  gap: 0 .22rem;
  width: calc((100% - .5rem) / 2);
  min-width: 0;
}

/* ⚠️ LA BORNE EST EN `ch`, COMME CELLE DE `.score-src` (V130), ET POUR LA
   MEME RAISON : un pseudo va jusqu a 36 signes et un surnom jusqu a 24, dans
   une carte qui fait 122 px au plus etroit. Ce qui cede est le LIBELLE
   (ellipse), jamais la valeur - la regle de la quatrieme case de la fiche
   (V132), transposee.
   ⚠️ ET `min-width: 0` N EST PAS DECORATIF : un enfant de grille porte
   `min-width: auto`, donc il refuse de descendre sous la largeur de son
   contenu et l ellipse ne se declenche jamais. Deja paye en V85 et en V132. */
.card-band.is-nomme .band-qui {
  flex: 0 0 100%;
  text-align: center;
  min-width: 0; max-width: 100%;
  margin-top: .1rem;
  font-size: var(--fs-micro); font-weight: 600; letter-spacing: .01em;
  color: var(--text-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* ⚠️ CHACUN SOUS SA COULEUR, comme `.score--mine .score-src` suit son etoile
   depuis la V63 : c est ce qui dit LEQUEL des deux noms va avec LAQUELLE des
   deux etoiles, sans une ligne de plus. */
.card-band.is-nomme .band-mine .band-qui { color: var(--star-mine); }
.card-band.is-nomme .band-duo .band-qui { color: var(--star-duo); }

/* ⚠️ V116 - « .band-provider » EST PARTIE AVEC SON PORTEUR. Le lien plateforme
   a quitte la carte en V11 (« il ne reste que dans la fiche ») ; sa mise en
   forme est restee cent cinq versions. Le logotype officiel sur pastille
   claire vit toujours dans la fiche, sous « .link-chip ». */

/* Titre : BLANC, decale vers le bas proportionnellement a l'affiche, et
   centre dans une boite de DEUX lignes. Un titre d'une seule ligne est donc
   centre sur la meme boite qu'un titre de deux lignes : toutes les cartes
   restent alignees entre elles.
   Chaque ligne est en `nowrap` et app.js reduit au besoin le corps du titre
   (fitTitles) : "CAPTAIN AMERICA" ne peut donc jamais se couper en deux. */
/* V331 - Visuel_B : le titre de film passe en CASSE NORMALE, graisse 700 a
   chasse normale - les capitales appartiennent desormais aux ROUTES. */
.card-title {
  text-transform: none; letter-spacing: -.005em;
  font-weight: 700; font-stretch: var(--ch-titre);
  margin: .5rem 0 0;                   /* V373 - un peu d air sous les notes */
  font-size: var(--title-fs); line-height: 1.16;
  color: var(--paper);                 /* titre en blanc */
  /* Hauteur calee sur le corps de BASE : elle ne bouge pas quand fitTitles
     reduit le corps d'un titre trop long. 2 lignes reservees pour tous. */
  height: calc(var(--title-fs) * 2.6);
  /* V371 - CALE EN HAUT, JAMAIS CENTRE : un titre d une ligne commence a la
     meme hauteur qu un titre de deux (regle de l auteur). */
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  gap: .04em;
}
/* ⚠️⚠️ V481 - SUR LA PAGE PRINCIPALE, L AFFICHE PARLE SEULE (retour de
   l auteur). Le titre et l annee ne sont pas masques par un `hidden` pose en
   JS : c est UNE SEULE REGLE, lue en negatif du reglage, donc rien a tenir
   d accord entre la feuille et le moteur.
   ⚠️ LA RESERVE DE DEUX LIGNES PART AVEC EUX, et c est la moitie de
   l interet : `.card-title` reserve `calc(var(--title-fs) * 2.6)` pour que
   les affiches restent alignees quel que soit le titre. Cache, il ne reserve
   plus rien - la rangee se resserre d autant.
   ⚠️ NI EN LISTE NI EN COLONNES : l affiche y fait 42 et 34 px, le titre est
   la seule chose qui identifie la ligne. */
:root:not(.reg-titres) #roadmap:not(.is-list):not(.is-cols) .card-title,
:root:not(.reg-titres) #roadmap:not(.is-list):not(.is-cols) .card-year { display: none; }
.card-title span { display: block; white-space: nowrap; }
/* ⚠️ LE SOUS-TITRE SE DISTINGUE PAR LE STYLE, PAS PAR DES TIRETS (V33).
   « — Le Monde des tenebres — » ajoutait deux signes qui ne disent rien, et
   deux signes de plus a faire tenir dans 122 px. L'italique dit la meme chose
   et ne coute pas un pixel. */
/* ⚠️ V297 - « max() » : a .8em d un titre reduit par fitTitles, le sous-titre
   tombait a 8,5 px (838 px de fenetre), le plus petit texte du site. Il garde
   son rapport tant que le titre est grand, et s arrete au plancher d etiquette. */
.card-title .title-sub {
  font-size: max(.8em, var(--fs-micro)); font-weight: 600; font-style: italic;
  letter-spacing: .005em; color: var(--text-dim);
}
.modal-title .title-sub {
  font-weight: 600; font-style: italic; color: var(--text-dim);
  font-size: .78em;
}

/* Annee : juste sous le titre, PLUS PETITE et plus sourde. C'est le
   contraste d'echelle qui fait ressortir le titre, pas un ecart vertical. */
.card-year {
  margin: .12rem 0 0; height: 1.25em;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-xs); font-weight: 500; font-stretch: var(--ch-etiquette); letter-spacing: .14em;
  color: var(--text-dim); font-variant-numeric: tabular-nums;
}

/* ==================== V360 - LA RANGEE DE DEUX BOUTONS ====================
   *Vu* et *mis de cote*, cote a cote ; des qu on choisit, l autre disparait
   (`hidden`, pose par `syncWatchedUI`).
   ⚠️⚠️ LA RANGEE SE CENTRE ET RETRECIT, elle ne reserve pas un creneau
   vide. Ce qui tient cette rangee de cartes est l alignement VERTICAL des
   pastilles d une meme ligne, et il vient de la HAUTEUR des blocs de la carte
   - pas de leur largeur. Un creneau vide reserve aurait decale la pastille
   d un demi-bouton vers la gauche pour ne rien montrer.
   ⚠️ LA MARGE A REMONTE DU BOUTON VERS LA RANGEE : deux boutons qui la
   porteraient chacun, ce serait la meme valeur a deux endroits (piege nº 6)
   - et `.card--dest` la redefinit, donc elle aurait diverge la-bas en
   premier. */
.watch-row {
  display: flex; align-items: center; justify-content: center;
  gap: .34rem;
  margin-top: .4rem;                   /* directement sous l'annee */
}

/* Pastille "vu" : cercle vide exterieur + un seul disque interieur.
   Le disque est pose en `inset` : il est concentrique PAR CONSTRUCTION,
   sans aucun calcul de demi-taille. Aucune transformation au survol :
   la pastille ne bouge jamais. */
.watch-dot {
  appearance: none; cursor: pointer; padding: 0;
  flex: 0 0 auto;
  margin-bottom: 0;
  position: relative; display: grid; place-items: center;
  width: 30px; height: 30px;
  border-radius: 50%;
  border: 0;
  background: transparent;
  color: var(--ink-7);
  transition: color .2s, filter .2s;
}
/* ⚠️⚠️ V411 - LES TROIS INDICATEURS PERDENT LEUR CADRE (demande de l auteur) :
   une coche pour Vu, une croix rouge pour Skip, une petite fleche jaune pour
   A revoir. Chacun a SA teinte - la route pour Vu, le rouge de refus pour
   Skip, le jaune de `.revoir-chip` pour A revoir - sourde au repos, pleine
   quand l etat est pose. Les cibles ne bougent pas (30 px, 34 au doigt par
   `::after`) : seul le dessin change. */
.watch-dot svg {
  width: 22px; height: 22px; display: block;
  transform: scale(.86);
  transition: transform .18s cubic-bezier(.34, 1.56, .64, 1);
}
.card.is-watched .watch-dot svg { transform: scale(1); }
/* ⚠️ V336 (S9, point [44]) - LA SEULE MICRO-ANIMATION DE CONFIRMATION DU SITE,
   et elle est sur LE geste du site : cocher. Le disque se remplit deja ; il
   part maintenant d un peu plus petit et rattrape sa taille, ce qui se lit
   comme « c est pris ». ⚠️ Une TRANSFORMATION, jamais une largeur ou une
   ombre : elle est composee, elle ne refait pas la mise en page, et la pastille
   ne bouge pas d un pixel (regle de la V29 - elle ne bouge jamais au survol).
   ⚠️ `prefers-reduced-motion` et « Animations coupees » la neutralisent par la
   regle globale : aucune seconde liste a tenir. */
.watch-dot:hover { color: var(--card-accent, var(--phase-accent)); }
.watch-dot:focus-visible { outline: 2px solid var(--phase-accent); outline-offset: 3px; }

/* ====================== LES CIBLES AU DOIGT (V227) ======================
   La critique du 11 septembre 2026 a MESURE ce que la regle des 34 px
   (`--pastille`) promettait sans le tenir : la pastille « vu » faisait 30 px,
   les deux etoiles du bandeau 14 px de haut. Le geste principal du site - cocher
   depuis le canape - visait donc une cible sous le plancher.
   ⚠️⚠️ ON AGRANDIT LA CIBLE, JAMAIS LE DESSIN, et c est le geste du numero de
   favori (V153) : un pseudo-element transparent, en `position: absolute`,
   qui ne pese RIEN dans la mise en page. Les pastilles restent alignees par
   construction, le bandeau garde sa hauteur, `--head-h` ne bouge pas.
   ⚠️ CHAQUE ZONE EST TAILLEE SUR SES VOISINS MESURES, pas sur un chiffre rond :
   - la pastille : 2 px de chaque cote, 30 -> 34 ; autour d elle il n y a que
     l annee et le vide de la rangee ;
   - les etoiles : 3 px vers le HAUT seulement - au-dessus c est l AFFICHE, qui
     est un bouton (elle ouvre la fiche), et une cible qui mord sur un autre
     bouton lui vole ses clics - et 17 px vers le BAS, sur le titre, qui ne se
     clique pas. Sur les cotes, la MOITIE de la gouttiere du bandeau (11,2 px) :
     deux voisines qui s etendraient chacune de 10 px se chevaucheraient.
     Resultat : 34 px de haut, 24 a 34 de large - le minimum WCAG 2.5.8 (24 px)
     partout, le plancher du site en hauteur. */
.watch-dot::after {
  /* Cale sur la boite de REMPLISSAGE (26 px, dans la bordure de 2 px) :
     26 + 2 x 4 = 34, soit 2 px au-dela du cercle dessine. */
  content: ""; position: absolute; inset: -4px;
  border-radius: 50%;
}

/* ======================== V360 - METTRE DE COTE ===========================
   ⚠️ MEME GABARIT QUE LA PASTILLE - 30 px, bordure de 2, cible de 34 par le
   meme `::after` : deux boutons cote a cote qui ne feraient pas la meme taille
   seraient une irregularite qu on lit sans savoir la nommer (V197).
   ⚠️⚠️ LE ROUGE EST `--brand`, PAS UNE SEPTIEME FAMILLE DE COULEUR. C est
   le rouge GENERAL du site - celui du `−` d un tiroir ouvert, des croix de
   refus (V212), de l etat *en retard*. Mettre de cote est un REFUS, et la
   famille qui dit refus existe deja.
   ⚠️ ET CE N EST PAS LA TEINTE DE LA ROUTE : `--card-accent` dit *cette
   route*, or un refus ne se decline pas par route - la croix est la meme
   partout, comme le signe qu elle emprunte.
   ⚠️ LE TRACE VIENT DE `MCU_PROFILE.signe("croix")` : aucun dessin neuf. */
/* ⚠️ V371 - UNE CROIX SEULE, SANS CERCLE (demande de l auteur) : la bordure
   part, le gabarit de 30 px reste pour la cible et l alignement. */
.watch-skip {
  appearance: none; cursor: pointer; padding: 0;
  flex: 0 0 auto;
  position: relative; display: grid; place-items: center;
  width: 30px; height: 30px;
  border-radius: 50%;
  border: 0;
  background: transparent;
  color: color-mix(in srgb, var(--brand) 55%, var(--ink-7));   /* V411 - rouge sourd au repos */
  transition: color .2s;
}
.watch-skip svg { width: 22px; height: 22px; display: block; }
.watch-skip::after {
  content: ""; position: absolute; inset: -4px;
  border-radius: 50%;
}
.watch-skip:hover { color: var(--brand); }

/* ===================== V361 - « A REVOIR » SUR LA CARTE =====================
   Le creneau libere par le skip : une fleche circulaire, visible seulement
   quand le titre est vu (ou deja marque) - la regle de la fiche depuis la
   V250. La rangee ne porte donc JAMAIS plus de deux boutons.
   ⚠️ MEME GABARIT QUE SES DEUX VOISINS (30 px, cible de 34 par le meme
   `::after`) : trois boutons qui ne feraient pas la meme taille seraient une
   irregularite qu on lit sans savoir la nommer.
   ⚠️⚠️ LE JAUNE EST `--tier-reco`, ET CE N EST PAS UNE COULEUR NEUVE : c est
   la teinte que le site emploie deja pour l etiquette `.revoir-chip` du meme
   objet, sur la meme carte. Deux teintes pour une seule chose se liraient
   comme deux choses. */
.watch-revoir {
  appearance: none; cursor: pointer; padding: 0;
  flex: 0 0 auto;
  position: relative; display: grid; place-items: center;
  width: 30px; height: 30px;
  border-radius: 50%;
  border: 0;
  background: transparent;
  color: color-mix(in srgb, var(--tier-reco) 55%, var(--ink-7));   /* V411 - jaune sourd, sans cercle */
  transition: color .2s;
}
.watch-revoir svg { width: 18px; height: 18px; display: block; }
.watch-revoir::after { content: ""; position: absolute; inset: -4px; border-radius: 50%; }
.watch-revoir:hover { color: var(--tier-reco); }
.watch-revoir:focus-visible { outline: 2px solid var(--phase-accent); outline-offset: 3px; }
.watch-revoir[aria-pressed="true"] { color: var(--tier-reco); }
.watch-skip:focus-visible { outline: 2px solid var(--phase-accent); outline-offset: 3px; }
.card.is-skip .watch-skip { color: var(--brand); }
/* ⚠️ UN TITRE MIS DE COTE SE LIT COMME MIS DE COTE, ET C EST TOUT CE QU IL
   FAUT : l affiche reste en gris (elle n est pas vue), le titre s eteint d un
   cran. Pas d etiquette sur l affiche - la pile `.card-flags` dit le STATUT
   (V34), pas ce que j en fais ; y melanger les deux serait deux vocabulaires
   au meme endroit. */
.card.is-skip .card-title { color: var(--text-dim); }
/* ⚠️ `button.` ET PAS `.band-note` SEUL : l etoile du duo porte la classe
   mais c est un `<span>` (V132, elle se LIT) - sa zone ne recevrait aucun
   clic et mangerait celle de sa voisine, dont la gouttiere passe a .5rem en
   route duo. */
.card-band button.band-note { position: relative; }
.card-band button.band-note::after {
  content: ""; position: absolute;
  top: -3px; bottom: -17px; left: -5px; right: -5px;
}
/* ⚠️⚠️ V322 - LA BULLE DES ETOILES, AU SURVOL ET AU FOCUS (critique UX, P2).
   Elle remplace le `title` natif : une seconde avant de paraitre, et rien au
   clavier. ⚠️ `::before`, JAMAIS `::after`, qui porte la cible au doigt (V227).
   ⚠️ Elle se pose AU-DESSUS de l etoile, donc sur le bas de l affiche : dans
   la carte, jamais sur la voisine. ⚠️ Ses mots restent COURTS (« Moyenne :
   7,6 ») : une rangee defile en `overflow`, et une bulle plus large que la
   carte y serait rognee au bord. Le detail vit dans le menu des notes.
   ⚠️ L etoile reste seule au repos : la decision de la V207 tient. */
.card-band button.band-note[data-bulle]::before {
  content: attr(data-bulle);
  position: absolute; bottom: calc(100% + .3rem); left: 50%; transform: translateX(-50%);
  z-index: 2; padding: .15rem .4rem; white-space: nowrap; pointer-events: none;
  background: var(--ink-1); color: var(--text); border: var(--frame-dim); border-radius: var(--r-1);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700; font-size: var(--fs-xs);
  opacity: 0; visibility: hidden; transition: opacity .12s;
}
.card-band button.band-note[data-bulle]:is(:hover, :focus-visible)::before { opacity: 1; visibility: visible; }

/* ⚠️ V221 - `.card-salle` ET `.salle-dot` SONT PARTIES AVEC LA LIGNE DE
   PASTILLES DE LA SALLE (V134), retiree sur demande. Une classe sans porteur
   part en entier (piege nº 4). */

.card.is-watched .watch-dot {
  color: var(--card-accent, var(--phase-accent));
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--card-accent, var(--phase-accent)) 60%, transparent));
}
/* PASTILLE-EMBLEME : sur un jalon, la pastille est l'embleme lui-meme, SANS
   cercle autour - deux formes rondes concentriques autour d'un signe deja
   rond, c'etait une decoration de trop. Meme gabarit de 30 px, donc meme
   alignement sur la rangee : la formule de position ne bouge pas. */
/* ⚠️⚠️ V369 - LE BOUTON D UN JALON GAGNE UNE SURFACE, ET C EST LA V360 QUI
   A CREE LE BESOIN. Jusque-la il etait SEUL sous son affiche : un embleme nu,
   sans cadre, et la regle de la V29 disait pourquoi - « deux formes rondes
   concentriques autour d un signe deja rond, c etait une decoration de trop ».
   Depuis la V360 il a un VOISIN : la croix « mettre de cote », dans un cercle
   de 30 px avec sa bordure. Deux boutons cote a cote dont l un s allume par un
   fond et l autre par rien, c est une irregularite qu on lit sans savoir la
   nommer (V197).
   ⚠️ LA REPONSE N EST PAS DE LUI RENDRE LE CERCLE : ce serait rouvrir la
   V29. C est un DISQUE DE SURFACE (`--ink-2`), pose au survol et au focus
   seulement - pas un trait, donc pas un second rond autour du signe. Au repos,
   l embleme reste nu : la regle tient.
   ⚠️⚠️ 24 px ET PLUS 26, ET C EST UNE MESURE OPTIQUE : ses deux voisins
   dessinent un cercle de 30 px dont le trait fait 2, donc leur signe utile
   tient dans 26. Un embleme de 26 qui touche les bords de sa boite se lit plus
   GROS qu un cercle de 30 - trois boutons alignes dont un pese plus lourd que
   les autres. La taille dessinee cede, jamais la cible : celle-ci reste a
   30 px, le gabarit de ses deux voisins (V29).
   ⚠️ LA LUEUR DU TITRE COCHE NE BOUGE PAS : DESIGN.md la compte parmi les
   trois effets expressifs que l auteur a gardes (V328, B4). */
.watch-dot--mark {
  border: 0; background: none;
  display: grid; place-items: center;
  width: 30px; height: 30px;
  border-radius: 50%;
  color: var(--ink-7);
  transition: color .2s, background-color .16s;
}
.watch-dot--mark svg { width: 24px; height: 24px; display: block; }
.watch-dot--mark:hover {
  border: 0;
  color: var(--card-accent, var(--phase-accent));
  background: var(--ink-2);
}
.card.is-watched .watch-dot--mark {
  border: 0;
  color: var(--card-accent, var(--phase-accent));
  filter: drop-shadow(0 0 8px color-mix(in srgb, var(--card-accent, var(--phase-accent)) 60%, transparent));
}


/* ------------------ Jalon : meme taille, mais encadre ------------------- */
/* Les marges negatives HAUT ET BAS annulent exactement le cadre : la carte
   interieure a donc la meme hauteur et le meme axe que les autres cartes,
   ce qui aligne l'affiche ET la pastille du jalon avec les autres. */
.milestone-frame {
  flex: 0 0 auto; align-self: stretch;
  display: flex; justify-content: center;
  padding: var(--mf-pad);
  border-radius: var(--r-3);
  margin-top: var(--mf-off);
  margin-bottom: var(--mf-off);
  /* ETEINT par defaut : le cadre est une RECOMPENSE, comme l'embleme. Tant que
     le jalon n'est pas coche il reste gris et sourd ; coche, il prend sa
     couleur pleine. Un cadre deja colore ne recompense rien - il decore. */
  border: 2px solid var(--ink-6);
  background: rgba(255, 255, 255, .015);
  transition: border-color .3s ease, background .3s ease, box-shadow .3s ease;
}
.milestone-frame.is-earned {
  border-color: var(--card-accent, var(--phase-accent));
  background: color-mix(in srgb, var(--card-accent, var(--phase-accent)) 10%, #000);
  box-shadow: 0 0 22px color-mix(in srgb, var(--card-accent, var(--phase-accent)) 18%, transparent);
}
/* SOUS-JALON : meme cadre, meme geometrie - donc meme alignement sur la
   rangee - mais un trait plus discret et pas de fond. Ce qui distingue le
   jalon final n'est ni sa taille ni sa place, c'est l'intensite du cadre.
   ATTENTION : le trait reste PLEIN. Le pointille est deja pris par les cartes
   de tiroir, ou il veut dire "optionnel" - un sous-jalon est un requis, il ne
   doit surtout pas emprunter ce signe. */
.milestone-frame--sub.is-earned {
  border-color: color-mix(in srgb, var(--phase-accent) 55%, transparent);
  /* Le cadre est HABITE, comme celui du jalon final : un cadre vide se lit
     comme une bordure decorative, un cadre teinte se lit comme une etape. */
  background: color-mix(in srgb, var(--card-accent, var(--phase-accent)) 8%, #000);
}

/* ============== LA CARTE DE DESTINATION (V49) ==========================
   « Plus imposantes, le titre et la date de sortie sur le cote droit de
   l'affiche. » Deux cartes la portent - Doomsday et Secret Wars - et elles
   occupent a elles deux la derniere ligne de la derniere route.

   ⚠️ C'EST LA MEME CARTE, AVEC LES MEMES CINQ ENFANTS. Rien n'est
   reconstruit en JS : on repose les cinq blocs sur une grille NOMMEE, comme
   la vue colonnes de la V45. Le seul ecart de contenu est la date de sortie
   a la place de l'annee, et il tient en une ligne de `buildCard`.

   ⚠️ ET LA GRILLE EST NOMMEE PRECISEMENT POUR QUE LE DOM NE BOUGE PAS. Une
   reorganisation par `order` ou par `appendChild` ferait diverger la carte
   de destination de la carte ordinaire au premier ajout - c'est le defaut
   que la V45 a passe une version a eviter.

   ⚠️ `--poster-w` EST REDEFINIE SUR LA CARTE, pas ailleurs : toute l'echelle
   de la carte en derive deja. Une seconde variable de taille aurait ete un
   second endroit a tenir d'accord avec le premier. */
.roadmap:not(.is-list) .card--dest {
  --poster-w: clamp(150px, 13.4vw, 196px);
  --title-fs: clamp(1.02rem, 1.5vw, 1.34rem);
  width: auto;
  display: grid;
  grid-template-columns: var(--poster-w) max-content;
  grid-template-areas: "p t" "p y" "p c" "p b" "p d";
  align-content: center;
  column-gap: var(--sp-4);
  text-align: left;
}
.roadmap:not(.is-list) .card--dest .poster-wrap { grid-area: p; align-self: center; }
.roadmap:not(.is-list) .card--dest .card-title {
  grid-area: t; align-self: end;
  height: auto; margin: 0;
  align-items: flex-start; text-align: left;
}
/* La date de sortie prend la teinte de la destination : c'est ce que la
   carte a de plus a dire, et le seul endroit du site ou une date compte. */
.roadmap:not(.is-list) .card--dest .card-year {
  grid-area: y; align-self: start;
  height: auto; margin: .25rem 0 0;
  justify-content: flex-start;
  font-size: var(--fs-m); letter-spacing: .08em; text-transform: uppercase;
  color: var(--card-accent, var(--phase-accent));
}
.roadmap:not(.is-list) .card--dest .card-band {
  grid-area: b; justify-content: flex-start; margin-top: .7rem;
}
.roadmap:not(.is-list) .card--dest .watch-row { grid-area: d; margin: .7rem 0 0; }   /* V360 */
/* ================ LE DECOMPTE (V84 - B6) ==============================
   « Un decompte vivant, facon compte a rebours officiel, EN DESSOUS de la
   date de sortie. » Il prend donc son propre rang de la grille nommee,
   entre l'annee et le bandeau de notes : "p t" "p y" "p c" "p b" "p d".

   ⚠️ IL EST PLUS PETIT ET PLUS SOURD QUE LA DATE, ET C'EST LA REGLE DU
   TITRE ET DE L'ANNEE (V5.1) : le contraste se fait par l'ECHELLE, pas par
   une couleur de plus. La date porte deja la teinte de la destination -
   c'est elle le rendez-vous, le decompte n'en est que la mesure. Une
   seconde teinte ici aurait mis deux accents sur deux lignes voisines.

   ⚠️ ET IL N'EST PAS EN `--cosmic`. Orbitron ne sert qu'au pourcentage de
   progression (V14, reaffirme en V44) : un second nombre « spatial » sur la
   meme page etendrait son domaine, ce que ce fichier interdit depuis dix
   versions. */
.roadmap:not(.is-list) .card--dest .card-countdown {
  grid-area: c; align-self: start;
  margin: .18rem 0 0;
  font-size: var(--fs-xs); letter-spacing: .06em; text-transform: uppercase;
  font-stretch: var(--ch-ui);
  color: var(--text-dim);
}


/* ⚠️ L'HORIZON PORTE LE MEME CADRE QUE LE JALON, ET C'EST VOULU : le cadre
   veut dire « destination », et Secret Wars en est une. Ce qui les distingue
   reste la pastille - celle du jalon EST l'embleme de la route (V29), celle
   de l'horizon est un cercle ordinaire, parce qu'il n'y a pas d'embleme a
   gagner sur un film qui n'est pas le jalon. */
.milestone-frame--horizon { align-self: center; }

/* ------------- Tiroirs optionnels, INSERES DANS LA RANGEE ---------------- */
/* Chaque tiroir est pose a la place CHRONOLOGIQUE de ce qu'il contient.
   Ferme : un carre blanc vide avec un "+". Ouvert : les cartes se glissent
   dans la rangee, exactement la ou elles se situent dans la timeline. */

.drawer-slot {
  flex: 0 0 auto; align-self: flex-start;
  display: flex; align-items: flex-start; gap: var(--gap);
  margin-top: calc((var(--poster-w) - 2px) * 0.75 - 15px);
  /* Le "+" est un signe, pas une carte : il n'a pas droit a la gouttiere
     pleine des affiches, sinon il ouvre deux trous dans la rangee. On la
     resserre des deux cotes, plus fort du cote du FILM auquel il se
     rattache - c'est ce qui le fait lire comme son prolongement. */
  margin-left: calc(var(--gap) * -0.7);
  margin-right: calc(var(--gap) * -0.35);
}

.drawer-toggle {
  appearance: none; cursor: pointer; padding: 0;
  flex: 0 0 auto;
  width: 32px; height: 32px;
  background: transparent; border: 0; color: #FFFFFF;
  display: grid; place-items: center;
  opacity: .75;
  transition: opacity .16s, color .16s, transform .16s;
}
.drawer-toggle svg { width: 26px; height: 26px; display: block; }
/* V394 - 32 px dessines, 34 sous le doigt : la zone invisible des pastilles. */
.drawer-toggle { position: relative; }
.drawer-toggle::after { content: ""; position: absolute; inset: -1px; }
.drawer-toggle:hover { opacity: 1; transform: scale(1.06); }
.drawer-toggle:focus-visible { outline: 2px solid var(--phase-accent); outline-offset: 3px; border-radius: var(--r-2); }

/* Ouvert : le "+" devient un "-" et passe au ROUGE - c'est la couleur qui dit
   "fermer". Le bleu de phase disait au contraire "actif", donc le contraire. */
/* Ouvert : le signe reste BLANC, comme le "+". Seule la barre verticale
   disparait pour former le "-". */
.drawer-slot.is-open .drawer-toggle { opacity: 1; color: #FFFFFF; }
.drawer-slot.is-open .drawer-plus-v { opacity: 0; }
.drawer-plus-v { transition: opacity .16s; }

.drawer-items {
  display: none;
  align-items: flex-start; gap: var(--gap);
  /* Les cartes reprennent l'axe de la rangee : on annule le decalage du slot */
  margin-top: calc((var(--poster-w) - 2px) * -0.75 + 15px);
}
.drawer-slot.is-open .drawer-items { display: flex; }

/* Carte optionnelle : hors parcours. Trait PLEIN et BLANC (jamais pointille),
   pastille blanche elle aussi. */
.card--optional .watch-dot:hover,
.card--optional.is-watched .watch-dot { color: #FFFFFF; }
.card--optional.is-watched .watch-dot { filter: drop-shadow(0 0 6px rgba(255, 255, 255, .45)); }

/* ⚠️ V136 - LA FORME EST DECLAREE ICI, PLUS PRES DE « .card-flags ». Elle y
   etait partagee avec « .card-warning », partie avec le champ « warning » :
   une forme « commune » a un seul porteur est une indirection qui ment. */
/* ⚠️⚠️ V305 - L ETIQUETTE DE STATUT EST UN FANION, PLUS UN MOT (retour de
   l auteur : « une etiquette qui fait quasi toute la largeur » rendait les
   affiches illisibles). Les TROIS statuts en portent un, a leur teinte ; le mot
   vit dans `aria-label` et `title`, la legende sous le « i » a gauche de SOLO.
   ⚠️ DOUBLE CONTOUR FIXE (decision de l auteur) : un trait sombre, puis un
   liseré clair, dessines dans le SVG - donc lisible sur n importe quelle
   affiche PAR CONSTRUCTION, sans lire un pixel. `clip-path` aurait coupe une
   bordure CSS, c est pourquoi le dessin est un trace. */
.status-fanion { display: block; width: 18px; height: 18px; line-height: 0; }
.status-fanion svg { width: 100%; height: 100%; overflow: visible; }
.fanion-bord { fill: none; stroke: var(--ink-0); stroke-width: 3.2; stroke-linejoin: round; }
.fanion-fond { stroke: #FFFFFF; stroke-width: .9; stroke-linejoin: round; }
.status-fanion--required .fanion-fond,    .legende-item--required .fanion-fond    { fill: var(--statut-requis); }
.status-fanion--recommended .fanion-fond, .legende-item--recommended .fanion-fond { fill: var(--statut-suggere); }
.status-fanion--bonus .fanion-fond,       .legende-item--bonus .fanion-fond       { fill: var(--statut-optionnel); }
/* ⚠️ V115 - « .status-chip--offcanon » EST PARTIE AVEC LE STATUT « hors-canon ».
   Elle distinguait le hors route du bonus par le REMPLISSAGE (evide contre
   plein) ; « st.tone » ne vaut plus jamais « offcanon », donc la regle n'avait
   plus de porteur - piege nº 4 en version interface. */
/* Un RECOMMANDE va dans un tiroir comme un bonus - c'est ce qui garde la
   rangee courte - mais il garde SA couleur : l'etiquette est orange. */

/* ⚠️ V464 - LE CADRE NE PREND PLUS LA COULEUR DU STATUT NON PLUS. La regle
   disait « dans un tiroir, la carte prend la couleur de son STATUT et non
   celle du degre de route » - elle repondait a une question qui ne se pose
   plus, puisque AUCUN cadre d affiche n est colore. Le statut, lui, se lit sur
   le fanion du bandeau, qui n a pas bouge. La coche, elle, garde sa teinte :
   c est une commande, pas un cadre. */
.card--optional.card--st-recommande.is-watched .watch-dot {
  color: var(--statut-suggere);
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--statut-suggere) 55%, transparent));
}

/* ------------------------------- Modale -------------------------------- */

/* ⚠⚠ V142 (2c) - LE CONTENEUR DE FENETRE A ENFIN UN FILET, ET UN CENTRAGE
   QUI NE ROGNE PAS. Il etait "display: grid; place-items: center" SANS
   "overflow-y" : le jour ou le panneau depasse la fenetre - une barre d URL
   qui se deplie sur telephone suffit - le debordement n etait pas defilable,
   il etait INATTEIGNABLE.
   ⚠️ "align-items: safe center" ET PAS "center" : un contenu centre qui
   deborde se fait rogner PAR LE HAUT, et la partie rognee ne se rattrape par
   aucun defilement. C est la mesure de la V90 sur ".landing" (titre a
   -71 px) et celle de ".phase-chain", rejouees ici - le mot-cle "safe"
   bascule sur "start" des que ca deborde, donc le haut du panneau reste
   atteignable.
   ⚠️ "overscroll-behavior: contain" empeche le geste de CHAINER vers la
   page derriere une fois la fenetre en bout de course. Le projet n en portait
   AUCUN avant cette version.
   ⚠️ LE VOILE PASSE EN "fixed", ET CE N EST PAS DECORATIF : un enfant
   "absolute" d un conteneur qui defile se cale sur sa boite VISIBLE, donc il
   serait parti vers le haut avec le contenu et aurait laisse la page a nu
   sous la fenetre. En "fixed" il se cale sur la fenetre du navigateur -
   son parent l est deja, donc rien d autre ne change. */
.modal {
  position: fixed; inset: 0; z-index: 80; padding: 1rem;
  display: grid; align-items: safe center; justify-items: center;
  overflow-y: auto; overscroll-behavior: contain;
}
.modal-backdrop { position: fixed; inset: 0; background: rgba(0, 0, 0, .9); backdrop-filter: blur(4px); }

/* ======================================================================== *
 *  FICHE - direction artistique "fiche technique" (V11)
 *  ----------------------------------------------------------------------
 *  Une seule regle de couleur :
 *      COULEUR DE PHASE = la structure   (filet lateral, signes +/-, statut)
 *      BLANC CASSE      = le contenu     (titre, intitules)
 *      GRIS CLAIR       = les valeurs
 *      AUCUN ROUGE DE MARQUE - il reste au HUD.
 *
 *  La couleur vient de la PHASE DE PRODUCTION du titre, pas de la rangee :
 *  Agent Carter (Phase 2) ouvre donc une fiche ROUGE meme s'il est place
 *  dans "Road to Avengers". Voir applyPhaseColor() dans app.js.
 *
 *  DEUX filets seulement : un sous les notes, un avant Synopsis.
 * ======================================================================== */

.modal-panel {
  position: relative; z-index: 1;
  /* Le panneau ne defile PAS : il se dimensionne sur son CONTENU jusqu a
     90 % de la hauteur d ecran, puis c est .modal-body qui defile a
     l interieur. La croix, posee sur le panneau, reste donc toujours
     visible - et l ouverture du synopsis ne fait plus deborder la fenetre.

     LARGEUR : max-content, bornee. La fenetre se cale donc sur la ligne la
     plus large - en pratique le TITRE - au lieu d occuper 940 px quel que
     soit le film. openModal() FIGE ensuite cette largeur : le synopsis et le
     casting se rangent dedans, ils ne l elargissent jamais. */
  width: max-content;
  /* Bornes en unites d ECRAN, pas en % : le panneau est un element de
     grille, et un % s y resoudrait sur une piste dimensionnee par le
     contenu - donc sur lui-meme. Le piege classique de max-content. */
  min-width: min(600px, calc(100vw - 2.2rem));
  max-width: min(1120px, calc(100vw - 2.2rem));
  /* ⚠⚠ V142 (2c) - "dvh" APRES "vh", ET LES DEUX SE LISENT COMME UN REPLI.
     "90vh" se resout sur la GRANDE fenetre du telephone - celle qu on a
     quand la barre d URL est repliee. Barre deployee, le bas du panneau
     tombait donc HORS DE L ECRAN, et avec lui les rubriques du bas. "dvh"
     suit la fenetre reellement visible. ⚠️ La ligne "vh" RESTE au-dessus :
     un navigateur qui ignore "dvh" ignore la seconde declaration et garde
     l ancien comportement - jamais une fenetre sans plafond. */
  max-height: 90vh;
  max-height: 90dvh;
  overflow: visible;
  display: flex; flex-direction: column;
  background: var(--panel);
  /* ⚠️⚠️ V463 - LE FILET DE COULEUR DU BORD GAUCHE EST PARTI (retour de
     l auteur : « le petit trait de couleur sur le cote gauche [...] il est
     present partout sur le site et je trouve que ca fait trop lourd »). Il
     portait la PHASE DE PRODUCTION du titre depuis la V20, et c etait une
     idee juste ; mais repetee sur chaque fenetre du site, elle avait cesse de
     dire quelque chose - un signal qui apparait partout ne signale plus rien.
     ⚠️ LA PHASE N EST PAS PERDUE POUR AUTANT : elle colore toujours le titre
     de rangee, les liaisons et le halo de la fenetre. Ce qui part, c est la
     TROISIEME facon de dire la meme chose, pas la chose. */
  border: var(--frame-dim);
  border-radius: var(--r-3);
  /* "Effet de phase" : le halo du panneau est de la couleur de la phase,
     comme le titre de rangee et les liaisons. */
  /* V416 - le halo colore sans decalage est parti (critique du 18 sept. :
     une lueur sans source est une decoration). La phase se dit par le
     liseré gauche ; la profondeur, par l ombre portee. */
  box-shadow: var(--lift-2);
  animation: pop .18s ease-out;
}
@keyframes pop { from { opacity: 0; transform: translateY(8px); } }

/* ====== LA CROIX PASSE AU PLANCHER TACTILE (V277 - point [86].1) ========
   Elle mesurait 32 x 32 px sur LES TREIZE FENETRES du site - mesure sur
   #raccourcis, #tuto et la fiche - alors que le plancher du projet est
   `--pastille`, 34 px. C est le defaut ② de la V267 (« Enregistrer l image »
   mesurait 33,81 px) A L ECHELLE DU SITE, et c est la premiere chose qu on
   vise au doigt pour SORTIR d un ecran qui recouvre la page.

   ⚠️⚠️ ICI ON AGRANDIT LE DESSIN, ET C EST LA REGLE DE LA V227 QUI LE DIT -
   PAS UNE EXCEPTION. Elle dit « on agrandit la CIBLE, jamais le DESSIN »
   PARCE QUE la pastille « vu » et les etoiles vivent dans une rangee dont les
   alignements sont mesures au demi-pixel : grossir leur dessin aurait decale
   67 cartes. Cette croix-la n aligne RIEN. Mesure avant de trancher : elle est
   soit en `absolute` dans un coin (quatre fenetres, 8 px de reserve autour),
   soit `static` dans le rang `.sheet-head` (huit fenetres) - dont la hauteur
   fait 45,06 px, commandee par le TITRE. Deux pixels de plus ne touchent ni
   l un ni l autre. ⚠️ Et un dessin de 32 px cache dans une cible de 34 aurait
   ete une affordance qui MENT sur sa propre taille, pour rien.

   ⚠️⚠️ C EST LE JETON QUI EST ECRIT, PAS LE NOMBRE. `--pastille` porte le
   plancher tactile du site depuis la V201 ; le litteral `32px` etait une
   TROISIEME valeur a cote de lui (piege nº 6). Le jour ou le plancher bouge,
   la croix suit sans qu on ait a s en souvenir.
   ⚠️ LE X RESTE CENTRE PAR CONSTRUCTION : un `<button>` centre son contenu,
   donc aucune demi-hauteur n est tenue a la main. */
.modal-close {
  position: absolute; top: .5rem; right: .55rem; z-index: 6;
  backdrop-filter: blur(6px);
  appearance: none; border: var(--frame-dim); background: transparent; color: var(--text-dim);
  border-radius: 50%;
  width: var(--pastille); height: var(--pastille);
  font-size: var(--fs-xl); line-height: 1; font-weight: 700; cursor: pointer;
  transition: color .14s, border-color .14s;
}
.modal-close:hover { color: var(--paper); border-color: var(--paper); }

/* ⚠️ V142 (2c) - "overscroll-behavior: contain" : arrive en haut ou en bas
   de la fiche, le geste s ARRETE la au lieu de faire defiler la page
   derriere. C est la cause la plus visible sur telephone - on croit que la
   fiche ne defile pas alors que c est la page qui a pris le geste. */
/* ================ V504 - LA FICHE A UNE LARGEUR, PLUS UNE MESURE =========
   Retour de l auteur : « modifier les proportions de la fenetre en vue PC (la
   vue telephone reste correcte) ».

   ⚠️⚠️ LA PROPORTION N ETAIT PAS CHOISIE, ELLE ETAIT SUBIE. `.modal-panel` est
   en `width: max-content` : la fenetre se calait sur sa ligne la plus large,
   c est-a-dire sur le TITRE DU FILM. Mesure a 1440 x 900 : un panneau de
   633 px, une affiche de 206 et une colonne de texte de 335, pour 800 px de
   vitre inutilisee - et une largeur qui changeait d un film a l autre.

   ⚠️ LA REGLE PORTE L IDENTIFIANT DE LA FICHE, ET C EST OBLIGATOIRE :
   `.modal-panel` est REEMPLOYE par cinq autres fenetres (amis, fin de
   parcours, tuto, lien de duo, reprise) qui vivent toutes de ce
   `max-content`. Elargir la classe les aurait toutes elargies.
   ⚠️ SUR TELEPHONE RIEN NE CHANGE, et c est mesurable : a 375 px la borne
   `calc(100vw - 2.2rem)` rend 340 px - exactement la largeur d avant. C est
   la meme formule qui bornait deja le panneau.

   ⚠️ 920 px N EST PAS UN NOMBRE ROND CHOISI AU JUGE, C EST UNE SOMME :
   300 px d affiche + 32 d ecart + 528 pour le texte + 60 de rembourrage. Les
   528 sont ce qu il faut aux 72 signes du synopsis, qui est la seule mesure
   de cette fenetre qui ait un optimum. Essaye a 1080, la colonne de texte
   recevait 688 px pour un texte borne a 495 : la fenetre devenait large pour
   rien, c est-a-dire le defaut d avant a l envers. */
#modal > .modal-panel { width: min(920px, calc(100vw - 2.2rem)); }
/* ⚠️ LE TITRE PREND LE CORPS QUE LA FENETRE LUI DONNE : dans un panneau de
   633 px il ne pouvait pas depasser 2rem sans commander la largeur (c est lui
   qui la fixait). La largeur ne dependant plus de lui, il peut enfin etre un
   titre. Il ne le prend QUE dans la fiche de film : la classe sert a cinq
   autres fenetres. */
#modal .modal-title { font-size: clamp(1.4rem, 3.1vw, 2.45rem); }

.modal-body {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden;
  overscroll-behavior: contain;
  display: flex; gap: clamp(1.1rem, 2.6vw, 2rem);
  padding: clamp(1.15rem, 2.6vw, 1.8rem);
}

/* --- Colonne gauche : affiche, statut, liens. Tout l'actionnable. -------- */

/* ⚠️ V504 - L AFFICHE SE MESURE SUR LE PANNEAU, PLUS SUR LA VITRE. `20vw`
   etait une part de l ECRAN posee dans une fenetre dont la largeur n avait
   rien a voir avec lui : a 1440 px elle butait sur son plafond de 206 px dans
   un panneau de 633. Une part en `%` se resout sur `.modal-body`, c est-a-dire
   sur la fenetre elle-meme, et l affiche garde sa proportion a toute taille.
   Les deux bornes restent : jamais moins de 150 px, jamais plus de 300. */
.modal-visual { flex: 0 0 auto; width: clamp(150px, 30%, 300px); display: flex; flex-direction: column; gap: .5rem; }
.modal-poster {
  width: 100%; display: block;
  border: var(--frame-dim); border-radius: var(--r-affiche);   /* V479 */
}

/* Le badge est le STATUT DE VISIONNAGE : c'est l'information la plus
   actionnable de la colonne, il a donc droit a du corps.
   ⚠⚠ V206 - ET C EST DESORMAIS UN <button>, DONC IL DOIT SE LIRE COMME UNE
   COMMANDE. C est la lecon de la V152 (le code de secours depouille se lisait
   comme du texte flottant) et de la V199 (une commande depouillee au point de
   ne plus se lire comme telle est le defaut qu on vient de corriger ailleurs) :
   il garde son cadre, gagne un fond au survol, un curseur et un anneau de
   focus. Rien d autre ne change a l oeil au repos.
   ⚠ LE SITE N A AUCUN RESET DE <button> - c est ce que la V194 a paye cher.
   La famille lui vient du groupe « elements d affichage » plus haut
   (`.status-badge` y figure depuis toujours) ; `width` lui vient de
   l etirement du flex en colonne de `.modal-visual`. Rien a ajouter pour ca.
   ⚠ `min-height: 34px` : la cible du site (coeur, roue, bouton Progression).
   Le badge en faisait deja ~37 par son corps et sa gouttiere - la declarer,
   c est empecher qu un reglage de corps la fasse passer dessous en silence.
   ⚠⚠ ET ELLE VIENT AVEC `flex: 0 0 auto`, PARCE QU UNE `min-height`
   ECRITE REMPLACE LE MINIMUM AUTOMATIQUE D UN ENFANT DE FLEX. Sans lui, la
   colonne de gauche pouvait comprimer le badge JUSQU A 34 px - releve, 37,75
   -> 34 - et son texte se serait fait rogner par une gouttiere plus grande que
   la place restante. La borne tactile serait devenue un plafond. C est le
   `flex: 0 0 auto` des cases de notes (V132), pour la meme raison. */
.status-badge {
  letter-spacing: .01em;
  margin: .3rem 0 0; padding: .68rem .5rem; text-align: center;
  font-size: var(--fs-s); font-weight: 800;
  flex: 0 0 auto; min-height: 34px;
  border: 1px solid color-mix(in srgb, var(--text-dim) 45%, transparent);
  border-radius: var(--r-2);
  background: transparent; color: var(--paper);
  cursor: pointer;
  transition: background .14s, border-color .14s;
}
.status-badge:hover { background: var(--ink-2); border-color: var(--ink-6); }
.status-badge:focus-visible { outline: 2px solid var(--star-mine); outline-offset: 2px; }
.status-badge.is-watched {
  background: var(--phase-accent); color: #000; border-color: var(--phase-accent);
}
.status-badge.is-watched:hover {
  background: color-mix(in srgb, var(--phase-accent) 82%, #FFF);
  border-color: color-mix(in srgb, var(--phase-accent) 82%, #FFF);
}

/* V250 - « A REVOIR » : le dessin du badge, en plus discret (il complete le
   badge, il ne le concurrence pas). Allume, un filet a la couleur de la phase. */
.revoir-btn {
  margin: .4rem 0 0; padding: .45rem .5rem; min-height: 34px;
  font: inherit; font-size: var(--fs-xs); font-weight: 700; letter-spacing: .01em;
  text-align: center; cursor: pointer;
  background: transparent; color: var(--text-2);
  border: 1px dashed var(--ink-6); border-radius: var(--r-2);
}
.revoir-btn:hover { background: var(--ink-2); color: var(--text); }
.revoir-btn:focus-visible { outline: 2px solid var(--star-mine); outline-offset: 2px; }
.revoir-btn[aria-pressed="true"] {
  color: var(--text); border-style: solid; border-color: var(--phase-accent);
}
/* V412 - « Mettre de cote » : le rouge de refus de la croix, une fois pose. */
.cote-btn[aria-pressed="true"] { color: var(--brand); border-color: var(--brand); }
/* V251 - TOUT COCHER D UN COUP : deux commandes secondaires, en texte, sous
   les boutons de la fiche. Cible de 34 px comme le reste. */
.cocher-lot { display: flex; flex-direction: column; margin: .3rem 0 0; }
.cocher-lot-btn {
  min-height: 34px; padding: .2rem .3rem; cursor: pointer;
  background: none; border: 0; border-radius: var(--r-1);
  color: var(--text-2); font: inherit; font-size: var(--fs-s);
  text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: var(--ink-6);
}
.cocher-lot-btn:hover { color: var(--text); text-decoration-color: var(--phase-accent); }
.cocher-lot-btn:focus-visible { outline: 2px solid var(--star-mine); outline-offset: 1px; }

/* L etiquette sur l affiche : sombre, pour ne pas se confondre avec celles du
   STATUT (claires). Meme forme, meme pile. */
.revoir-chip {
  display: flex; align-items: center;
  line-height: 1.2; font-weight: 700;
  border-radius: 0 var(--r-1) var(--r-1) 0;
  padding: .16rem .45rem; font-size: var(--fs-xs); letter-spacing: .01em;
  background: var(--ink-1); color: var(--text); border: 1px solid var(--text-2); border-left: 0;
}

/* Les liens : COTE A COTE, moities egales de la largeur de l'affiche.
   ⚠️ V128 - IL N EN RESTE QU UN, ET DEUX REGLES PERDENT LEUR PORTEUR AVEC
   Apple TV : « .link-chip svg » (plus aucune icone en ligne) et
   « .link-chip:hover » (sa teinte de texte n avait plus de texte, et sa
   bordure etait deja battue par celle du chip a logo). Une classe sans
   porteur laisse a l ecran l apparence d un mecanisme qui n existe plus
   (V116). ⚠ V233 : le « flex: 1 1 0 » est PARTI - en colonne, une base de
   zero ecrasait la hauteur de 36 px ; la pleine largeur vient desormais de
   l etirement de la colonne. */
/* ⚠ V233 - LA RANGEE PASSE EN COLONNE : la bande-annonce s ajoute SOUS la
   plateforme. Cote a cote dans la largeur d une affiche, « Bande-annonce VF »
   ne tenait plus a 320 px. */
.links { display: flex; flex-direction: column; gap: .4rem; }
.link-chip--ba {
  gap: var(--sp-2); color: var(--text-2);
  font-size: var(--fs-s); font-weight: 600; font-stretch: var(--ch-ui);
}
.link-chip--ba:hover { border-color: var(--ink-6); background: var(--ink-2); color: var(--text); }
.link-chip--ba:focus-visible, .link-chip--logo:focus-visible { outline: 2px solid var(--star-mine); outline-offset: 2px; }
.link-chip-ico { font-size: var(--fs-xs); color: var(--brand); }
/* La phrase d un titre sans plateforme : elle se LIT, elle ne se clique pas -
   pas de cadre, donc pas d apparence de bouton. */
.links-none { margin: 0; font-size: var(--fs-xs); line-height: 1.35; color: var(--text-dim); text-align: center; }
/* ⚠⚠ V206 - LA HAUTEUR DE LA CASE ET CELLE DU LOGO SONT LIEES PAR UN
   JETON. La demande dit « le logo plus visible, la case gardant les memes
   proportions » : c est donc la GOUTTIERE qui cede, pas la boite. Le logo
   vaut `--chip-h` moins son ecart, et la gouttiere verticale vaut la moitie
   de cet ecart - trois valeurs tenues d accord a la main, c etait le
   piege nº 6, et la premiere retouche de corps aurait fait deborder le logo
   de sa case SANS qu aucune erreur ne se produise. */
.link-chip {
  --chip-h: 36px;
  --chip-air: 8px;
  flex: 0 0 auto; min-width: 0;
  display: flex; align-items: center; justify-content: center;
  padding: calc(var(--chip-air) / 2) .3rem; height: var(--chip-h);
  border: var(--frame-dim); border-radius: var(--r-2);
  background: transparent; text-decoration: none;
  transition: border-color .14s, background .14s;
}

/* Chip a logo : le logotype Disney+ est reproduit dans ses COULEURS
   OFFICIELLES (bleu nuit), ce qui impose une pastille claire - c est le seul
   chip du site a ne pas etre transparent, et c est assume. */
.link-chip--logo { background: var(--paper); border-color: var(--paper); }
.link-chip--logo:hover { background: #FFFFFF; border-color: #FFFFFF; }
/* ⚠⚠ LE LOGO SE MESURE SUR SA CASE, IL NE S ECRIT PAS EN PIXELS. Une
   hauteur ecrite - `--chip-h` moins la gouttiere - donnait 28 px pour une case
   de 36 : elle oubliait les DEUX FILETS, et la case passait a 38 px, le
   contraire exact de « la case garde les memes proportions » (mesure). Un
   troisieme terme « - 2px » aurait ete la largeur du filet tenue a la main,
   donc fausse au premier reglage de `--frame-dim` (piege nº 6).
   ⚠ `height: 100%` se resout sur la boite de CONTENU de la case - filets et
   gouttiere deja retires -, et c est pour ca que la case porte desormais une
   `height` ferme plutot qu une `min-height` : un pourcentage n a pas de
   reference contre une hauteur indefinie. La case garde ses 36 px par
   construction, et `--chip-air` est la SEULE valeur qui decide de l ecart.
   ⚠ `width: auto` sur un element remplace garde son rapport : le logotype
   n est jamais deforme (15 -> 26 px de haut, 28 -> 48 px de large).
   ⚠ ET `align-self: stretch` A ETE ESSAYE PUIS ECARTE : avec `height: auto`
   un `<img>` retombe sur sa taille INTRINSEQUE - releve : 1041 x 565, la case
   a 575 px de haut. L etirement ne borne pas un element remplace. */
.link-chip--logo img { display: block; height: 100%; width: auto; }

/* --- Colonne droite : tout ce qui se lit -------------------------------- */

.modal-info { min-width: 0; flex: 1; display: flex; flex-direction: column; }

.modal-eyebrow {
  margin: .45rem 0 0; display: flex; align-items: center; gap: .55rem;
  font-size: var(--fs-micro); font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-dim);
}
/* Aucune case autour de la phase : la pastille de couleur suffit a la
   signaler, un cadre de plus surchargerait le sur-titre. */
.phase-chip {
  display: inline-flex; align-items: center; gap: .34rem;
  padding: 0; border: 0; background: transparent;
  white-space: nowrap;
}
.phase-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--prod-phase, var(--text-dim));
  flex: 0 0 auto;
}
.modal-year { font-variant-numeric: tabular-nums; }

/* TITRE : blanc casse, sur AUTANT DE LIGNES que `titleLines` en declare
   (ou que le decoupage automatique en deduit - voir splitTitle dans app.js). */
.modal-title {
  margin: 0; color: var(--paper);
  text-transform: uppercase; letter-spacing: -.008em;
  font-size: clamp(1.4rem, 3.1vw, 2rem); line-height: 1.04;
  /* La croix n'est evitee QUE par le titre, qui est seul a passer sous elle.
     Reserver la gouttiere sur toute la colonne creerait une bande vide sur
     toute la hauteur de la fiche. */
  padding-right: 2.4rem;
}
.modal-title span { display: block; }

/* --------------------------- Les trois notes ---------------------------- */
/* Valeur + etoile sur une ligne, LIBELLE CENTRE dessous. Aucune case. */

/* ⚠️ LES TROIS NOTES RESTENT SUR UNE LIGNE, toujours (V22).
   C'est la seule exception a la regle "le contenu se range dans le panneau" :
   ici c'est le panneau qui s'elargit pour elles. Une note qui passe a la ligne
   casse la lecture - on compare TMDB, la communaute et sa propre note d'un
   seul coup d'oeil, ou on ne compare pas.
   `flex: 0 0 auto` sur les cases : elles gardent leur largeur naturelle et
   poussent donc la largeur `max-content` du panneau au lieu de se comprimer. */
/* ⚠️ V83 - LE FILET SOUS LES NOTES EST RETIRE (menage B5). Le padding, lui,
   RESTE : c'est lui qui separait vraiment le bloc de notes du statut, le
   filet ne faisait que le souligner. ⚠️ Les border-left ENTRE les trois notes
   restent - ils sont VERTICAUX, et ce sont eux qui font des trois valeurs
   trois colonnes comparables. Le menage vise les barres HORIZONTALES. */
.scores {
  display: flex; flex-wrap: nowrap; align-items: stretch;
  gap: 0; margin: 1rem 0 0;
  padding-bottom: 1rem;
}
.score {
  flex: 0 0 auto;
  display: flex; flex-direction: column; align-items: center; gap: .3rem;
  padding: 0 1.25rem;
  text-decoration: none; color: inherit;
  border-left: 1px solid color-mix(in srgb, var(--text-dim) 28%, transparent);
}
.score:first-child { padding-left: 0; border-left: 0; }

.score-row { display: flex; align-items: center; gap: .42rem; }

.score-val {
  font-family: var(--display);
  font-size: var(--fs-xl); font-weight: 700; line-height: 1;
  font-stretch: var(--ch-chiffre);
  color: var(--paper); font-variant-numeric: tabular-nums;
}
.score.is-empty .score-val { color: var(--text-dim); }

.score-star { width: 21px; height: 21px; display: block; flex: 0 0 auto; }
.score-star path { fill: var(--paper); }

/* Le libelle est CENTRE sous le couple valeur+etoile. */
.score-src {
  font-family: var(--display);
  font-size: var(--fs-micro); font-weight: 700; font-stretch: var(--ch-ui);
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-dim); line-height: 1; white-space: nowrap;
  text-align: center;
}

/* ⚠️⚠️ V130 - « MA NOTE » EST DEVENU LE PSEUDO, ET IL LUI FAUT UNE BORNE.
   La V22 pose l invariant : LES TROIS NOTES RESTENT SUR UNE LIGNE, TOUJOURS
   (`flex-wrap: nowrap`), et c est le PANNEAU qui s elargit pour elles - la
   seule exception du site a la regle « le contenu se range dans le panneau ».
   « MA NOTE » fait 7 signes ; un pseudo en fait jusqu a 36 (V17), et la V22
   avait mesure qu un libelle allonge portait le panneau de 628 a 752 px SANS
   retour a la ligne. Sans borne, un pseudo long pousserait donc la fiche
   au-dela de ce que l ecran peut donner.

   ⚠️ 12ch, ET C EST UNE MESURE : le libelle est en capitales de .58rem avec
   .12em d interlettrage, donc 12ch valent ~72 px - a peine plus que
   « COMMUNAUTE » (10 signes), qui est deja le plus long des trois libelles
   fixes. La ligne ne s elargit donc pas d un pixel dans le cas courant.

   ⚠️ ET C EST UNE ELLIPSE, PAS UN RETOUR A LA LIGNE : `white-space: nowrap`
   est deja pose au-dessus. Un pseudo tronque reste lisible ; une ligne de
   notes cassee ne compare plus rien, ce qui est la seule chose que ce bloc
   existe pour faire. */
/* ⚠️ V132 - LA BORNE A REJOINT `.score--duo` DANS UN SELECTEUR GROUPE, PLUS
   BAS : les deux libelles variables de la ligne la partagent, et une seconde
   ecriture aurait diverge au premier reglage (piege nº 6). */

/* ⚠⚠ V207 - LES QUATRE REGLES `.score--tmdb` SONT PARTIES AVEC LEUR
   PORTEUR. La case TMDB n existe plus dans `.scores` : une regle sans porteur
   laisse a l ecran l apparence d un mecanisme qui n existe plus (V116), et
   `tools/verif-selecteurs.js` le dit desormais tout haut. Le degrade officiel
   n est pas perdu - il a suivi la valeur dans le menu, plus bas.

   Communaute : etoile BLANCHE, strictement neutre. */
.score--community .score-star path { fill: var(--paper); }

/* ⚠⚠ ET C EST DESORMAIS UN <button>. Le site n a AUCUN reset de <button>
   (ce que la V194 a paye cher) : la famille et l alignement lui viennent de
   `.score` juste au-dessus, qui declare deja `color: inherit` et sa colonne
   flex. Restent le curseur, le survol et l anneau de focus - une commande
   depouillee au point de ne plus se lire comme telle est le defaut que la
   V152 et la V199 ont corrige ailleurs.
   ⚠ SANS NOTE, LA VALEUR N EST PAS UN TIRET : elle N EST PAS AFFICHEE. La
   colonne etant deja centree, l etoile se retrouve centree sur le mot
   « Communaute » sans une ligne de plus. */
.score--community {
  appearance: none; background: transparent;
  border-top: 0; border-right: 0; border-bottom: 0;
  font: inherit; cursor: pointer;
  transition: opacity .14s;
}
.score--community:hover { opacity: .78; }
.score--community:focus-visible { outline: 2px solid var(--paper); outline-offset: 3px; }
.score--community.is-empty .score-val { display: none; }

/* Ma note : la seule couleur PERSONNELLE de la fiche (V63) - celle du profil,
   la meme que la bulle du HUD. Creuse -> pleine.
   Ne PAS mettre `stroke` dans la transition (cf. le coeur).
   ⚠️ L'INTITULE SUIT SON ETOILE, comme celui de TMDB suit la sienne : laisser
   « MA NOTE » en jaune sous une etoile qui ne l'est plus en ferait la derniere
   trace d'une teinte abandonnee. */
.score--mine .score-star path {
  fill: none; stroke: var(--star-mine); stroke-width: 1.7px;
  transition: fill .16s;
}
.score--mine.is-rated .score-star path { fill: var(--star-mine); }
.score--mine .score-src { color: color-mix(in srgb, var(--star-mine) 70%, var(--text-dim)); }

/* ⚠️⚠️ V132 (C2b) - LA NOTE DU DUO : quatrieme case, et la seule de la ligne
   qui puisse ne pas exister. Elle suit « Ma note » exactement comme celle-ci
   suit « Communaute » - meme geometrie, meme etoile, meme libelle centre.
   Ce qui la distingue est sa TEINTE, celle du profil du partenaire.

   ⚠️ SON LIBELLE EST UN SURNOM OU UN PSEUDO, DONC IL EST BORNE COMME CELUI DE
   « MA NOTE » (V130) - et il l est PAR LA MEME REGLE, pas par une seconde :
   une valeur recopiee ici aurait ete le piege nº 6 sur l invariant le plus
   fragile de la fiche (V22, « les notes restent sur UNE ligne, toujours »). */
.score--duo .score-star path { fill: var(--star-duo); }
.score--duo .score-val { color: var(--paper); }
/* ⚠⚠ V209 - LE NOM DU GROUPE REJOINT LE SELECTEUR GROUPE, IL N EN OUVRE
   PAS UN SECOND. Il y a desormais TROIS libelles variables dans cette ligne -
   mon pseudo ou le surnom qu on me donne (V130, V204), le surnom du
   partenaire (V132) et le nom du groupe - et une seconde valeur de 12ch
   ecrite a cote aurait ete le piege nº 6 sur l invariant le plus fragile de
   la fiche : « les notes restent sur UNE ligne, toujours » (V22). Un nom de
   groupe va jusqu a 36 signes, comme un pseudo. */
.score--duo .score-src,
.score--groupe .score-src,
.score--mine .score-src {
  max-width: 12ch; overflow: hidden; text-overflow: ellipsis;
}
.score--duo .score-src { color: color-mix(in srgb, var(--star-duo) 70%, var(--text-dim)); }

.score-select {
  appearance: none;
  padding: 0 .95rem 0 0;
  font-family: var(--display);
  font-size: var(--fs-xl); font-weight: 700; font-stretch: var(--ch-chiffre); line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--paper); background: transparent;
  border: 0; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, var(--text-dim) 50%),
                    linear-gradient(135deg, var(--text-dim) 50%, transparent 50%);
  background-position: right .28rem center, right .04rem center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.score-select:focus-visible { outline: 2px solid var(--star); outline-offset: 3px; }
.score-select option { background: var(--ink-3); color: var(--paper); }

/* ------------------------------ Metadonnees ----------------------------- */
/* AUCUN filet dans le bloc : les deux seuls traits de la fiche sont celui
   sous les notes et celui avant Synopsis. */

.meta { margin: 1.05rem 0 1.15rem; display: flex; flex-direction: column; gap: .9rem; }
.meta dt {
  font-family: var(--display);
  font-size: var(--fs-micro); letter-spacing: .16em; text-transform: uppercase;
  color: var(--paper); font-weight: 700; font-stretch: var(--ch-ui);
}
.meta dd { margin: .26rem 0 0; font-weight: 500; font-size: var(--fs-base); color: var(--text-2); }

/* Realisation | Sortie France | Duree : une seule ligne, separee par des
   filets, exactement comme les notes. */
/* ⚠️ CHAQUE LIGNE DE DETAILS RESTE SUR UNE LIGNE (V22), meme principe que les
   trois notes : ce sont des valeurs qu'on lit cote a cote, separees par des
   filets - "Réalisation | Sortie France | Durée" casse en deux devient une
   liste, et les filets ne veulent plus rien dire.
   `flex: 0 0 auto` sur les cellules : elles gardent leur largeur naturelle et
   commandent donc la largeur du panneau au lieu de se comprimer. */
.meta-line { display: flex; flex-wrap: nowrap; align-items: stretch; gap: 0; }
/* ⚠️ `display:flex` bat l attribut `hidden` : sans cette regle, la ligne
   "Saisons" restait visible sur un FILM. */
.meta-cell {
  flex: 0 0 auto;
  min-width: 0; padding: 0 1.15rem;
  white-space: nowrap;
  border-left: 1px solid color-mix(in srgb, var(--text-dim) 28%, transparent);
}
.meta-cell:first-child { padding-left: 0; border-left: 0; }

/* Statut : une SEULE couleur, trois intensites = trois degres d'importance. */
/* `.meta dd` est plus specifique : il faut le meme poids pour le surcharger. */
/* INDISPENSABLE : vert Doomsday - la couleur de la destination finale.
   Elle ne se confond ni avec l accent de phase ni avec le rouge de marque. */
.meta dd.status-word--required    { color: var(--statut-requis); }
.meta dd.status-word--recommended { color: var(--statut-suggere); }
.meta dd.status-word--bonus       { color: var(--statut-optionnel); }

/* --- 5e rubrique : les FACULTATIFS rattaches (V26) ----------------------- */
/* Un facultatif ne vit plus sur la rangee. Il est ici, dans la fiche du titre
   dont il depend, et la ligne entiere est un bouton : cliquer REMPLACE la
   fiche par la sienne. Pas de modale dans la modale - on navigue. */
.extras { list-style: none; margin: 0; padding: 0; display: grid; gap: .34rem; }
.extra-btn {
  width: 100%; display: flex; align-items: center; gap: .6rem;
  padding: .3rem .4rem .3rem .3rem;
  background: none; border: 1px solid transparent; border-radius: var(--r-2);
  color: inherit; text-align: left; cursor: pointer;
  transition: background .18s ease, border-color .18s ease;
}
.extra-btn:hover { background: rgba(255,255,255,.05); border-color: color-mix(in srgb, var(--phase-accent) 40%, transparent); }
.extra-btn:focus-visible { outline: 2px solid var(--phase-accent); outline-offset: 2px; }
.extra-poster {
  flex: 0 0 auto; width: 34px; height: 51px; object-fit: cover;
  border-radius: var(--r-1);
}
.extra-body { display: flex; flex-direction: column; gap: .12rem; min-width: 0; }
.extra-title {
  font-family: var(--display); font-stretch: var(--ch-ui); font-size: var(--fs-base);
  color: var(--paper); line-height: 1.2;
}
.extra-sub {
  display: flex; align-items: center; gap: .3rem;
  font-size: var(--fs-s); color: var(--text-dim);
}
.extra-status { font-weight: 700; }
.extra-status.status-word--required    { color: var(--statut-requis); }
.extra-status.status-word--recommended { color: var(--statut-suggere); }
.extra-status.status-word--bonus       { color: var(--statut-optionnel); }
.extra-dot { opacity: .5; }
/* Le mot du statut est SEUL sur sa ligne depuis V19 : le resume a son propre
   bloc, il n'y a donc plus rien a aligner a cote. */
#modalStatusText { display: flex; align-items: baseline; flex-wrap: wrap; gap: .2rem .75rem; font-weight: 800; }

/* ---- SPOILER : le contenu d'une rubrique repliee (V20) ------------------ */
/* Plus de flou ni de tag : c'est le <summary> qui nomme et qui protege.
   Meme largeur de lecture que le synopsis - 72ch - pour que la fiche ne
   s'elargisse pas a cause de lui (le panneau est en `width: max-content`). */
.spoiler { margin: 0 0 1rem; max-width: 72ch; }

.spoiler-resume, .spoiler-role {
  margin: 0; font-size: var(--fs-s); line-height: 1.6; color: var(--text-2);
  text-align: justify; text-justify: inter-word;
  hyphens: auto; -webkit-hyphens: auto;
}
/* La phrase de role est le POURQUOI : elle se detache du recit par un filet
   et par la couleur de la phase, sans changer de taille. */
.spoiler-role {
  margin-top: .55rem; padding-top: .55rem;
  border-top: 1px solid color-mix(in srgb, var(--phase-accent) 34%, transparent);
  color: var(--paper);
}

/* ⚠️ V116 - « .spoiler-plain » EST ORPHELINE DEPUIS LA VERSION PRECEDENTE : la
   V115 a retire « NO_IMPACT » avec le statut « hors-canon », et cette phrase
   generique en etait le seul porteur. Ce qui part doit partir en entier, et
   ici la seconde moitie a une version de retard - c est le genre de reste que
   ce balayage existe pour ramasser. */

/* ------------------------ Blocs deplies a la demande -------------------- */

/* ⚠️ V83 - LE FILET DE TETE DES RUBRIQUES EST RETIRE (menage B5). La regle
   `.fold + .fold { border-top: 0 }` part avec lui : elle n'existait que pour
   empecher le filet de se repeter entre deux rubriques. Une regle dont
   l'objet a disparu ne se laisse pas en place "au cas ou" (lecon V59). */
.fold summary::-webkit-details-marker { display: none; }
.fold summary::marker { content: ""; }

.fold-head {
  display: flex; align-items: center; gap: .6rem;
  padding: .72rem 0; cursor: pointer; list-style: none;
  letter-spacing: .01em;
  font-size: var(--fs-xs);
  color: var(--paper);
  transition: color .14s;
}
.fold-head:hover { color: var(--phase-accent); }
.fold-head:focus-visible { outline: 2px solid var(--phase-accent); outline-offset: 3px; }

.fold-sign { position: relative; width: 13px; height: 13px; flex: 0 0 auto; }
.fold-sign::before, .fold-sign::after {
  content: ""; position: absolute; background: var(--phase-accent); border-radius: 1px;
}
.fold-sign::before { left: 0; right: 0; top: 5.5px; height: 2px; }
.fold-sign::after  { top: 0; bottom: 0; left: 5.5px; width: 2px; transition: opacity .14s; }
.fold[open] .fold-sign::after { opacity: 0; }

/* La rubrique SPOILER garde sa fleche : c'est sa signature, elle dit que le
   contenu decoule du statut juste au-dessus. Le +/- reste identique aux
   autres rubriques - une seule affordance a apprendre pour les quatre. */
.fold--spoiler .fold-head { gap: .38rem; }
.fold--spoiler .fold-sign { margin-right: .22rem; }
.fold-arrow { color: var(--phase-accent); font-size: var(--fs-s); line-height: 1; }

/* Le bloc STATUT est la seule chose affirmee en clair : il n'a plus de filet
   sous lui, c'est la premiere rubrique qui le porte. */
/* Centre VERTICALEMENT dans sa bande : entre le filet des notes et celui de
   la premiere rubrique, l espace doit etre le meme au-dessus et en dessous.
   Mesure avant : 17 px dessus, 3 px dessous - d ou l impression de decalage. */
.meta--status { margin: .62rem 0; }
/* V234 - la ligne des scenes post-generique suit le statut, un cran dessous. */
.meta-status + .meta-status { margin-top: .45rem; }
/* V258 - « Scène(s) post-générique : 2 » tient sur UNE ligne. */
.meta-status--ligne { display: flex; align-items: baseline; gap: .5rem; }
.meta-status--ligne dt::after { content: "\00a0:"; }
.meta-status--ligne dd { margin: 0; }

/* V258 - LE PANNEAU « SIGNALER UNE ERREUR » : un cadre orange (la teinte
   d avertissement du site, `--tier-reco`) et un triangle « ! » a la place du
   +/-. Il prend la largeur de son texte, pas celle de la colonne : c est un
   bouton, pas une rubrique a lire. Ouvert, il se remplit un peu plus. */
.fold--signal { padding-top: .5rem; }
/* V416 - PLUS DISCRET (critique du 18 sept. : un bouton jaune cerne criait
   plus fort que les actions de la fiche). Un lien sans cadre ; seul le
   triangle garde le jaune, et le mot s allume au survol ou une fois ouvert. */
.fold--signal .fold-head {
  display: inline-flex; gap: .5rem; min-height: 34px; box-sizing: border-box;
  padding: .45rem .6rem .45rem .45rem;
  border: 0; border-radius: var(--r-2);
  background: transparent;
  color: var(--text-dim); font-weight: 700; font-size: var(--fs-xs); letter-spacing: .12em;
}
.fold--signal .signal-ico { color: var(--tier-reco); }
.fold--signal .fold-head:hover,
.fold--signal[open] .fold-head { background: var(--ink-3); color: var(--tier-reco); }
.fold--signal .fold-head:focus-visible { outline-color: var(--tier-reco); }
.signal-ico { display: inline-flex; width: 18px; height: 18px; flex: 0 0 auto; }
.signal-ico svg { width: 100%; height: 100%; }
.fold--signal .signal { padding-top: .7rem; }

/* V235 - Signaler une erreur. Les champs prennent la largeur de la colonne,
   jamais plus (le panneau ne s elargit pas pour eux : il ne mesure que la
   rubrique Details). */
.signal { display: flex; flex-direction: column; gap: .4rem; padding: 0 0 1rem; max-width: 72ch; }
.signal-lbl { font-size: var(--fs-xs); color: var(--text-2); }
.signal-cat, .signal-texte {
  width: 100%; box-sizing: border-box; min-width: 0;
  background: var(--ink-1); color: var(--text);
  border: var(--frame-dim); border-radius: var(--r-2);
  font: inherit; font-size: var(--fs-m); padding: .45rem .55rem;
}
.signal-cat { min-height: 34px; }
.signal-texte { resize: vertical; }
.signal-cat:focus-visible, .signal-texte:focus-visible { outline: 2px solid var(--phase-accent); outline-offset: 1px; }
.signal-pied { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; }
.signal-btn {
  min-height: 34px; padding: 0 1rem; cursor: pointer;
  background: var(--ink-2); color: var(--text);
  border: 1px solid var(--ink-6); border-radius: var(--r-2);
  font: inherit; font-size: var(--fs-s); font-weight: 600;
}
.signal-btn:hover { background: var(--ink-3); }
.signal-btn:disabled { opacity: .55; cursor: default; }
.signal-btn:focus-visible { outline: 2px solid var(--phase-accent); outline-offset: 2px; }
.signal-msg { margin: 0; font-size: var(--fs-s); color: var(--text-2); }


/* V252 - OU J EN SUIS (serie) : deux listes natives, le dessin des champs de
   la fiche. */
.ep-choix { display: flex; gap: .4rem; flex-wrap: wrap; }
.ep-sel {
  min-height: 34px; background: var(--ink-1); color: var(--text);
  border: var(--frame-dim); border-radius: var(--r-2);
  font: inherit; font-size: var(--fs-s); padding: .2rem .4rem;
}
.ep-sel:focus-visible { outline: 2px solid var(--phase-accent); outline-offset: 1px; }

/* ⚠️⚠️ V504 - LE SYNOPSIS N EST PLUS DANS UN TIROIR : il se lit. Il garde
   sa mesure de 72 signes - une colonne de texte plus large ne se lit pas
   mieux, elle se lit moins bien - et c est desormais LUI qui borne la
   colonne d information, au lieu d etre borne par elle.
   ⚠️ SON FILET DE TETE LE SEPARE DES FAITS QUI LE PRECEDENT : sans tiroir, il
   n y a plus de libelle « Synopsis » pour dire qu on change de registre. */
.modal-texte {
  margin: 0 0 .2rem; padding-top: .9rem;
  border-top: 1px solid color-mix(in srgb, var(--text-dim) 24%, transparent);
}
/* ⚠️ V504 - LES DETAILS NE SONT PLUS DANS UN TIROIR NON PLUS : trois faits
   courts n ont jamais eu besoin d etre ouverts. Ils se serrent contre le bloc
   de statut qui les precede - c est la meme nature d information. */
.meta--details { margin-top: -.35rem; }

.modal-synopsis {
  margin: 0 0 1rem;
  font-size: var(--fs-m); line-height: 1.68; color: var(--text-2);
  text-align: justify; text-justify: inter-word;
  hyphens: auto; -webkit-hyphens: auto;
  max-width: 72ch;
}

/* -------------------------------- Casting ------------------------------- */

.cast {
  list-style: none; margin: 0 0 .7rem; padding: 0;
  display: grid; gap: .75rem 1.1rem;
  grid-template-columns: repeat(auto-fill, minmax(min(210px, 100%), 1fr));
}
.cast li { display: flex; align-items: center; gap: .65rem; min-width: 0; }
.cast img {
  width: 50px; height: 50px; flex: 0 0 auto;
  object-fit: cover;
  /* Les vignettes TMDB cadrent le visage haut : on remonte le point d'ancrage
     sans quoi le menton est coupe. */
  object-position: 50% 28%;
  border-radius: 50%;
  border: var(--frame-dim); background: var(--panel-2);
}
.cast li > div { min-width: 0; }
.cast-name {
  display: block; font-size: var(--fs-m); font-weight: 700; color: var(--paper);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cast-role {
  display: block; margin-top: .1rem;
  font-size: var(--fs-xs); line-height: 1.25; color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.cast-more {
  display: inline-block; margin: 0 0 1rem;
  font-size: var(--fs-xs); font-weight: 700; color: var(--phase-accent);
  text-decoration: none;
}
.cast-more:hover { text-decoration: underline; }
/* V394 - 18 px de haut dans la fiche : la zone s etend sans deplacer le lien. */
.cast-more { position: relative; }
.cast-more::after { content: ""; position: absolute; inset: -8px 0; }

/* ---- V239 - LE MODE SANS SPOILER RENFORCE --------------------------------
   ⚠️ LA NOTE N EXISTE A L ECRAN QUE SOUS `.is-sans-spoil`, posee par le script
   sur le panneau. Hors de ce cas elle est en `display: none`, pas en `hidden` :
   c est la classe du panneau qui decide, un seul interrupteur. */
.spoil-note { display: none; margin: 0 0 .9rem; font-size: var(--fs-m); color: var(--text-dim); }
.modal-panel.is-sans-spoil .spoil-note { display: block; }
.modal-panel.is-sans-spoil .modal-synopsis,
.modal-panel.is-sans-spoil .cast img { display: none; }
.spoil-voir {
  appearance: none; cursor: pointer; background: none; border: 0; padding: 0 0 0 .3rem;
  font: inherit; font-weight: 700; color: var(--phase-accent);
  text-decoration: underline; text-underline-offset: 2px;
}
.spoil-voir:hover { color: var(--paper); }

/* ---- V241 - LA FENETRE DES RACCOURCIS CLAVIER ----------------------------
   Elle emprunte `.amis-win` (le gabarit de « Ajouter un ami ») : seule la
   liste est a dessiner. */
.raccourcis { margin: .4rem 0 0; display: grid; gap: .55rem; }

/* ---- V264 - PARTAGER SA PROGRESSION --------------------------------------
   Elle emprunte le meme gabarit `.amis-win` : seule l image est a dessiner.
   ⚠️ L IMAGE EST CARREE ET NE COMMANDE PAS LA FENETRE : `max-width: 100%` et
   `height: auto` - un `<img width="1080">` sans borne aurait impose sa
   largeur au panneau et fait deborder la page sur un telephone.
   ⚠️ SON FOND EST CELUI DE L IMAGE ELLE-MEME (`--ink-0`) : pendant le chargement
   du `blob:`, un cadre vide transparent aurait montre le panneau au travers. */
.partage-img {
  display: block; width: 100%; max-width: 420px; height: auto;
  margin: 0 auto; aspect-ratio: 1;
  background: var(--ink-0); border-radius: var(--r-2);
}
.partage-actes {
  margin: 1.1rem 0 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: .6rem;
}
/* ⚠️ LE LIEN EST UN BOUTON A L OEIL : `.sheet-btn` est dessine pour un
   `<button>`, et un `<a>` qui le porte perd le centrage vertical.
   ⚠️⚠️ V267 - ET IL FAUT AUSSI LUI RENDRE SA HAUTEUR. Mesure aux trois
   largeurs : 33,81 px, c est-a-dire SOUS le plancher tactile de 34 px que le
   site impose au cœur, a la roue et au bouton *Progression* - et sans qu
   aucune erreur ne se produise. `.sheet-btn` tient sa hauteur de son
   `padding` et d un corps de 11,2 px ; ici on l ECRIT, une fois, pour les
   deux boutons de la rangee. Le commentaire ci-dessus signalait deja la
   moitie du probleme (le centrage) sans en tirer l autre. */
.partage-actes .sheet-btn {
  margin: 0; min-height: var(--pastille);
  display: inline-flex; align-items: center; justify-content: center;
  text-decoration: none;
}
.partage-note {
  margin: .8rem 0 0; text-align: center;
  font-family: var(--read); font-style: italic;
  font-size: var(--fs-s); line-height: 1.4; color: var(--text-dim);
}

/* ---- V292 - A RETENIR AVANT DE LANCER (point [8]) ------------------------
   Une liste de rappels dans la fiche, en Literata comme tout ce qui se LIT en
   phrases (le synopsis, les deux textes de spoiler). Aucune valeur neuve : le
   corps est celui du synopsis, la puce est le filet de marque que le site
   emploie deja pour les cartes optionnelles d un tiroir.
   ⚠️ `list-style: none` + puce dessinee, donc `role="list"` est OBLIGATOIRE
   dans le balisage (V272).
   ⚠️ LA PUCE EST EN `--brand`, PAS A LA TEINTE DE PHASE : ces phrases
   rappellent ce qui vient d AILLEURS dans le parcours - d autres routes, donc
   d autres teintes. Les peindre a la couleur de la fiche dirait le contraire de
   ce qu elles racontent. */
.retenir {
  margin: .2rem 0 0; padding: 0; list-style: none;
  display: grid; gap: .45rem;
}
.retenir li {
  position: relative; padding-inline-start: .9rem;
  font-family: var(--read); font-size: var(--fs-s); line-height: 1.5;
  color: var(--text-2); text-wrap: pretty;
}
.retenir li::before {
  content: ""; position: absolute; inset-inline-start: 0; top: .6em;
  width: .4rem; height: 2px; background: var(--brand); border-radius: 1px;
}

/* ---- V290 - REPRENDRE UNE LISTE D UN AUTRE SITE (point [30]) -------------
   Elle emprunte le meme gabarit `.amis-win` que « Ajouter un ami », les
   raccourcis et le partage : il n y a ici que du TEXTE et une liste a dessiner,
   et aucune valeur neuve - `--ink-*`, `--r-*`, `--pastille` et les trois degres
   de texte suffisent.

   ⚠️ LA PHRASE QUI RASSURE PREND UNE SURFACE, PAS UNE COULEUR. « Le fichier ne
   quitte pas votre appareil » est l information la plus importante de l ecran ;
   la peindre en vert aurait invente une septieme famille de couleur, et le vert
   du site appartient a la destination. Un aplat pose (`--ink-2` + `--lift-0`)
   la detache sans rien inventer - c est le motif des cases de stats. */
.import-lede {
  margin: 0; font-family: var(--read);
  font-size: var(--fs-m); line-height: 1.5; color: var(--text-2);
}
.import-sur {
  margin: .7rem 0 0; padding: .55rem .7rem;
  background: var(--ink-2); border: var(--frame-dim); border-radius: var(--r-2);
  box-shadow: var(--lift-0);
  font-family: var(--read); font-size: var(--fs-s); line-height: 1.45; color: var(--text-2);
}
.import-sur strong { color: var(--text); }

/* Le champ de fichier. ⚠️ `min-height: var(--pastille)` : c est une cible qu on
   vise au doigt, et le bouton natif que le navigateur dessine dedans ne garantit
   pas les 34 px du site. */
.import-champ {
  margin: 1rem 0 0; display: grid; gap: .3rem;
}
.import-champ-mot {
  font-size: var(--fs-xs); letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-dim);
}
.import-champ input[type="file"] {
  width: 100%; min-height: var(--pastille);
  padding: .35rem .5rem;
  background: var(--ink-1); color: var(--text-2);
  border: var(--frame-dim); border-radius: var(--r-2);
  font: inherit; font-size: var(--fs-s);
}
.import-champ input[type="file"]::file-selector-button {
  margin-inline-end: .6rem; padding: .3rem .6rem;
  background: var(--ink-3); color: var(--text);
  border: var(--frame-dim); border-radius: var(--r-1);
  font: inherit; font-size: var(--fs-xs); cursor: pointer;
}
.import-champ input[type="file"]::file-selector-button:hover { background: var(--ink-4); }

/* L aide. Elle reprend l affordance du site - un `<summary>` - et rien d autre.
   ⚠️ Son corps est a `.75rem` (12 px) : c est du texte de LECTURE, pas une
   etiquette, donc il est au-dessus des deux planchers (10,5 et 11 px). */
.import-aide { margin: .8rem 0 0; }
.import-aide > summary {
  cursor: pointer; font-size: var(--fs-xs); color: var(--text-dim);
}
.import-aide > summary:hover { color: var(--text-2); }
.import-aide p {
  margin: .5rem 0 0; font-family: var(--read);
  font-size: var(--fs-s); line-height: 1.5; color: var(--text-dim);
}
.import-aide code {
  padding: 0 .25em; border-radius: var(--r-1);
  background: var(--ink-2); color: var(--text-2);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .95em;
}

/* Le bilan de lecture : ce qu on a compris, AVANT d ecrire quoi que ce soit. */
.import-bilan {
  margin: 1rem 0 0; padding: .7rem 0 0; border-top: var(--frame-dim);
}
.import-bilan-titre {
  margin: 0; font-weight: 700; font-stretch: var(--ch-ui);
  font-size: var(--fs-m); color: var(--text);
}
.import-liste {
  margin: .5rem 0 0; padding: 0; list-style: none;
  display: grid; gap: .22rem;
  /* ⚠️ La liste DEFILE dans le panneau au lieu de l allonger : un export de
     soixante titres aurait fait une fenetre plus haute que l ecran, et
     `.modal-body` porte deja `overflow-y: auto` - deux defilements imbriques.
     ⚠️ `overscroll-behavior: contain` comme les trois autres surfaces qui
     defilent (V142) : sans lui, arrive en bout de course, le geste CHAINE vers
     la page derriere. */
  max-height: 13rem; overflow-y: auto; overscroll-behavior: contain;
}
.import-liste li {
  display: flex; align-items: baseline; justify-content: space-between; gap: .6rem;
  font-size: var(--fs-s); color: var(--text-2);
}
.import-titre { min-width: 0; }
.import-jour { flex: 0 0 auto; font-size: var(--fs-xs); color: var(--text-dim); }
.import-liste--sourde li { color: var(--ink-7); }
.import-et-plus { font-style: italic; }

.import-restes { margin: .7rem 0 0; }
.import-restes > summary {
  cursor: pointer; font-size: var(--fs-xs); color: var(--text-dim);
}
.import-restes > summary:hover { color: var(--text-2); }

.import-msg {
  margin: .8rem 0 0; font-size: var(--fs-s); line-height: 1.4; color: var(--text-2);
}
.import-msg.is-mauvais { color: var(--brand); }

.import-actes {
  margin: 1rem 0 0; display: flex; justify-content: center;
}
.import-actes .sheet-btn { margin: 0; min-height: var(--pastille); }

.import-note {
  margin: 1rem 0 0; padding: .7rem 0 0; border-top: var(--frame-dim);
  font-family: var(--read); font-size: var(--fs-s); line-height: 1.45; color: var(--text-dim);
}
.import-note strong { color: var(--text-2); }

/* ---- V294 - LE PANNEAU PROGRESSION, REFAIT (demande de l auteur) ----------
   Une zone qui defile, puis quatre icones posees SUR le filet du bas. Les
   plis `Emporter mon parcours` et `Reprendre une liste` sont partis avec
   `.progmenu-emporter` et `.progmenu-acte` (piege nº 4). */
/* ⚠️ V318 - LE PROCHAIN TITRE A QUITTE CE PANNEAU pour la barre hero
   (`.hero-prochain`) : `.progmenu-prochain` est partie avec son balisage. */
/* ⚠️ LES QUATRE ICONES CHEVAUCHENT LE FILET : centrees sur la bordure basse
   (`translateY(50%)`), elles portent le fond du panneau, donc le trait
   s interrompt derriere chacune - « en discontinu ». Elles ne se chevauchent
   pas entre elles (un `gap`), et la reserve basse de la zone qui defile les
   empeche de chevaucher le contenu. */
.progmenu-outils {
  position: absolute; left: 0; right: 0; bottom: 0;
  transform: translateY(50%);
  display: flex; justify-content: center; gap: .55rem;
  pointer-events: none;
}
.progmenu-outil {
  position: relative; pointer-events: auto;
  appearance: none; cursor: pointer;
  width: var(--pastille); height: var(--pastille); padding: 0;
  display: grid; place-items: center;
  background: var(--ink-3); color: var(--text-2);
  border: 0; border-radius: 50%;
  transition: color .16s;
}
.progmenu-outil svg { width: 19px; height: 19px; }
.progmenu-outil:hover { color: var(--paper); }
.progmenu-outil:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
/* LA BULLE : le mot au survol et au focus. Un `title` natif aurait mis une
   seconde a paraitre, et rien au clavier. Elle se pose SOUS le panneau. */
.progmenu-outil::after {
  content: attr(data-bulle);
  position: absolute; top: calc(100% + .3rem); left: 50%; transform: translateX(-50%);
  padding: .2rem .45rem; white-space: nowrap;
  background: var(--ink-1); color: var(--text); border: var(--frame-dim); border-radius: var(--r-1);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700; font-size: var(--fs-xs);
  opacity: 0; visibility: hidden; transition: opacity .12s;
}
.progmenu-outil:hover::after,
.progmenu-outil:focus-visible::after { opacity: 1; visibility: visible; }
.progmenu-msg { margin: .45rem 0 0; text-align: center; font-size: var(--fs-xs); color: var(--text-dim); }

.temps-route-droite { display: inline-flex; align-items: center; gap: .15rem; }
/* LE [i] DU PLANNING : le dessin du « i » des affiches (INFO_SVG), sans son
   voile. La cible garde 34 px par un pseudo-element (regle V227 : on agrandit
   la CIBLE, jamais le DESSIN - il vit dans une ligne de texte). */
.temps-info {
  position: relative; appearance: none; cursor: pointer;
  width: 20px; height: 20px; padding: 0; margin: -4px 0;
  display: grid; place-items: center;
  background: none; border: 0; color: var(--text-dim);
}
.temps-info::after { content: ""; position: absolute; inset: -7px; }
.temps-info svg { width: 16px; height: 16px; }
.temps-info:hover, .temps-info[aria-expanded="true"] { color: var(--paper); }
.temps-info:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; border-radius: 50%; }
.plan-bulle {
  margin: .35rem 0 .2rem; padding: .45rem .55rem .5rem;
  background: var(--ink-2); border: var(--frame-dim); border-radius: var(--r-2);
}
.plan-bulle > .plan-texte:first-child { margin-top: 0; }
.marathon-bloc { margin-top: .5rem; padding-top: .35rem; border-top: var(--frame-dim); }
.temps-routes { list-style: none; margin: 0; padding: 0; display: grid; gap: .15rem; }
.temps-route {
  display: flex; justify-content: space-between; align-items: baseline; gap: .8rem;
  font-size: var(--fs-s);
}
.temps-route-nom { font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700; white-space: nowrap; }
.temps-route-val { color: var(--text-2); font-variant-numeric: tabular-nums; white-space: nowrap; }
.temps-route.is-fini .temps-route-val { color: var(--tier-must); }
/* Le planning et la soiree marathon : des phrases courtes, bornees pour ne
   pas elargir le panneau au-dela de la liste des routes. */
.marathon-pop .plan-texte, .marathon-pop .plan-regle { max-width: 17rem; }
.plan-texte { margin: .3rem 0 0; font-size: var(--fs-xs); line-height: 1.4; color: var(--text-2); }
.plan-texte.is-juste { color: var(--tier-reco); }
.plan-reel { color: var(--text-dim); }
.plan-bilan { text-align: center; }
.plan-regle { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin: .4rem 0 0; font-size: var(--fs-xs); color: var(--text-2); }
.plan-select {
  min-height: 34px; padding: 0 .4rem;
  background: var(--ink-2); color: var(--text);
  border: 1px solid var(--ink-5); border-radius: var(--r-2);
  font: inherit;
}
.plan-select:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* ============ V422 - « J AI DEVANT MOI », AU CENTRE SOUS LA BARRE ==========
   Un bouton-pilule qui dit la duree (la seule chose a lire), un panneau qui
   pose la question, les durees en boutons et la liste avec ses affiches. */
/* ⚠️ LE PANNEAU SE CENTRE SUR LA LIGNE, PAS SUR LE BOUTON : centre sur un bouton
   pose de cote, il sortait de l ecran sous 1200 px (mesure : 60 px a 375). */
.hero-pied { position: relative; }
.devant { flex: 0 0 auto; }
/* ⚠️ V433 - ABSENT EN VUE GROUPE (demande de l auteur) : une soiree se compte
   a une ou a deux personnes, pas a dix. */
:root.is-vue-groupe .devant { display: none; }
/* ⚠️⚠️ V471 - EN GROUPE, LA LIGNE SOUS LA BARRE SE REFERME SUR CE QUI RESTE
   (retour de l auteur : « en groupe, l espace est vide »). « J ai devant moi »
   n existe pas en vue Groupe - c est une question personnelle, pas collective
   (V433) - et le compteur de la route disparait avec lui. Le prochain titre
   restait donc SEUL a droite, deux colonnes vides a sa gauche : un trou qui
   n existait que parce que la ligne gardait la forme d un rang a trois.
   ⚠️ CETTE REGLE A DEJA ETE ECRITE ET RETIREE UNE FOIS (V461) : elle faisait
   deborder la page de 399 px des qu on revenait en Solo. La faute n etait pas
   ici - c etait le calage cumulatif du compteur, corrige dans `js/app.js` a
   cette version. On ne la remet qu avec sa cause reglee, et le banc tient
   desormais les deux bouts. */
/* ⚠️⚠️ ET LE COMPTEUR DISPARAIT, C EST LA PHRASE MEME DU RETOUR : « remonte
   tout ce qui se trouve en dessous de la barre de progression HORS COMPTEUR
   QUI DISPARAIT ». Il dit ou j en suis de MA route, ce qui ne veut rien dire
   sur une vue collective - comme « J ai devant moi » (V433).
   ⚠️ ET SANS LUI, LE CALAGE DU COMPTEUR N A PLUS DE CIBLE : 
   se tait des que le compteur n a plus de largeur, donc la marge mesuree
   retombe a zero et le prochain titre peut enfin se centrer. Les deux ne
   pouvaient pas tenir ensemble : aligner sous le pourcentage, c est se coller
   a droite. */
:root.is-vue-groupe .suivi-compte { display: none; }
/* ⚠️⚠️ ET LA LIGNE VIDE SE REFERME, PLUTOT QUE DE SE RECENTRER. Mesure en
   vue Groupe : cette ligne ne contient PLUS RIEN - « J ai devant moi » est
   parti en V433, le prochain titre se tait en vue collective (V359), et le
   compteur vient de partir ci-dessus. Elle mesurait 0 px de haut et gardait
   82 px de vide sous la barre, contre 67 px en Solo pour une ligne qui, elle,
   dit quelque chose. Centrer un contenu inexistant n aurait rien donne : ce
   qu il faut, c est que la suite REMONTE, et c est le mot du retour. */
:root.is-vue-groupe .hero-pied { margin-top: 0; }
:root.is-vue-groupe .hero-progress { padding-bottom: .5rem; }
/* ⚠️⚠️ V433 - LE BOUTON SE REDESSINE (demande de l auteur : « plus aguicheur ») :
   une capsule a deux temps - la phrase, puis la DUREE dans une puce pleine a
   la couleur de la route, chevron compris. C est la duree qu on lit et qu on
   change : c est elle qui porte la couleur. Le cerne prend un soupcon de la
   teinte, le survol l allume et pose un halo doux. */
/* ⚠️ V444 - PLUS D ENCADREMENT (retour de l auteur). Il ne reste que les
   mots et la pastille de duree : c est ELLE qui porte la couleur de la route,
   donc le bouton n a pas besoin d un second cerne pour se faire voir. Au
   survol, c est la pastille qui s allume - pas une bordure qui reapparait. */
.devant-btn {
  appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: .55rem;
  min-height: var(--pastille); padding: 3px 3px 3px .3rem;
  background: transparent;
  color: var(--text);
  border: 0; border-radius: 999px;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-base); white-space: nowrap;   /* V482 - plus imposant */
}
/* ⚠️ V447 - DORE, PAS LA TEINTE DE LA ROUTE (retour de l auteur) : le
   signe et la pastille de duree portent l or du Parcours. En Duo, le violet
   disait « a deux » sur un bouton qui ne parle pas du duo ; en Secret Wars,
   l orange de la route se confondait avec la pastille elle-meme. */
.devant-btn > svg:first-child { width: 16px; height: 16px; flex: 0 0 auto; color: var(--or-progression); }
.devant-btn:hover, .devant-btn[aria-expanded="true"] { color: var(--paper); }
.devant-btn:hover .devant-puce, .devant-btn[aria-expanded="true"] .devant-puce {
  background: color-mix(in srgb, var(--or-progression) 30%, var(--ink-1));
}
.devant-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.devant-puce {
  display: inline-flex; align-items: center; gap: .3rem;
  min-height: calc(var(--pastille) - 8px); padding: 0 .5rem 0 .65rem;
  background: color-mix(in srgb, var(--or-progression) 18%, var(--ink-1));
  color: var(--or-progression); border-radius: 999px;
}
.devant-duree {
  font-stretch: var(--ch-ui); font-weight: 800; font-size: var(--fs-m);
  font-variant-numeric: tabular-nums;
}
.devant-chevron { width: 11px; height: 11px; flex: 0 0 auto; transition: transform .16s; }
.devant-btn[aria-expanded="true"] .devant-chevron { transform: rotate(180deg); }
.devant-pop {
  position: absolute; z-index: 30; top: calc(100% + .4rem); left: 50%; transform: translateX(-50%);
  width: min(28rem, calc(100vw - 2rem));
  padding: .75rem .75rem .6rem;
  background: var(--ink-1); border: var(--frame-dim); border-radius: var(--r-3);
  box-shadow: var(--lift-2);
}
.devant-question { margin: 0 0 .55rem; text-align: center; font-size: var(--fs-s); color: var(--text-2); }
.devant-choix { display: flex; flex-wrap: wrap; justify-content: center; gap: .3rem; }
.devant-duree-btn {
  appearance: none; cursor: pointer;
  min-height: 34px; min-width: 3rem; padding: 0 .6rem;
  background: transparent; color: var(--text-2);
  border: 1px solid var(--ink-5); border-radius: 999px;
  font-family: var(--display); font-weight: 700; font-size: var(--fs-s);
  font-variant-numeric: tabular-nums;
  transition: background-color .16s, border-color .16s, color .16s;
}
.devant-duree-btn:hover { background: var(--ink-3); color: var(--text); }
.devant-duree-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.devant-duree-btn[aria-pressed="true"] {
  background: color-mix(in srgb, var(--route-accent) 18%, var(--ink-1));
  border-color: var(--route-accent); color: var(--route-accent);
}
/* V433 - le titre qui depasse de 15 min au plus : sa duree en ambre, et une
   phrase sous le bilan. */
.marathon-titre.is-deborde .marathon-duree { color: var(--tier-reco); }
.devant-avert { margin-top: .2rem; text-align: center; color: var(--tier-reco); }
.devant-pop .marathon-liste { margin-top: .65rem; padding-top: .55rem; border-top: var(--frame-dim); }
.devant-pop .marathon-liste[hidden] + .plan-bilan { margin-top: .65rem; }
.marathon-affiche {
  width: 24px; height: 36px; flex: 0 0 auto; object-fit: cover;
  border-radius: 2px; background: var(--ink-3);
}
/* ============ V431 - LES DEUX BULLES DU PARCOURS ==========================
   *Mon marathon* sous le pourcentage, *Suivi titres* sous le compteur. Toutes
   deux s alignent sur le bord droit de leur porte (le bord droit de la barre)
   et descendent ; sous 620 px, *Suivi titres* se centre sur la ligne, comme le
   compteur. */
.ancre-pop {
  position: absolute; z-index: 30; top: calc(100% + .45rem); right: 0;
  width: max-content; min-width: 16rem; max-width: min(22rem, calc(100vw - 2rem));
  padding: .65rem .8rem .75rem;
  background: var(--ink-1); border: var(--frame-dim); border-radius: var(--r-3);
  box-shadow: var(--lift-2); text-align: left;
}
.ancre-pop-titre {
  margin: 0 0 .5rem; text-align: center;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-s); letter-spacing: .01em; color: var(--paper);
}
/* Les quatre sorties chevauchent le filet du bas (V294) : la reserve basse. */
.suivi-pop { padding-bottom: 1.7rem; }
.suivi-pop .progmenu-outil { background: var(--ink-1); }
.suivi-pop .stat-scope { margin-top: .45rem; }
/* ⚠️⚠️ V482 - LE CENTRAGE SOUS 620 px EST PARTI, ET C ETAIT UNE VRAIE PANNE.
   Le panneau se centrait sur son ancre parce que cet ancre etait la ligne du
   bas, PLEINE LARGEUR ; depuis qu il pend du compteur - une boite de 65 px
   collee au bord droit - ce centrage le poussait 41 px hors de l ecran, sans
   qu aucune erreur ne se produise (mesure a 375 px). Il reprend donc la pose
   de `.ancre-pop`, la meme que « Mon marathon » juste a cote : accroche a
   droite, ou il tient. */
/* Le chevron du pourcentage (comme « J ai devant moi ») ; celui du compteur est
   plus petit et sourd, il ne s allume qu au survol ou ouvert. */
.pct-btn { display: inline-flex; align-items: center; gap: .3rem; }
.pct-chevron { width: 12px; height: 12px; flex: 0 0 auto; color: var(--route-accent); transition: transform .16s; }
.pct-btn[aria-expanded="true"] .pct-chevron { transform: rotate(180deg); }
.suivi-chevron { width: 9px; height: 9px; flex: 0 0 auto; margin-left: .25rem; opacity: .45; transition: transform .16s, opacity .16s; }
.suivi-compte:is(:hover, [aria-expanded="true"]) .suivi-chevron { opacity: 1; }
.suivi-compte[aria-expanded="true"] .suivi-chevron { transform: rotate(180deg); }
/* Le pourcentage est un bouton : il ouvre *Mon marathon*. */
.pct-btn {
  appearance: none; cursor: pointer;
  padding: .1rem .25rem; background: transparent; border: 0; border-radius: var(--r-2);
  transition: background-color .16s;
}
.pct-btn:hover { background: var(--ink-3); }
.pct-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.marathon-liste { list-style: none; margin: .4rem 0 0; padding: 0; display: grid; gap: .15rem; }
.marathon-titre {
  appearance: none; width: 100%; min-height: 30px; cursor: pointer;
  display: flex; justify-content: space-between; align-items: center; gap: .6rem;
  padding: .2rem .4rem; background: none; border: 0; border-radius: var(--r-1);
  color: var(--text); font: inherit; font-size: var(--fs-s); text-align: left;
}
.marathon-titre:hover { background: var(--ink-2); }
.marathon-titre:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.marathon-nom { flex: 1 1 auto; min-width: 0; font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700; }
.marathon-duree { color: var(--text-2); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* ---- V246 - L INDICATEUR HORS LIGNE ---------------------------------------
   Une pastille posee en bas, centree, qui ne prend pas un pixel au flux.
   ⚠️ `z-index: 36` : au-dessus de l avertissement « sans compte » (35), sous
   la bande figee (40) et sous toute fenetre - elle informe, elle ne bloque
   rien. `pointer-events: none` : elle ne vole aucun clic a ce qu elle couvre. */
.hors-ligne {
  position: fixed; z-index: 36; left: 50%; bottom: .9rem; transform: translateX(-50%);
  max-width: calc(100vw - 2rem); margin: 0; padding: .45rem .9rem;
  background: var(--ink-3); color: var(--text-2);
  border: 1px solid var(--ink-6); border-radius: var(--r-pill); box-shadow: var(--lift-1);
  font-size: var(--fs-s); text-align: center; pointer-events: none;
}

/* ---- V247 - ANNULER UNE COCHE -----------------------------------------------
   Meme dessin que la pastille hors ligne, mais elle se CLIQUE. Au-dessus de la
   fiche (80), sous le consentement (90) : on coche aussi depuis la fiche. */
.annuler {
  position: fixed; z-index: 85; left: 50%; bottom: .9rem; transform: translateX(-50%);
  display: flex; align-items: center; gap: .8rem;
  max-width: calc(100vw - 2rem); padding: .3rem .3rem .3rem 1rem;
  background: var(--ink-3); color: var(--text);
  border: 1px solid var(--ink-6); border-radius: var(--r-pill); box-shadow: var(--lift-1);
  font-size: var(--fs-m);
}
.hors-ligne:not([hidden]) ~ .annuler { bottom: 3.4rem; }

/* ============ V439 - NOTER JUSTE APRES AVOIR COCHE (planche A1) ===========
   La barre d « Annuler », en plus haut : l affiche (on reconnait le titre sans
   le lire, comme dans « J ai devant moi »), le titre, et le menu de la fiche.
   ⚠️ ELLE SE POSE AU-DESSUS D « ANNULER » quand les deux sont la : elles
   partagent le bas de l ecran, et deux barres l une sur l autre se recouvrent.
   L ordre du document le permet - la barre de note vient APRES. */
.noter {
  /* ⚠️⚠️ V467 - ELLE QUITTE LE BAS DE L ECRAN POUR LE CENTRE (retour de
     l auteur : « plus pertinent et imposant [...] directement au centre de
     notre ecran », « assez imposant » sur telephone). En barre basse elle se
     lisait comme une notification - quelque chose qui passe et qu on laisse
     passer - alors qu elle pose une QUESTION, la seule du site apres un geste.
     ⚠️⚠️ LE VOILE EST UNE OMBRE, PAS UN CALQUE, ET C EST DELIBERE. Une ombre
     portee de 100vmax assombrit toute la page sans ajouter un noeud, sans
     piege de focus, et surtout SANS BLOQUER LE CLIC : la page reste utilisable
     derriere. Une note n est pas un formulaire a remplir pour continuer - on
     doit pouvoir l ignorer et cocher le titre suivant. Un vrai calque modal
     aurait transforme une proposition en obligation. */
  position: fixed; z-index: 86; left: 50%; top: 50%; transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; gap: .85rem;
  /* ⚠️ V486 - PLUS IMPOSANTE (retour de l auteur : « une fenetre plus
     imposante/pertinente, notamment l affiche »). 22 -> 26 rem, et l affiche
     gagne 42 px de haut plus bas : c est elle qui dit DE QUEL TITRE on parle,
     et c est la seule chose de cette fenetre qu on reconnait d un coup d oeil.
     ⚠️⚠️ ET LE VOILE S ECLAIRCIT (« la fenetre doit etre moins opaque ») : 62
     -> 38 %. C est le VOILE qu on baisse, pas le fond de la fenetre - une
     fenetre translucide posee sur une grille d affiches rendrait la question
     illisible, ce qui est l inverse de « plus pertinente ». A 38 % la page
     reste lisible derriere, ce qui va dans le sens de ce voile depuis le
     debut : il n a jamais bloque le clic, on peut ignorer la question. */
  /* ⚠️⚠️ V492 - ELLE S AJUSTE A CE QU ELLE PORTE (retour de l auteur : « la
     fenetre de note doit etre ajustee aux proportions reelles des elements »).
     `26rem` etait une largeur DECIDEE : elle laissait deux gouttieres vides de
     part et d autre d une affiche de 136 px. `fit-content` la fait naitre du
     contenu - l affiche, la question, le menu - et elle suivra d elle-meme le
     jour ou l un des trois change de taille. La borne d ecran reste, elle :
     c est la seule largeur qu on ne deduit pas du contenu.
     ⚠️⚠️ CARREE (« fenetre et affiche carrees »), comme les affiches depuis la
     V479 : `--r-affiche` vaut 0, et la fenetre prend le meme angle droit. Elle
     ne recopie PAS ce zero - elle emprunte le jeton, donc le jour ou l auteur
     radoucit les angles, les deux bougent ensemble.
     ⚠️⚠️ FOND NOIR ET TRANSLUCIDE (« fond noir avec une opacite assez
     faible »). C est un RENVERSEMENT de ce que la V486 avait ecrit : elle
     refusait une fenetre translucide « posee sur une grille d affiches », et
     avait baisse le VOILE a la place. L auteur le redemande en toutes lettres,
     donc il est pris tel quel - et le flou de fond est ce qui rend la decision
     tenable : sous `blur(10px)` les affiches derriere deviennent une masse
     sombre, pas un damier. Le voile reste a 38 % : c est lui qui fournit le
     noir sous le verre. */
  width: fit-content; max-width: calc(100vw - 2.2rem);
  padding: 1.4rem 1.3rem 1.45rem;
  text-align: center;
  background: color-mix(in srgb, var(--ink-0) 62%, transparent);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  color: var(--text);
  border: 1px solid var(--ink-6); border-radius: var(--r-affiche);
  box-shadow: var(--lift-2), 0 0 0 100vmax color-mix(in srgb, var(--ink-0) 38%, transparent);
  animation: pop .18s ease-out;
}
/* ⚠️ V467 - LES DEUX REGLES QUI LA DECALAIENT AU-DESSUS D « Annuler » SONT
   PARTIES AVEC LA RAISON QUI LES AVAIT ECRITES : les deux ne se disputaient le
   bas de l ecran que tant que les deux y vivaient. */
.noter-affiche {
  width: 136px; height: 204px; flex: 0 0 auto; object-fit: cover;   /* V486 */
  border-radius: var(--r-affiche); background: var(--ink-4);   /* V479 */
}
/* ⚠️⚠️ V486 - LE FILET DE GAUCHE PART, ET IL NE SEPARAIT RIEN (retour de
   l auteur : « retirer la barre de separation inutile de cette fenetre »).
   `.score` porte un `border-left` parce que dans la FICHE il y a quatre cases
   cote a cote - Communaute, Groupe, Ma note, le duo - et que le filet est ce
   qui les separe. Ici il n y en a qu UNE : le filet n avait personne a sa
   gauche, il pendait dans le vide. Le padding qui lui faisait de la place
   part avec lui.
   ⚠️ ON RETIRE ICI, ON NE TOUCHE PAS A `.score` : le filet est juste dans la
   fiche, et une regle de base modifiee pour un seul emploi aurait casse les
   quatre autres. */
.noter-score { border-left: 0; padding-inline: 0; }
/* ⚠️ L ETOILE SUIT LA FENETRE : plus grande, comme tout le reste. Creuse tant
   qu aucune note n existe, pleine des qu il y en a une (voir
   `peindreEtoileNote` dans js/app.js). */
.noter-score .score-star { width: 26px; height: 26px; }
.noter-texte { display: flex; flex-direction: column; gap: .2rem; min-width: 0; max-width: 100%; }
/* ⚠️ V492 - `.noter-quoi` EST PARTIE AVEC SON PORTEUR (« pas de titre ») : ce
   qui n a plus de porteur part en entier. Le titre vit desormais dans l `alt`
   de l affiche, ou il ne se dessine pas.
   ⚠️ ET LA QUESTION MONTE D UN CRAN : seule sur sa ligne, elle n a plus de
   titre au-dessus d elle pour la tenir - `--fs-m` en faisait une legende. */
.noter-invite {
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 800;
  font-size: var(--fs-l); color: var(--text);
}
/* V468 - le rang dans la file : plus sourd que la question, il la precise. */
/* ⚠️ V469 - LA NOTE DU PODIUM D AUTRUI : posee sous la carte, au-dessus de la
   marche, a la couleur des etoiles - c est une NOTE, elle parle la meme langue
   que celles des affiches. */
.favs-note {
  display: block; margin: .2rem auto 0;
  font-family: var(--display); font-weight: 800; font-size: var(--fs-s);
  font-variant-numeric: tabular-nums; color: var(--star); text-align: center;
}

.noter-file {
  margin-top: .15rem;
  font-family: var(--display); font-size: var(--fs-xs); letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-2);
}
.noter-fermer {
  appearance: none; cursor: pointer; flex: 0 0 auto;
  position: absolute; right: .3rem; top: .3rem;
  width: var(--pastille); height: var(--pastille); padding: 0;
  display: grid; place-items: center;
  background: transparent; color: var(--text-dim);
  border: 0; border-radius: 50%; font-size: 1.2rem; line-height: 1;
}
.noter-fermer:hover { color: var(--text); background: var(--ink-4); }
.noter-fermer:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
@media (max-width: 420px) {
  /* ⚠️ V467 - SUR TELEPHONE ELLE NE RETRECIT PAS, C EST LA DEMANDE. Seule
     l affiche cede un peu de hauteur, pour que la carte entiere tienne sans
     que la question sorte du cadre. */
  .noter-affiche { width: 116px; height: 174px; }   /* V486 */
}
.annuler-texte { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* V405 - le pourcentage lu apres le geste : pour l oreille seulement. */
.annuler-sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.annuler-btn {
  flex: 0 0 auto; min-height: 34px; padding: 0 1rem;
  background: var(--ink-2); color: var(--text); border: 1px solid var(--ink-6);
  border-radius: var(--r-pill); font: inherit; font-weight: 700; cursor: pointer;
}
.annuler-btn:hover { background: var(--ink-4); }
.annuler-btn:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
/* V397 - SANS ACCORD, DEUX LIGNES ET DEUX BOUTONS : la phrase ne tient plus
   dans une capsule. Elle passe en carte qui flotte (`--r-3`, la regle des
   rayons), le texte au-dessus sur toute la largeur, les boutons dessous a
   droite. La note est plus sourde que le constat : elle le precise. */
.annuler-note { display: block; margin-top: .15rem; color: var(--text-dim); font-size: var(--fs-s); }
.annuler--fragile {
  flex-wrap: wrap; justify-content: flex-end; gap: .5rem;
  width: min(30rem, calc(100vw - 2rem));
  padding: .6rem .6rem .6rem .9rem; border-radius: var(--r-3);
}
.annuler--fragile .annuler-texte { flex: 1 1 100%; white-space: normal; line-height: 1.35; }

/* ---- V243 - LA FEUILLE IMPRIMEE -------------------------------------------
   ⚠️ A L ECRAN ELLE N EXISTE PAS ; A L IMPRESSION ELLE EST SEULE. Tout le
   reste du document part : une mosaique d affiches sur papier coute une
   cartouche et ne se coche pas au stylo. Encre noire sur blanc. */
.impression { display: none; }
@media print {
  body { background: #fff; color: #000; display: block; min-height: 0; }
  body > *:not(.impression) { display: none !important; }
  .impression { display: block; font-family: var(--body); color: #000; }
  .impr-titre { margin: 0 0 .6rem; font-size: 16pt; }
  .impr-route { margin: 1rem 0 .3rem; font-size: 12pt; break-after: avoid; }
  .impr-liste { list-style: none; margin: 0; padding: 0; columns: 2; column-gap: 1.2rem; }
  .impr-ligne {
    display: flex; align-items: baseline; gap: .35rem;
    padding: .12rem 0; font-size: 9.5pt; break-inside: avoid;
  }
  .impr-case {
    flex: 0 0 auto; width: .8rem; height: .8rem; align-self: center;
    border: 1px solid #000; font-size: 8pt; line-height: .8rem; text-align: center;
  }
  .impr-num { flex: 0 0 auto; min-width: 1.4rem; color: #444; }
  .impr-meta { color: #555; font-size: 8pt; }
  .impr-pied { margin-top: 1rem; font-size: 8pt; color: #555; }
}
.raccourcis > div { display: flex; align-items: center; gap: .9rem; }
.raccourcis dt { flex: 0 0 4.2rem; margin: 0; text-align: right; }
.raccourcis dd { margin: 0; color: var(--text-2); font-size: var(--fs-m); }
/* ⚠️ V271 - LA TOUCHE EST UN DESSIN PARTAGE, PAS UNE SECONDE RECETTE. La page
   Nouveautes cite les memes raccourcis ; recopier les six valeurs ici aurait
   ete deux dessins a tenir d accord au premier reglage (piege nº 6). Un
   selecteur de plus, zero valeur de plus. */
.raccourcis kbd,
.doc-body kbd {
  display: inline-block; min-width: 1.9rem; padding: .18rem .45rem;
  background: var(--ink-3); color: var(--text);
  border: 1px solid var(--ink-6); border-radius: var(--r-1); box-shadow: var(--lift-0);
  font-family: var(--display); font-weight: 700; font-size: var(--fs-s); text-align: center;
}

/* ================== LE TUTORIEL EN TROIS ETAPES (V269) ====================
   Il reprend la fenetre des raccourcis (`.raccourcis-win`) sans une regle de
   plus : seul le CONTENU a un dessin a lui, et il tient en quatre selecteurs.
   ⚠️ LE NUMERO EST UN COMPTEUR CSS, PAS UN CHIFFRE ECRIT : c est un `<ol>`,
   donc retirer une etape renumerote les autres toute seule (piege nº 6 : un
   nombre ecrit est date du jour ou on l ecrit).
   ⚠️⚠️ V272 - CE COMMENTAIRE AFFIRMAIT QUE « le lecteur d ecran annonce 1 sur 3
   sans qu on ait rien a lui dire ». C EST FAUX, et c est pourquoi le balisage
   porte desormais `role="list"` : `list-style: none` retire la semantique de
   liste sous Safari/VoiceOver, et un `::before` n est pas du contenu. Le
   numero se voyait, il ne s entendait pas - une premisse ecrite qui tombe a la
   verification, la septieme du projet.
   ⚠️ LE TEXTE EST EN PROSE, DONC EN LITERATA - c est l appariement du site
   (`--serif` pour ce qui se lit en phrases, Archivo pour ce qui se repere).
   Les trois titres d etape, eux, se REPERENT : ils restent en Archivo. */
.tuto { counter-reset: tuto; list-style: none; margin: .5rem 0 0; padding: 0; display: grid; gap: 1.05rem; }
.tuto li { position: relative; padding-left: 2.6rem; counter-increment: tuto; }
/* ⚠️⚠️ V272 - LE NUMERO NE PREND PLUS `--route-accent`, ET C EST DEUX DEFAUTS
   D UN COUP. ① Le plancher de contraste (V117) calcule une couleur libre
   contre `--ink-2`, « le plus CLAIR des deux fonds ou un signe est pose » ;
   cette pastille est posee sur `--ink-3`, plus clair encore - un TROISIEME
   fond, que le plancher ne connait pas. Mesure : les trois exemples de
   `CLAUDE.md` (#5C5C94, #007500, #8405FF) donnent 3,01 / 3,12 / 3,04 sur
   ink-2 et tombent a 2,81 / 2,92 / 2,84 sur ink-3 - SOUS le plancher, sans
   qu aucune erreur ne se produise. ② La couleur de route veut dire « la route
   qu on regarde » (la regle des familles) : un numero d etape de tutoriel ne
   parle d aucune route. On ne corrige pas le fond, on retire l emprunt. */
.tuto li::before {
  content: counter(tuto);
  position: absolute; left: 0; top: -.1rem;
  display: grid; place-items: center; width: 1.85rem; height: 1.85rem;
  background: var(--ink-3); color: var(--text);
  border: 1px solid var(--ink-6); border-radius: 50%; box-shadow: var(--lift-0);
  font-family: var(--display); font-weight: 800; font-size: var(--fs-m);
}
.tuto h3 {
  margin: 0 0 .22rem; font-family: var(--display);
  font-stretch: var(--ch-ui); font-weight: 800; font-size: var(--fs-base); color: var(--text);
}
.tuto p { margin: 0; font-family: var(--read); font-size: var(--fs-m); line-height: 1.6; color: var(--text-2); }
.tuto-fin {
  margin: 1.1rem 0 0; padding-top: .8rem; border-top: var(--frame-dim);
  font-family: var(--read); font-size: var(--fs-s); color: var(--text-dim);
}
/* ⚠️ UN SEUL BOUTON PLEIN, ET C EST LA SORTIE (regle V51) : la croix reste
   le chemin discret, celui-ci est celui qu on vise. Plancher tactile du site. */
.tuto-go {
  appearance: none; cursor: pointer; display: block; width: 100%;
  margin-top: .9rem; min-height: var(--pastille); padding: .5rem 1rem;
  background: var(--brand-plein); color: #FFFFFF;
  border: 1px solid transparent; border-radius: var(--r-2);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 800;
  font-size: var(--fs-m); letter-spacing: .02em;
  transition: background-color .16s;
}
/* V230 - le survol FONCE : eclaircir ferait retomber le blanc sous 4,5:1. */
.tuto-go:hover { filter: brightness(.9); }
.tuto-go:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }

/* ============ REPRENDRE LE COMPTE FANTOME (V341, point [N4]) ==============
   ⚠️ AUCUN DESSIN NEUF : la fenetre reemploie `.modal`, `.raccourcis-win`,
   `.sheet-head` et `.tuto-go` - « on LIT un dessin qui existe » (V46, V125,
   V202). Il ne reste ici que ce qu aucun d eux ne porte : les deux phrases et
   le rang des deux boutons. */
.reprise-quoi {
  margin: 0; font-family: var(--read); font-size: var(--fs-base);
  line-height: 1.6; color: var(--text);
}
.reprise-note {
  margin: .7rem 0 0; font-family: var(--read); font-size: var(--fs-s);
  line-height: 1.6; color: var(--text-dim);
}
/* ⚠️ UN SEUL DES DEUX EST PLEIN, ET C EST CELUI QUI NE PERD RIEN (regle V51,
   celle de l accueil) : « Commencer a zero » garde le meme corps et le meme
   contraste, il n a que le fond en moins. Ce n est pas un mur, c est un
   choix - et fermer la fenetre vaut « Reprendre ». */
.reprise-actions { margin-top: 1.1rem; display: grid; gap: .5rem; }
.reprise-actions .tuto-go { margin-top: 0; }
.reprise-actions .sheet-btn {
  width: 100%; justify-content: center; min-height: var(--pastille);
}

/* ⚠️ V356 - LE SECOND BOUTON DE L ECRAN DE FIN : CERNE, JAMAIS PLEIN. La
   V270 a deja tranche que le plein est celui de la suite, et sa raison tient
   ici aussi - un aplat a `accent` demanderait un texte sombre, et sur
   `#C02F24` le fond de page ne donne que 3,0:1. On reprend donc la teinte
   GARANTIE lisible (`--route-accent`, qui passe par le plancher V117) en
   TRAIT, et le texte la garde. */
.fin-partage {
  appearance: none; cursor: pointer; display: block; width: 100%;
  margin-top: .5rem; min-height: var(--pastille); padding: .5rem 1rem;
  background: transparent; color: var(--text-2);
  border: 1px solid var(--ink-5); border-radius: var(--r-2);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-s); letter-spacing: .02em;
  transition: border-color .16s, color .16s;
}
.fin-partage:hover { border-color: var(--text-dim); color: var(--text); }
.fin-partage:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }

/* ============ LE LIEN D INVITATION AU DUO (V353, point [F2]) ==============
   ⚠️ AUCUN DESSIN NEUF : la fenetre est celle de la reprise (V341), donc
   celle du tutoriel. Il ne reste ici que les trois phrases. */
.duolien-qui {
  margin: 0; font-family: var(--read); font-size: var(--fs-base);
  line-height: 1.6; color: var(--text);
}
.duolien-quoi {
  margin: .7rem 0 0; font-family: var(--read); font-size: var(--fs-s);
  line-height: 1.6; color: var(--text-dim);
}
.duolien-msg {
  margin: .7rem 0 0; font-family: var(--read); font-size: var(--fs-s);
  line-height: 1.5; color: var(--text-2);
}
.duolien-msg:empty { display: none; }

/* ⚠️ V353 - LE LIEN A ENVOYER, cote invitant. Il reemploie `.account-code`
   (le code de secours, V152) : meme cadre, meme cible, meme geste - on clique,
   c est copie. Il ne reste ici qu un filet et la casse. */
.duolien-part { margin-top: 1rem; padding-top: .9rem; border-top: var(--frame-dim); }
.duolien-part-tete {
  margin: 0 0 .5rem; font-family: var(--read); font-size: var(--fs-s);
  color: var(--text-dim);
}
/* ⚠️ UNE ADRESSE NE SE MET PAS EN CAPITALES, et `.account-code` le fait pour
   un code de neuf signes : on le defait ici, sur le SEUL porteur concerne. */
.duolien-lien {
  text-transform: none; letter-spacing: 0; font-size: var(--fs-s);
  word-break: break-all;
}

/* ⚠️ V350 - LES TROIS FILTRES DE GROUPE NE SE VOIENT QUE SUR LEUR VUE. Une
   pastille qui ne peut rien filtrer est un bouton mort qui s ignore (V18) ;
   la garde qui les rend INERTES, elle, est dans `cardMatches` - un pas
   d interface se contourne, une garde de filtre non (V70). */
:root:not(.is-vue-groupe) .chip-f[data-vue="groupe"] { display: none; }

/* ============ « VU PAR N » ET LE MENU QUI DIT QUI (V349) ==================
   ⚠️ LE NOMBRE REEMPLOIE `.band-value`, le dessin des valeurs du bandeau : il
   n a ni corps ni chasse a lui. Seule sa couleur change quand TOUT le groupe a
   vu - c est la seule information que le nombre seul ne donne pas d un coup
   d oeil. */
.band-grp .grp-compte { color: var(--text-dim); }
.band-grp.is-tous .grp-compte { color: var(--tier-must); }

/* ⚠️ UNE LIGNE « PAS VU » RESTE LISIBLE : elle se distingue par sa SOURDINE,
   jamais en disparaissant - on vient precisement d ouvrir pour savoir qui n a
   pas vu. Le signe, lui, garde son contraste : c est lui qui repond.
   ⚠️⚠️ V362 - TROIS STATUTS, ET CHACUN A SA TEINTE DE SIGNE, jamais une
   teinte de LIGNE : coche `--tier-must` (vu), sablier `--ink-7` (a regarder),
   croix `--brand` (mis de cote) - le rouge du refus, celui-la meme que porte
   le bouton de la carte (V360). Peindre la ligne entiere aurait fait du menu
   un nuancier, exactement ce que la V54 refuse aux filtres. */
.grpmenu-line.is-pas-vu .grpmenu-nom,
.grpmenu-line.is-mis .grpmenu-nom { color: var(--text-dim); }
.grpmenu-signe {
  display: inline-flex; flex: 0 0 auto; width: 14px; height: 14px;
  color: var(--tier-must);
}
.grpmenu-line.is-pas-vu .grpmenu-signe { color: var(--ink-7); }
.grpmenu-line.is-mis .grpmenu-signe { color: var(--brand); }
.grpmenu-signe svg { width: 100%; height: 100%; }

/* ============ LE BANDEAU DES MEMBRES (V348, points [G5] et [G11]) =========
   ⚠️ IL PREND LA PLACE DU RUBAN DU DUO, ET IL N Y A QU UNE ROUTE AFFICHEE A LA
   FOIS : les deux ne coexistent jamais, donc aucun empilement a arbitrer.
   ⚠️ IL S ENROULE (`flex-wrap`) au lieu de defiler : une rangee horizontale qui
   defile cacherait des membres SANS LE DIRE - le defaut que la V77 a retire a
   la bande figee. Vingt membres au plus (plafond serveur), donc trois ou
   quatre lignes dans le pire cas.
   ⚠️ LA LIGNE ENTIERE PORTE SON NOM ACCESSIBLE ; le rang et la bulle sont
   `aria-hidden` (V149 : « le libelle disparait, le verbe non »). */
/* ============== V365 - LE RUBAN DU GROUPE (renversement de la V348) ========
   ⚠️⚠️ LES CINQ REGLES DE LA V348 SONT PARTIES AVEC LEUR OBJET : `.grp-membre`,
   `.grp-rang`, `.grp-nom`, `.grp-pct` et leurs variantes `is-moi` decrivaient
   un CLASSEMENT - un rang, un pourcentage, un filet sur ma ligne. L auteur
   retire le classement : « meme visuel que la route DUO, les pourcentages ne
   sont pas affiches, seul le clic profil permet de le voir ».
   ⚠️ ON RETIRE DES SELECTEURS, PAS UNE PLAGE DE LIGNES (faute de la V194) :
   `tools/verif-selecteurs.js` verifie qu aucun jeton retire n a encore de
   porteur.
   ⚠️⚠️ ET LE DESSIN D UN MEMBRE N EST PAS RECOPIE : `.ruban-cote`,
   `.ruban-bulle`, `.ruban-nom` et `.ruban-filet` sont ceux du DUO, repris par
   leurs classes. Deux rubans jumeaux auraient diverge au premier reglage, sur
   un ecart MUET - le geste de `.amis-item` (V176). Il ne reste ici que ce qui
   n existe QUE dans le groupe : la tete, la couronne et la croix. */
.grp-bandeau {
  display: flex; flex-direction: column; align-items: center;
  gap: .35rem;
  width: var(--shell); margin: .5rem auto 0;
}
/* ⚠️ V371 - LA TETE S EMPILE : embleme, nom, roue (meneur), partage.
   L embleme du groupe fait 10 % de plus que celui d un membre, et il SUIT la
   taille des membres (36 px, 46 px des 1200 px) par le meme jeton. */
/* V373 - l identite reste au centre (au-dessus des membres) ; le partage et
   son filet sortent du flux a sa droite, comme le [+] des membres. */
/* ⚠️ V461 - LA TETE S EMPILE : la plaque, puis la sortie du groupe, toutes
   deux centrees sur la page. */
.grp-tete { position: relative; display: flex; flex-direction: column; align-items: center; }
/* V415 - l identite du groupe sur UNE ligne (embleme, nom, roue) : empilee,
   elle prenait trois rangs pour un nom. */
/* ⚠️⚠️ V451 - LA PLAQUE (planche N1b, choix de l auteur) : embleme, nom,
   roue sur une ligne, et un FILET a la couleur du groupe sous les trois.
   ⚠️ AUCUN FOND, AUCUN CADRE - l auteur l a ecrit deux fois. Ce qui dit
   l appartenance, c est le trait, exactement comme le souligne sous MON
   pseudo dans le rang des membres (V189) : une seule grammaire pour la meme
   idee. Un aplat de plus se serait dispute l attention avec la barre juste
   au-dessus (regle des trois portes, V51).
   ⚠️ NI COMPTE DE MEMBRES NI POURCENTAGE (demande de l auteur) : le rang
   juste dessous dit qui est la, et une liste repond a QUI, pas a OU EN
   SONT-ILS (V216). */
.grp-ident {
  display: flex; align-items: center; justify-content: center;
  gap: .45rem; min-width: 0; max-width: min(24rem, 100%);
  padding: 0 .2rem .3rem;
  border-bottom: 2px solid var(--grp-teinte, var(--brand));
}
.grp-enseigne { color: var(--avatar-color, var(--brand)); flex: 0 0 auto; display: grid; place-items: center; }
/* V371b - +15 % sur le dessin et sur le nom (demande de l auteur) : 1,1 x 1,15 */
.grp-enseigne svg { width: var(--ruban-emb); height: var(--ruban-emb); display: block; }   /* V424 - la taille des membres, donc du duo */
/* V424 - l embleme d un membre N A PLUS de taille a lui : c est celle du duo */
/* `.profile-avatar` (plus bas) pose 28 px : la tete le leve, sinon le dessin deborde sur le nom. */
.grp-tete .grp-enseigne { width: auto; height: auto; }
/* ⚠️⚠️ V461 - LA SORTIE DU GROUPE, SOUS LA PLAQUE (retour de l auteur :
   « centre a la page, en dessous du nom de groupe ligne horizontale [...] il
   reste rouge mais n est plus souligne [...] la taille est grandement
   reduite »). Elle ne reprend plus `.cpt-out` : ce bouton-la est SOULIGNE par
   definition, et le souligner ici aurait demande de lui retirer une propriete
   qu il tient de sa classe - on aurait ecrit la regle deux fois, une pour
   donner, une pour reprendre.
   ⚠️ ROUGE, MAIS PAS LE ROUGE DE MARQUE : c est celui du statut requis, le
   rouge « attention » du site. Le rouge de marque est celui de la capsule
   Snapline et des liens ; les deux ne disent pas la meme chose. */
.grp-sortie {
  margin: .3rem 0 0; padding: .1rem .5rem;
  background: none; border: 0; cursor: pointer;
  font-family: var(--display); font-size: var(--fs-micro); letter-spacing: .04em;
  text-transform: uppercase; text-decoration: none;
  color: color-mix(in srgb, var(--statut-requis) 78%, var(--ink-0));
  border-radius: var(--r-1);
}
.grp-sortie:hover { color: var(--statut-requis); }
.grp-sortie:focus-visible { outline: 2px solid var(--statut-requis); outline-offset: 2px; }
/* V424 - 36 px a toutes les largeurs, comme le duo (la regle des 40 px a 1200 est partie) */
/* ⚠️ LE NOM PASSE SOUS L EMBLEME : c est le geste du ruban du duo (V189). */
.grp-titre {
  text-align: center;
  min-width: 0; max-width: 100%; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 800;
  font-size: calc(var(--fs-l) * 1.15); letter-spacing: .02em; color: var(--text);
}
/* ⚠️ LA ROUE EST EN ORDRE DE DOCUMENT APRES L EMBLEME, donc a sa droite :
   aucun `order` en CSS - une inversion que le document ne porte pas est une
   inversion que le clavier ne voit pas (V45, V53, V77). */
/* ⚠️ V451 - ELLE NE PREND PLUS DE PLACE INUTILE (retour de l auteur) : 22 px
   au lieu de la cible de 34, et sourde tant qu on ne la regarde pas. Elle
   descend sous le plancher tactile du site, et c est assume ICI seulement :
   elle n est visible que pour le meneur, elle ne fait rien d irreversible, et
   tout ce qu elle ouvre est aussi atteignable par l ecran du groupe. */
.grp-roue {
  appearance: none; cursor: pointer; padding: 0;
  position: relative; flex: 0 0 auto;
  display: grid; place-items: center;
  width: 22px; height: 22px;
  background: transparent; border: 0; border-radius: 50%;
  color: var(--ink-7);
  transition: color .16s, background-color .16s;
}
.grp-roue svg { width: 14px; height: 14px; display: block; }
.grp-roue:hover { color: var(--text); background: var(--ink-3); }
.grp-roue:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

/* ⚠️ LE RANG S ENROULE, IL NE DEFILE PAS : une rangee horizontale qui defile
   cacherait des membres SANS LE DIRE - le defaut retire a la bande figee en
   V77. Un groupe est borne a dix membres, donc deux lignes au pire. */
/* ⚠️⚠️ V424 - LE RANG DU GROUPE EST CELUI DU DUO (demande de l auteur) : meme
   ecart (`--sp-5`), memes filets de 34 px, alignement au centre - et UNE SEULE
   LIGNE, qui defile si elle deborde (renversement de la V52 pour ce rang,
   proposition 4 acceptee). `safe center` : centre tant qu il tient, cale a
   gauche des qu il deborde (sinon le premier membre sortirait par la gauche,
   hors d atteinte du defilement). Le rembourrage garde l anneau de focus et la
   croix d exclusion hors de la coupe du defilement. */
.grp-ligne {
  position: relative;
  display: flex; align-items: center; justify-content: safe center;
  flex-wrap: nowrap; gap: var(--ruban-ecart);
  width: 100%; max-width: 100%; overflow-x: auto; overflow-y: hidden;
  padding: 6px 12px 4px;
  scrollbar-width: thin; scrollbar-color: var(--ink-5) transparent;
  -webkit-overflow-scrolling: touch;
}
.grp-ligne > .ruban-cote { flex: 0 0 auto; }
/* ⚠️ V443 - LE CONTREPOIDS DE LA V435 EST PARTI AVEC SON OBJET : le rang se
   centre desormais sur le milieu des EMBLEMES, mesure par `js/equipe.js`, qui
   repond du [+] comme du reste (« Quitter le groupe », la couronne, un pseudo
   plus long). Le rembourrage lateral du rang est son seul levier. */
/* Le fondu dit qu il reste des membres de ce cote-la (`js/equipe.js`). */
.grp-ligne.a-droite { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent); }
.grp-ligne.a-gauche { -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 2.5rem), transparent); mask-image: linear-gradient(to left, #000 calc(100% - 2.5rem), transparent); }
.grp-ligne.a-gauche.a-droite { -webkit-mask-image: linear-gradient(to right, transparent, #000 2.5rem, #000 calc(100% - 2.5rem), transparent); mask-image: linear-gradient(to right, transparent, #000 2.5rem, #000 calc(100% - 2.5rem), transparent); }
/* ⚠️ V371 - LES MEMBRES ONT LA TAILLE DU DUO : plus de surcharge du nom. */

/* ⚠️⚠️ LA COURONNE EST EN EXPOSANT, HORS DU FLUX, ET C EST CE QUI TIENT LE
   « sans chevauchement » DEMANDE : dans le flux elle aurait elargi la colonne,
   donc decale le pseudonyme sous elle plutot que sous l embleme - le defaut
   exact que le crayon du ruban a paye en V195. Elle deborde sur le VIDE entre
   deux membres, jamais sur eux (l ecart du rang est `--sp-4`). */
.grp-bulle-bloc { position: relative; display: inline-flex; flex: 0 0 auto; }
/* V373 - la couronne passe AU-DESSUS du meneur, centree : a droite, elle
   touchait le filet qui separe deux membres. Le rang lui reserve sa hauteur. */
/* ⚠️ V424 - LA COURONNE A GAUCHE DU NOM, HORS DU FLUX : `.ruban-sous` est deja
   en `position: relative` (c est l ancre du menu du surnom), elle s y pose a
   `right: 100%`. La case du meneur a donc la taille de celle des autres. */
.grp-couronne-boite {
  position: absolute; right: calc(100% + 4px); top: 50%; transform: translateY(-50%);
  display: block; pointer-events: none; color: var(--star);
}
.grp-couronne { width: 13px; height: 10px; display: block; }

/* ⚠️ LA CROIX VIT SOUS LE PSEUDONYME (demande de l auteur), et sa cible fait
   le plancher tactile du site. ⚠️ Elle ne PREND PAS de hauteur aux autres :
   le rang est en `align-items: flex-start`, donc un membre sans croix ne
   grandit pas pour rien. */
/* V415 - la croix se pose sur le coin haut-droit de l embleme (elle ne prend
   plus une ligne sous chaque membre) ; sa zone de toucher reste de 34 px. */
.grp-retirer {
  appearance: none; cursor: pointer; padding: 0;
  position: absolute; top: -4px; right: -10px;
  display: grid; place-items: center;
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--ink-0); border: 0;
  color: var(--brand);            /* V371 - la croix est rouge */
  transition: filter .16s;
}
.grp-retirer svg { width: 12px; height: 12px; display: block; }
.grp-retirer::after { content: ""; position: absolute; inset: -7px; }
.grp-retirer:hover { filter: brightness(1.25); }
.grp-retirer:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

@media (max-width: 620px) {
  .grp-titre { font-size: calc(var(--fs-m) * 1.15); }
}

/* ⚠️⚠️ V366 - `.is-sans-groupe` EST PARTIE AVEC SA RAISON D ETRE. La V347
   cachait le troisieme choix tant qu on n avait pas de groupe ; l auteur veut
   la route Groupe accessible SANS groupe, comme la route Duo l est sans duo
   depuis la V193 - c est de la que part la creation. Une classe sans porteur
   part en entier (piege nº 4), et ce qui cache encore ce bouton est le
   `hidden` que `js/duo.js` pose sur les trois quand il n y a pas de compte. */

/* ⚠️ LE BOUTON *Creer un groupe* DU RUBAN : il reprend `.sheet-btn`, le
   dessin des boutons cernes du site. Aucune couleur neuve. */
.grp-creer { margin-top: .2rem; }
/* V373 - le compte rendu des gestes du groupe, sous le ruban */
.grp-msg { width: var(--shell); margin: .5rem auto 0; text-align: center; }   /* V371 - le bouton a logo (`.equipe-creer-btn`) */
/* ⚠️ LE `[+]` EST CELUI DU DUO (`.ruban-plus`), repris par sa classe : il n y
   a donc rien a dessiner ici. Seule sa place change. */
/* V371b - le [+] et son filet hors du flux, a droite : les membres restent centres */

/* ================= LA VUE GROUPE, EN LECTURE SEULE (V347) =================
   ⚠️ LA PASTILLE RESTE A SA PLACE ET GARDE SA TAILLE : elle ne bouge jamais
   (regle V6), donc on ne la retire pas - on la rend inerte et on le MONTRE.
   Une pastille absente aurait decale les soixante-sept cartes d une vue a
   l autre ; une pastille qui repond aurait menti.
   ⚠️ `pointer-events: none` SUR LE BOUTON, ET LA GARDE EST DANS `poserVus` :
   la CSS dit ce qu on voit, le JS dit ce qui s ecrit. Un pas d interface se
   contourne, une garde d ecriture non (V70). */
/* ⚠️⚠️ V368 - LA PASTILLE ET LA CROIX NE SONT PLUS GRISEES ICI, ELLES NE SONT
   PLUS LA DU TOUT : l embleme du groupe prend leur place (demande de
   l auteur). L argument de la V347 - « la pastille reste a sa place et garde
   sa taille, on la rend inerte et on le MONTRE » - perd son objet : il n y a
   plus rien a griser, il y a autre chose a voir.
   ⚠️ CE QUI RESTE GRISE EST CE QUI N A PAS DE REMPLACANT : la coche de route
   et mon etoile de note. Et la garde d ECRITURE ne bouge pas d une ligne -
   un pas d interface se contourne, une garde d ecriture non (V70). */
/* V371b - la barre du groupe prend la couleur du groupe : pas de crayon pour la changer */
:root.is-vue-groupe .pctmenu-btn,
:root.is-vue-groupe .pctmenu-pop { display: none; }
:root.is-vue-groupe .phase-tout,
:root.is-vue-groupe .band-note.band-mine {
  pointer-events: none; opacity: .38;
}

/* ⚠️ V368 - L EMBLEME DU GROUPE DANS LE CRENEAU DE LA PASTILLE. Il garde le
   dessin qu il avait dans le bandeau (`.band-note.band-grp`), et il n a besoin
   que de la taille du creneau : 30 px, comme ses trois voisins. */
/* ⚠️ V371 - L EMBLEME GRANDIT ET SE CENTRE, LE COMPTE PASSE DESSOUS
   (demande de l auteur). Toutes les cartes de la vue Groupe portent le meme
   empilement, donc les rangees restent alignees entre elles. */
.watch-row .band-grp {
  display: inline-flex; flex-direction: column; align-items: center; gap: .15rem;
  min-height: var(--pastille); padding: 0 .2rem;
}
.watch-row .band-grp .profile-avatar--band { width: 34px; height: 34px; }
.watch-row .band-grp .grp-compte { font-size: var(--fs-s); }

/* ==================== L ECRAN DE FIN DE ROUTE (V270) =======================
   Il reprend la fenetre du site sans une regle de structure neuve : `.modal`,
   `.modal-panel`, la croix. Ce qui lui appartient est un bloc CENTRE et
   l embleme de la route, a sa taille de celebration.
   ⚠️ LA TEINTE VIENT DE LA ROUTE, POSEE SUR LA FENETRE PAR `js/app.js`
   (`--phase-accent` / `--phase-accent-soft`) : aucune couleur neuve, et
   `accentSoft` est deja la variante lisible sur noir (4,5:1 minimum). */
/* ⚠️⚠️ V272 - `display: flex` MANQUAIT, ET LES DEUX LIGNES SUIVANTES ETAIENT
   DONC INERTES. `.modal-body` est en `display: block` au-dessus de 860 px ;
   declarer `flex-direction` et `align-items` sans declarer le `display`, c est
   ecrire trois regles dont deux ne font rien - et rien ne le signale. Mesure
   avant correction : l embleme tombait 228 px a GAUCHE du centre a 1280 px,
   pendant que le texte, lui, etait centre par `text-align`.
   ⚠️⚠️ ET LE PREMIER CORRECTIF N A PAS SUFFI, POUR LE PIEGE Nº 2 EXACTEMENT :
   `.modal.amis-win .modal-body` declare `display: block` avec une specificite
   de (0,3,0) - un `.fin-body` a (0,1,0) ne gagnait pas, il ATTENDAIT. On monte
   donc a (0,4,0) plutot que de recopier (0,3,0) plus bas dans le fichier : une
   regle qui ne gagne que par ORDRE DE SOURCE ne gagne pas. */
.fin-body {
  flex-direction: column; align-items: center; text-align: center;
  gap: 0; padding-top: .5rem;
}
/* SEUL `display` est conteste (la regle d en face ne declare que lui) : on ne
   monte en specificite QUE la propriete qui en a besoin, et `.fin-body` garde
   son selecteur - sinon `tools/verif-selecteurs.js` signale, a juste titre,
   une classe dont la regle a disparu. */
.modal.amis-win.fin-win .fin-body { display: flex; }
.fin-mark {
  width: 84px; height: 84px; margin-bottom: .9rem;
  color: var(--phase-accent-soft);
}
.fin-mark svg { width: 100%; height: 100%; display: block; }
.fin-quoi {
  margin: 0; font-family: var(--display);
  font-stretch: var(--ch-ui); font-weight: 700; font-size: var(--fs-xs);
  letter-spacing: .14em; text-transform: uppercase; color: var(--text-dim);
}
.fin-titre {
  margin: .3rem 0 0; font-family: var(--display);
  font-stretch: var(--ch-affiche); font-weight: 800; font-size: clamp(1.25rem, 4vw, 1.6rem);
  line-height: 1.15; color: var(--phase-accent-soft);
}
.fin-compte {
  margin: .55rem 0 0; font-family: var(--display);
  font-weight: 700; font-size: var(--fs-m); color: var(--text-2);
  font-variant-numeric: tabular-nums;
}
.fin-mot {
  margin: .85rem 0 0; font-family: var(--read);
  font-size: var(--fs-m); line-height: 1.55; color: var(--text-dim);
}
/* ⚠️⚠️ IL EST CERNE, PAS PLEIN, ET C EST UNE MESURE DE CONTRASTE. Un aplat a
   la teinte de la route aurait demande un texte sombre par-dessus : sur
   #C02F24 (l Ere d Ultron) le fond de page ne donne que 3,0:1, sous le seuil
   d un TEXTE (4,5:1) - illisible sur UNE route et pas sur les autres, SANS
   qu aucune erreur ne se produise. `accentSoft` est au contraire la teinte
   garantie lisible sur noir (4,5:1 minimum, regle de `js/data.js`) : on la
   porte donc en TEXTE et en filet. Plancher tactile. */
.fin-go {
  appearance: none; cursor: pointer; display: block;
  width: 100%; margin-top: 1.2rem;
  min-height: var(--pastille); padding: .55rem 1rem;
  background: var(--ink-3); color: var(--phase-accent-soft);
  border: 1px solid var(--phase-accent-soft); border-radius: var(--r-2);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 800;
  font-size: var(--fs-m);
  transition: background-color .16s;
}
.fin-go:hover { background: color-mix(in srgb, var(--phase-accent-soft) 16%, var(--ink-3)); }
.fin-go:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
/* ⚠️ L ENTREE NE PORTE AUCUNE INFORMATION, donc elle se coupe sans reserve :
   la classe `is-anime` n est posee que hors `prefers-reduced-motion` et hors
   `reg-calme` (c est `js/app.js` qui decide, pas une seconde media query a
   tenir d accord). */
@keyframes finpose {
  from { opacity: 0; transform: translateY(10px) scale(.97); }
  to   { opacity: 1; transform: none; }
}
.fin-win.is-anime .modal-panel { animation: finpose .34s ease-out both; }

/* Mobile ET tablette : la fiche passe en colonne, donc TOUT est centre -
   sinon le bloc de metadonnees flotte a gauche sous une affiche centree. */
@media (max-width: 860px) {
  .modal-body { flex-direction: column; align-items: center; }
  .modal-visual { width: min(206px, 62%); }
  .modal-info { width: 100%; text-align: center; align-items: center; }
  .modal-eyebrow { justify-content: center; }

  /* Les trois notes se repartissent la largeur au lieu de se bousculer. */
  .scores { flex-wrap: nowrap; width: 100%; }
  .score { flex: 1 1 0; padding: 0 .4rem; }

  /* ⚠️⚠️ V132 - `flex: 1 1 0` NE SUFFISAIT PAS A LES FAIRE SE PARTAGER, ET LE
     DEFAUT ETAIT DEJA LA. Un enfant de flex porte `min-width: auto`, donc il
     refuse de descendre sous la largeur de son contenu : les quatre cases
     (les trois d avant, deja) DEBORDAIENT de leur conteneur - mesure faite,
     de 17 px a 320 px AVANT cette version, de 74 px avec la quatrieme. Et
     `.modal-body` porte `overflow-x: hidden`, donc rien ne se voyait : la
     derniere case etait ROGNEE, pas decalee. « Un debordement qui se voit est
     un bug ; un debordement qu on rogne est un bug qui se tait » (V95).

     ⚠️ CE QUI CEDE EST LE LIBELLE, JAMAIS LA VALEUR : l ellipse est sur
     `.score-src`, et les quatre `.score-row` restent entieres - mesure faite,
     zero debordement interne. C est le meme arbitrage que la borne de 12ch
     (V130) : un libelle tronque reste lisible, une ligne de notes cassee ne
     compare plus rien (V22).

     ⚠️ ET LA BORNE DE 12ch EST RELACHEE ICI, PAS DOUBLEE : au-dessus de
     860 px c est le panneau qui s elargit pour les notes, donc la borne
     protege ; en dessous, la largeur est fixe et c est le partage qui
     protege - une borne en `ch` y volerait de la place a une case qui n en a
     deja plus. */
  .scores .score { min-width: 0; }
  .scores .score-src {
    max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  }

  .meta { align-items: center; }
  .meta-line { flex-direction: column; align-items: center; gap: .85rem; }
  .meta-cell { padding: 0; border-left: 0; text-align: center; }
  /* En colonne, c est `align-items` qui centre horizontalement - pas
     `justify-content`, qui agit sur l axe vertical. */
  #modalStatusText { flex-direction: column; align-items: center; gap: .5rem; }

  .spoiler-resume, .spoiler-role { text-align: justify; }

  .fold-head { justify-content: center; }
  .cast { grid-template-columns: 1fr; }
  .cast li { justify-content: center; }
  .cast-name, .cast-role { text-align: left; }
}


/* ------------------------------- Footer -------------------------------- */

/* ⚠️⚠️ V139 - LE FILET REVIENT, ET C'EST UN RENVERSEMENT DE LA V83.
   Elle l'avait efface avec quatre autres, au titre du menage B5 : « les barres
   de separation horizontales sont effacees, SAUF celle de la ligne des
   onglets ». Le huitieme retour le redemande, et la raison tient - le pied de
   page n'est pas un bloc PARMI les autres, c'est la FIN du document. Les
   quatre autres filets de B5 separaient deux choses de meme rang ; celui-ci
   dit ou le contenu s'arrete.

   ⚠️ ET C'EST LA SEULE LIGNE VISIBLE DE TOUTE LA PARTIE 1. Le reste du socle
   est du retrait de code mort et de l'outillage.

   ⚠️ IL VAUT POUR LES CINQ PAGES : `.site-footer` est le seul bloc de
   `styles.css` que les quatre pages documentaires partagent avec l'accueil -
   c'etait deja l'argument de la V83, dans l'autre sens.
   ⚠️ Les filets propres a `css/pages.css` (en-tete, barre de navigation, date)
   NE SONT PAS touches : ils structurent un DOCUMENT, ils ne separent pas des
   blocs d'interface, et la V83 les avait deja epargnes pour cette raison.

   ⚠️ `--frame-dim` ET NON UNE COULEUR ECRITE A LA MAIN : c'est le raccourci
   de la maison (V44), et il resout sur `--ink-5` - exactement la valeur de
   `--rule`, que porte la ligne des onglets, la seule barre horizontale que B5
   avait gardee. Ecrire un gris ici serait un quatorzieme gris, ce que la
   rampe d'encre existe pour empecher. */
.site-footer {
  border-top: var(--frame-dim);
  padding: 1.75rem 0 2.5rem; text-align: center;
  color: var(--text-dim); font-size: var(--fs-s); line-height: 1.6;
}
.site-footer p { margin: .3rem 0; }
.credits { opacity: .75; font-size: var(--fs-xs); }

/* ------------------------------- Responsive ---------------------------- */

@media (max-width: 820px) {
  /* `--head-h` est MESUREE en JS (voir mesureSticky dans app.js) : les
     valeurs ecrites ici ne sont qu un repli avant le premier calcul. */

  /* Sous 820 px la rangee est DEJA une colonne : le repli en lignes ne
     change donc rien, et le virage devient une liaison verticale de plus. */
  /* ⚠️⚠️ V503 - LE SIGNAL « LA RANGEE EST REPLIEE » DEVIENT UNE VARIABLE.
     `layoutChain` lisait `flex-direction: column` pour savoir qu il ne faut
     rien decouper sous 820 px. Ce n etait pas une declaration d intention,
     c etait un EFFET DE BORD de la regle ci-dessous : elle empile les cartes,
     et le moteur devinait l etage a cet indice. Il suffisait que la
     disposition change de technique - c est exactement ce que fait cette
     version - pour que le moteur se remette a decouper des lignes dans une
     rangee qui n en a plus. Le signal est desormais ECRIT. */
  .chain-line { --rangee-repliee: 1; flex-direction: column; align-items: center; gap: .35rem; padding: .35rem .5rem; }
  /* ⚠️ CES DEUX REGLES SONT SCOPEES A LA RANGEE DEPUIS LA V55. Elles
     accompagnent la colonne ci-dessus : une carte seule sur sa ligne peut
     s elargir, et sa boite de titre n a plus personne avec qui s aligner.
     La GRILLE d archives, elle, garde des lignes de plusieurs cartes - une
     carte a 210 px n y tiendrait qu une par ligne sur un telephone, et des
     boites de titre a hauteur libre y casseraient l alignement des
     pastilles, qui est l invariant de toute ligne de cartes du site.
     Elles ne s appliquaient qu a la rangee avant la V55 : on ecrit ce
     qu elles voulaient dire, on ne pose pas de contre-regle. */
  .roadmap .card { width: min(56vw, 210px); }
  .roadmap .card-title, .roadmap .card-year { height: auto; }
  .drawer-slot { align-self: center; margin: 0; flex-direction: column; align-items: center; }
  .drawer-items { flex-direction: column; align-items: center; margin-top: .35rem; }
  /* ⚠️ EN COLONNE, L'ENCART PERD SA GEOMETRIE DE CADRE DE JALON : il n'y a
     plus d'axe d'affiches a tenir, donc plus rien a compenser. Il garde son
     filet et son aplat, qui sont tout ce qu'il dit. */
  .encart { align-self: center; margin: 0; align-items: center; }
  .encart-body { flex-direction: column; align-items: center; }
  .encart-row { flex-direction: column; align-items: center; }

  .milestone-frame { align-self: center; margin-top: .4rem; margin-bottom: 0; }

  .modal-body { flex-direction: column; align-items: center; }
  .modal-visual { width: 100%; max-width: 240px; }
  .meta, .links { justify-content: center; }
  .modal-info { text-align: center; }
  /* ⚠️ V116 - « .modal-role » est partie : le bloc « timelineRole » a quitte
     la fiche en V6, le champ reste dans les donnees mais plus rien ne le
     rend. */
  .modal-synopsis { text-align: left; }

  /* ⚠️⚠️ V336 (S9, point [43] de la critique) - LA TABLETTE N ETAIT SERVIE PAR
     PERSONNE. Mesure : UNE carte par ligne de 620 a 820 px (768 et 820 px
     relevees), contre trois a 900 px et deux sous 620 px depuis la V319 - le
     pire endroit du site se trouvait etre la taille d un iPad en portrait.
     ⚠️ C EST LA GRILLE DE LA V319, A TROIS COLONNES : une grille posee sur la
     colonne, `flex-direction: column` reste la valeur calculee, donc
     `layoutChain` sort au meme test et AUCUNE ligne de JS ne change.
     ⚠️ La regle des deux colonnes (620 px) vient APRES dans le fichier : a
     specificite egale elle gagne, et c est l ordre voulu - on descend de trois
     a deux, jamais l inverse. */
  /* ⚠️⚠️ V503 - CE N EST PLUS UNE GRILLE A COLONNES, C EST UNE LIGNE QUI SE
     REPLIE (voir le bloc de 620 px, ou le defaut se mesurait). Trois colonnes
     fixes laissaient la derniere carte d une suite impaire dans la PREMIERE
     colonne, avec deux vides a sa droite. */
  .roadmap:not(.is-list) .chain-line {
    display: flex; flex-flow: row wrap;
    justify-content: center; align-items: flex-start;
    column-gap: var(--sp-3); row-gap: var(--sp-4);
    padding: .35rem 0;   /* V431 - plus de flanc : la reserve de chaque cote (V426) part */
  }
  .roadmap:not(.is-list) .chain-line > .card {
    flex: 0 0 auto; width: min(calc(33.333% - var(--sp-3) * 2 / 3), 190px);
  }
  .roadmap:not(.is-list) .chain-line > :not(.card) { flex: 1 1 100%; }
  .roadmap:not(.is-list) .card-title { height: calc(var(--title-fs) * 2.6); }
  .roadmap:not(.is-list) :is(.drawer-items, .encart-row) {
    flex-direction: row; flex-wrap: wrap; justify-content: center;
    gap: var(--sp-4) var(--sp-3);
  }
  .roadmap:not(.is-list) :is(.drawer-items, .encart-row) > .card { width: min(calc(33% - var(--sp-3)), 190px); }
}

/* ⚠️⚠️ V319 - SOUS 620 px, LA RANGEE PASSE SUR DEUX COLONNES (critique UX du
   14 sept. 2026, P1 : a 375 px une carte faisait ~420 px de haut, soit un
   titre et demi par ecran pour le geste du canape - « cocher ce qu on vient
   de voir »). La colonne de 820 px garde sa regle au-dessus de ce seuil.
   ⚠️⚠️ C EST UNE GRILLE POSEE SUR LA COLONNE, PAS UNE RANGEE REDECOUPEE :
   `flex-direction: column` reste la valeur CALCULEE de `.chain-line` (la
   regle de 820 px la pose, `display: grid` l ignore sans l effacer), donc
   `layoutChain` sort toujours au meme test et ne coupe rien. Aucune ligne de
   JS n a change.
   ⚠️ L ALIGNEMENT DES PASTILLES REVIENT : deux cartes sur une ligne
   reprennent la boite de titre calee sur `--title-fs` (la colonne l avait
   rendue a `auto` parce qu une carte seule n avait personne avec qui
   s aligner).
   ⚠️ CE QUI N EST PAS UNE CARTE PREND LES DEUX COLONNES : cadre de jalon,
   tiroir, encart. Leurs cartes s y rangent a leur tour deux par deux.
   ⚠️ RESERVE DE SCENE A GAUCHE (regle ecrite depuis la V110 : « le jour ou
   une rangee remplira la scene, la reponse sera une reserve, jamais un
   deplacement du bouton ») : les trois boutons du flanc font 40 px et sont
   fixes au bord gauche.
   ⚠️ LA VUE COLONNES (`is-list`) N EST PAS TOUCHEE : elle est deja compacte. */
@media (max-width: 620px) {
  /* ⚠️⚠️ V503 - LA GRILLE A COLONNES FIXES DEVIENT UNE LIGNE QUI SE REPLIE
     (retour de l auteur : « corriger l alignement des films »).

     LE DEFAUT, MESURE A 375 px : une suite de cinq cartes suivie d un cadre
     de jalon rangeait *Thor* seule dans la COLONNE DE GAUCHE (gauche 6 px),
     avec 180 px de vide a sa droite, puis posait *Avengers* centre et plus
     large juste en dessous. Une grille a deux colonnes n a aucun moyen de
     savoir qu une case est la derniere de sa suite : elle la met dans la
     colonne 1, point.

     ⚠️⚠️ ET LA PARITE NE SE CALCULE PAS EN CSS. Une rangee n est pas une
     suite continue de cartes : les cadres de jalon, les tiroirs et les
     encarts la coupent, donc chaque suite commence a un rang different -
     `:nth-child` ne peut pas compter a partir d un depart mobile. Une classe
     posee par le moteur l aurait pu, mais elle aurait ete FAUSSE des le
     premier filtre : « une carte filtree ne quitte jamais le DOM », elle
     devient seulement invisible, et la parite du DOM n est pas celle de
     l ecran.
     ⚠️ UNE LIGNE QUI SE REPLIE N A PAS CE PROBLEME : elle ne connait que ce
     qui se VOIT, et elle centre chaque repli, y compris le dernier quand il
     ne porte qu une carte. Ce qui n est pas une carte prend toute la
     largeur et coupe donc la suite - le role que tenait `grid-column: 1/-1`.
     ⚠️ LA LARGEUR GARDE SA SOUSTRACTION DE GOUTTIERE : `min(50%, 170px)`
     aurait donne 320 px de cartes plus 16 px d ecart dans une vitre de
     320 - le debordement que la grille evitait par construction. */
  .roadmap:not(.is-list) .chain-line {
    display: flex; flex-flow: row wrap;
    justify-content: center; align-items: flex-start;
    column-gap: var(--sp-3); row-gap: var(--sp-4);
    padding: .35rem 0;   /* V431 - plus de flanc : la reserve de chaque cote (V426) part */
  }
  .roadmap:not(.is-list) .chain-line > .card {
    flex: 0 0 auto; width: min(calc(50% - var(--sp-3) / 2), 170px);
  }
  /* ⚠️ V426 - LA RANGEE SE CENTRE SUR LA PAGE (demande de l auteur) : la reserve
     du bouton Parcours, fixe au bord gauche, se pose AUSSI a droite. Les cartes
     perdent ~18 px de large a 375 ; elles restent deux par ligne. */
  .roadmap:not(.is-list) .chain-line > :not(.card) { flex: 1 1 100%; }
  .roadmap:not(.is-list) .card-title { height: calc(var(--title-fs) * 2.6); }
  .roadmap:not(.is-list) :is(.drawer-items, .encart-row) {
    flex-direction: row; flex-wrap: wrap; justify-content: center;
    gap: var(--sp-4) var(--sp-3);
  }
  .roadmap:not(.is-list) :is(.drawer-items, .encart-row) > .card { width: min(calc(50% - var(--sp-3) / 2), 170px); }

  /* ⚠️⚠️ V324 - LA CARTE DE DESTINATION S EMPILE (defaut preexistant, mesure
     en V319) : affiche de 150 px + texte de 153 px cote a cote faisaient 319 px,
     dans une scene qui n en offre que ~230 a 320 px - le cadre montait a 349 px
     et debordait de l ecran (absorbe par l `overflow` de la rangee, donc muet).
     ⚠️ LA GRILLE NOMMEE PASSE SUR UNE COLONNE, LE DOM NE BOUGE PAS (regle de la
     V49) : affiche, titre, date, decompte, notes, pastille - centres, comme une
     carte ordinaire. Ce qui distingue une destination reste son CADRE et sa
     date teintee, jamais sa taille (V29).
     ⚠️ L affiche reprend la borne des cartes de la rangee (170 px). */
  .roadmap:not(.is-list) .card--dest {
    --poster-w: min(100%, 170px);
    width: min(100%, 170px);
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "p" "t" "y" "c" "b" "d";
    justify-items: center; text-align: center;
  }
  .roadmap:not(.is-list) .card--dest .poster-wrap { width: 100%; }
  .roadmap:not(.is-list) .card--dest .card-title { align-items: center; text-align: center; margin-top: .4rem; }
  .roadmap:not(.is-list) .card--dest .card-year,
  .roadmap:not(.is-list) .card--dest .card-band { justify-content: center; }
}

@media (max-width: 700px) {
  /* ⚠⚠ V110 (E4) - LE REPLI DE LA TETE DE PAGE EST PARTI AVEC ELLE. Il
     ramenait sa grille a une colonne et y reposait le menu « Progression » ;
     la tete de page n existe plus, et le menu ne vit plus dans aucune
     grille - il est en `position: fixed`, donc il ne se replie pas, il ne
     bouge pas. Une regle CSS dont l objet a disparu ne se laisse pas « au
     cas ou » : le code mort ne fait rien, une regle PERIMEE fait quelque
     chose de FAUX (lecon V59), et celle-ci aurait pose une grille a un
     conteneur absent.
     ⚠️ ET LA MISE EN GARDE QU ELLE PORTAIT RESTE VRAIE AILLEURS : tout ce
     qui se reecrit dans un repli s ecrit `.parent .x`, jamais `.x` - a
     specificite egale (0,1,0), c est l ordre de source qui tranche, et une
     regle qui ne gagne que par la ne gagne pas, elle attend (piege nº 2,
     attrape a la mesure en V52 puis en V59). */
  /* ⚠ V105 (D4) - LE REPLI DU BOUTON SYNCHRONISER EST PARTI AVEC LE BOUTON.
     La V79 lui donnait ici son propre rang (`grid-row: 4`) parce qu il vivait
     dans la tete de page ; il est monte dans la bande figee. Une regle CSS
     dont l objet a disparu ne se laisse pas « au cas ou » : le code mort ne
     fait rien, une regle perimee fait quelque chose de FAUX (lecon V59), et
     celle-ci aurait pose une quatrieme rangee de grille a un enfant absent. */
  /* ⚠ V101 (D3) - LE REPLI DU TITRE EST PARTI AVEC LE TITRE. Il le posait
     sous les cases dans la tete de page ; le titre a quitte la tete de page
     pour la bande figee. Une regle CSS dont l objet a disparu ne se laisse
     pas « au cas ou » : le code mort ne fait rien, une regle perimee fait
     quelque chose de faux (lecon V59). */

  /* ⚠ V102 - le padding redevient symetrique (le filet absolu est parti)
     et le plancher des heures n a plus d objet : elles sont seules sur leur
     rang, donc plus aucun filet a caler. */
  .stat { column-gap: .45rem; padding: .28rem .5rem .32rem; }
  .stat-label { font-size: var(--fs-micro); letter-spacing: .07em; }
  .stat-scope-btn { font-size: var(--fs-xs); letter-spacing: .06em; padding: .3rem .45rem; }

  /* ⚠️ LE REPLI SUIT LA MESURE, ET IL A DEJA ETE FAUX DEUX FOIS (V53, V58).
     Un repli ne sert qu une frame - la mesure JS reprend la main aussitot -
     mais il n en reste pas moins le chiffre qu on lit quand on cherche
     pourquoi une bande se pose au mauvais endroit. */
  :root { --head-h: 124px; }   /* repli : la mesure JS prend le relais */
}

/* ============ LE REPLI DE L EN-TETE FIGE (V77 - B2) ====================
   ⚠️ TOUT S ECRIT `.topbar-inner .x`, JAMAIS `.x` - c est le piege nº 2,
   attrape a la mesure en V52 puis en V59. Les regles de base (`.tabs`,
   `.identity`, `.topbar-tools`) sont a specificite (0,1,0) : une media query
   de meme specificite ne gagnerait que par l ORDRE DE SOURCE, c est-a-dire
   qu elle attendrait le premier composant ajoute plus bas dans la feuille.
   Deux classes (0,2,0) tranchent ou qu on ecrive la regle.

   ⚠️ LE SEUIL EST CALCULE, PAS CHOISI. La rangee d onglets demande ~660 px,
   la colonne de gauche ~270 (bouton + champ a son plancher) et le coin de
   droite ~215 : sous ~1200 px de fenetre, les trois ne tiennent plus sur une
   ligne sans que le champ passe sous son plancher. Les onglets descendent
   alors sur un second rang, pleine largeur - ils se replient, ils ne
   defilent pas (regle V41/V52). */
@media (max-width: 1199px) {
  /* ⚠️⚠️ V408 - UN SEUL RANG ENTRE 621 ET 1199 px : la capsule « Snapline »,
     Filtres et la recherche, le coin de profil. Le compte a rebours et
     Synchroniser sont descendus dans la zone Parcours ; la capsule reprend le
     rang qu ils laissent - la bande passe de 67 a 45 px. */
  .topbar-inner { grid-template-columns: auto minmax(0, 1fr) auto; row-gap: 0; }
  .topbar-inner .tabs { grid-column: 1; grid-row: 1; justify-self: start; }
  .topbar-inner .topbar-tools { grid-column: 2; grid-row: 1; }
  .topbar-inner .identity { grid-column: 3; grid-row: 1; }

  /* Dans le flux (un `absolute` recouvrirait le champ de recherche, V101), a
     la hauteur de la pastille (34 px, la cible du site). */
  .topbar-inner .tab.tab--roadto {
    position: static; transform: none; margin: 0;
    padding: 0 .85rem; font-size: var(--fs-m);
  }
}

/* ⚠️ SOUS 620 px, LA BANDE PORTE TOUJOURS TROIS RANGS - MAIS PLUS LES
   MEMES. La V77 y avait pose (1) Filtres + identite, (2) le champ pleine
   largeur, (3) les onglets : le champ tombait a ZERO pixel a 320 px quand il
   partageait la premiere ligne avec le coin de profil, donc on lui avait
   rendu une ligne ENTIERE. Le retour d usage dit l inverse - un champ large
   comme l ecran ne se lit plus comme un champ, et le bouton Filtres se
   retrouvait a l autre bout de la bande de ce sur quoi il agit.

   ⚠⚠ V141 (2a) - LE COUPLE « FILTRES + RECHERCHE » SE REFORME, ET C EST LE
   COIN DE PROFIL QUI PREND LA LIGNE ENTIERE. Les deux commandes qui agissent
   sur la MEME rangee redeviennent voisines - c est l ordre de la maquette D,
   celui de toutes les largeurs au-dessus du seuil ; le champ perd la largeur
   du bouton et gagne d etre lu comme un champ.
   ⚠️ LE NOMBRE DE RANGS NE BOUGE PAS : on echange le CONTENU de deux rangs,
   on n en ajoute aucun. La bande figee reste a sa mesure (132 px a 375 et a
   320), et c est ce qui rend l echange gratuit - la V77 a passe une version
   entiere a ramener cette bande de 201 px a 45.

   ⚠️ L ESPACEMENT DU COUPLE SE POSE SUR LA GRILLE (column-gap), PAS SUR
   ".topbar-tools". Ce conteneur est en "display: contents" ici : il ne cree
   AUCUNE boite, donc son "gap" ne s applique a rien. Une regle
   ".topbar-tools { gap: .4rem }" vivait sous 560 px depuis la V77 et n a
   jamais espace quoi que ce soit - elle part avec cette version (piege nº 4 :
   une regle sans porteur part en entier).

   ⚠️ "display: contents" SUR LE CONTENEUR, PAS UN SECOND BALISAGE. Le bouton
   et le champ deviennent des cases de la grille sans qu on les sorte de leur
   boite : "#topbarTools" garde son role - c est lui que setTab masque, et
   "[hidden]" (le seul !important du site) bat "display: contents", donc les
   deux disparaissent ensemble, exactement comme au-dessus du seuil. */
/* ⚠️⚠️ V425 - SUR TELEPHONE, L EN-TETE « EN X » (demande de l auteur) : la
   capsule Snapline au CENTRE, sur les deux rangs ; la langue en haut a gauche,
   le profil en haut a droite, la cloche et les amis sous le profil, Filtres et
   la recherche sous la langue. Quatre coins autour d un centre, rien ne se
   touche. Remplace le rang du coin de profil pleine largeur (V141) et la
   capsule en debut de rang 2 (V408).
   ⚠️ `.identity` PASSE EN `display: contents` : ses enfants deviennent des
   cases de la grille. Les menus qui s ancraient sur elle (profil) s ancrent
   donc sur `.topbar-inner`, qui prend `position: relative` - meme bord droit.
   ⚠️ LA RECHERCHE EST DE MOITIE (demande de l auteur) : elle ne prend que le
   reste du coin, borne a 7,5 rem ; des qu on y tape, elle s ouvre sur toute la
   largeur du rang du bas, par-dessus la cloche, et se replie en sortant. */
@media (max-width: 620px) {
  .topbar-inner {
    position: relative;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    grid-template-rows: auto auto;
    column-gap: .5rem; row-gap: .3rem;
  }
  .topbar-inner .tabs { grid-column: 2; grid-row: 1 / 3; align-self: center; justify-self: center; }
  /* ⚠️ V441 - FILTRES ET LA RECHERCHE MONTENT AU RANG DU HAUT, LE DRAPEAU
     DESCEND (retour de l auteur : « inverse recherche/filtres avec le choix de
     la langue »). Le nombre de rangs ne bouge pas : on echange leur contenu. */
  .topbar-inner .topbar-tools {
    display: flex; grid-column: 1; grid-row: 1; justify-self: start;
    gap: .35rem; width: 100%; min-width: 0;
  }
  /* V441 - elle prend tout le coin : le plafond de 7,5 rem laissait un trou */
  .topbar-inner .search { flex: 1 1 auto; min-width: 0; max-width: none; }
  /* repliee, la recherche est au moins une loupe de 34 px ; ses marges
     internes se resserrent (celle de droite gardait la place de la croix) */
  .topbar-inner .search { min-width: var(--pastille); }
  .topbar-inner .search-input { min-width: 0; padding-right: .5rem; }
  .topbar-inner .search:focus-within .search-input { padding-right: 2rem; }
  /* repliee, elle ne montre que sa loupe : un « Titr » coupe ne dit rien */
  .topbar-inner .search:not(:focus-within) .search-input:placeholder-shown::placeholder { color: transparent; }
  /* Filtres : l icone seule, le chevron cede sa place au champ */
  .topbar-inner .fmenu-caret { display: none; }
  /* S inscrire / Se connecter : plus serres, pour ne jamais toucher la capsule */
  .topbar-inner .cta-main { padding: .3rem .6rem; }
  /* le coin de droite fait ~110 px : le chevron cede sa place au pseudonyme */
  .topbar-inner .profile-id { grid-template-columns: auto minmax(0, auto); }
  .topbar-inner .profile-id .profile-caret { display: none; }
  /* V441 - elle s ouvre sur SON rang, qui est celui du haut desormais */
  .topbar-inner .search:focus-within {
    position: absolute; left: 0; right: 0; top: 0; z-index: 6; max-width: none;
  }
  .topbar-inner .identity { display: contents; }
  .topbar-inner .langue--tete { grid-column: 1; grid-row: 2; justify-self: start; }
  .topbar-inner .langue-filet { display: none; }
  .topbar-inner .langue-pop { left: 0; right: auto; }
  .topbar-inner .profile-id { grid-column: 3; grid-row: 1; justify-self: end; max-width: 100%; min-width: 0; }
  .topbar-inner .identity-soc {
    grid-column: 3; grid-row: 2; justify-self: end;
    position: relative; display: flex; align-items: center; gap: .35rem;
  }
  .topbar-inner .identity-cta {
    grid-column: 3; grid-row: 1 / 3; justify-self: end; align-self: center;
    flex-direction: column; align-items: flex-end; gap: .25rem;
  }
  .topbar-inner .tab.tab--roadto { padding: 0 .75rem; font-size: var(--fs-s); }
}
@media (max-width: 360px) {
  .topbar-inner { column-gap: .35rem; }
  .topbar-inner .cta-main, .topbar-inner .cta-sub { letter-spacing: .02em; }
  .topbar-inner .cta-main { padding: .3rem .45rem; }
  .topbar-inner .tab.tab--roadto { padding: 0 .5rem; font-size: var(--fs-xs); letter-spacing: .05em; }
  .topbar-inner .cta-main, .topbar-inner .cta-sub { font-size: .66rem; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ---- V240 - LES REGLAGES DE LISIBILITE (menu Filtres, bloc Affichage) -----
   ⚠️ « ANIMATIONS COUPEES » RECOPIE MOT POUR MOT LA REGLE CI-DESSUS, et elle
   emprunte ses `!important` pour la MEME raison : couper tout mouvement ne
   peut pas dependre de la specificite de chaque animation du site. C est la
   seconde porte vers le meme etat - le systeme OU la personne. */
html.reg-calme *, html.reg-calme *::before, html.reg-calme *::after {
  animation-duration: .01ms !important; animation-iteration-count: 1 !important;
  transition-duration: .01ms !important;
}
html.reg-calme .syncnow-btn.is-spinning { color: var(--doom); border-color: var(--doom); }

/* ⚠️ « TEXTE PLUS GRAND » AGIT SUR LA RACINE : tout ce qui est en `rem` suit
   (texte ET ses marges, donc les proportions tiennent). Les AFFICHES ne
   bougent pas, elles sont en px (`--poster-w`) : c est le texte qu on grossit,
   pas la mise en page.
   ⚠️ LE TITRE DE CARTE GARDE SA TAILLE : il vit dans la largeur FIXE d une
   affiche, et `fitTitles` a un plancher (72 % puis 62 % de chasse) - mesure,
   « L Incroyable Hulk » chevauchait son voisin. `--title-fs` est donc divise
   par le meme facteur, ce qui le ramene a sa valeur d origine. */
html.reg-grand {
  font-size: 112.5%;
  --title-fs: clamp(calc(.92rem / 1.125), 1.15vw, calc(1.06rem / 1.125));
}

/* ⚠️ « CONTRASTE RENFORCE » REMONTE LES DEGRES DE TEXTE ET LES FILETS, il ne
   touche pas aux couleurs de phase ni a l echelle de statut (verrouillees). */
html.reg-contraste {
  --text-2:   #F2F2F4;
  --text-dim: #D6D6DE;
  --ink-7:    #9A9AAA;
  --ink-5:    #4A4A58;
  --ink-6:    #5E5E6E;
}

/* Credit TMDB : le logo doit rester MOINS proeminent que celui du site
   (exigence des conditions d'utilisation de TMDB). */
.tmdb-credit { display: inline-block; vertical-align: -1px; margin-right: .4rem; opacity: .9; }
.tmdb-credit img { display: inline-block; height: 12px; width: auto; }
.tmdb-credit:hover { opacity: 1; }
/* ⚠️ V394 - LE LOGO RESTE A 12 px (obligation TMDB : moins proeminent que le
   site), SA CIBLE PASSE A 34. Mesure : 18 px de haut, la plus petite du
   telephone apres le compteur de route. La zone est invisible, donc le logo
   ne grandit pas d un pixel - l obligation et le plancher tiennent ensemble. */
.tmdb-credit { position: relative; }
.tmdb-credit::after { content: ""; position: absolute; inset: -8px 0; }

/* ⚠️⚠️ V116 - « .road-mark » EST PARTIE, QUATRE-VINGT-DEUX VERSIONS APRES SON
   PORTEUR. La V34 a retire l embleme de la barre de route - il s affichait
   DEUX FOIS, sur la barre et sur la pastille du jalon, qui EST l embleme
   depuis la V29 - et elle a supprime « roadMarksOf() » et la boucle
   « .road-mark[data-mark-for] ». La mise en forme, elle, etait restee.
   ⚠️ « ROAD_MARKS » ET « markglow » NE PARTENT PAS : le dessin sert toujours,
   sur la pastille du jalon. C est la classe du SOMMAIRE qui n a plus de
   porteur, pas l embleme. */

/* ================= ROUTE A DEUX JALONS : LE DEGRADE (V27) =================
   Une route qui passe par un sous-jalon ne porte plus UNE couleur mais un
   TRAJET : elle part de la teinte du premier jalon et arrive a celle du
   dernier. Road to Civil War part du rouge d'Ultron et arrive au bleu-acier
   du bouclier - le titre le dit, les liaisons le font, les cadres le closent.

   ⚠️⚠️ V331 - VISUEL_B (B4) : LE REFLET HOLOGRAPHIQUE (`holo`, sa bande
   blanche qui balayait le titre) ET SON HALO SONT PARTIS, decision de
   l auteur. Le DEGRADE reste, fige : il dit le TRAJET d un jalon a l autre,
   c est une information, pas un effet. */
.phase--gradient .phase-title {
  background-image: linear-gradient(100deg,
    var(--phase-accent-from-soft) 0%, var(--phase-accent-soft) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* Le cadre du sous-jalon prend SA couleur, pas celle du jalon final. */
.phase--gradient .milestone-frame--sub.is-earned {
  border-color: color-mix(in srgb, var(--card-accent, var(--phase-accent-from)) 70%, transparent);
}

@keyframes markglow {
  0%, 100% {
    filter:
      drop-shadow(0 0 4px color-mix(in srgb, var(--phase-accent-soft) 60%, transparent))
      drop-shadow(0 0 11px color-mix(in srgb, var(--phase-accent-soft) 30%, transparent));
  }
  45% {
    filter:
      drop-shadow(0 0 8px color-mix(in srgb, var(--phase-accent-soft) 90%, transparent))
      drop-shadow(0 0 20px color-mix(in srgb, var(--phase-accent-soft) 55%, transparent));
  }
}

/* Statut : le mot lui-meme, avant le bloc spoiler. */
.status-label { font-family: var(--display); font-stretch: var(--ch-ui); font-size: var(--fs-l); }



/* ======================================================================== *
 *  IDENTITE : consentement, accueil, profil, compte (V16)
 *  ----------------------------------------------------------------------
 *  Un seul coin, fige : appel a l'action tant que personne n'est identifie,
 *  profil ensuite. Un seul panneau pour l'accueil, le profil et le compte.
 * ======================================================================== */

/* ⚠️ `.profile-btn` A DISPARU (V59), PASTILLE COMPRISE. La bulle etait un
   `<button>` en gelule - avatar, pseudo et roue sur une ligne - et toute sa
   mise en forme n'existait que pour ca : le fond, le flou, le cadre, le rayon
   `--r-pill`, le survol, l anneau de focus. La bulle n ouvrant plus rien,
   garder la gelule aurait laisse a l ecran l apparence d un bouton sur ce qui
   n en est plus un - la version interface du code mort (piege nº 4).

   ⚠️ V77 (B2) - LA COLONNE DEVIENT UN LOCKUP HORIZONTAL. Elle etait empilee
   parce qu elle occupait le centre d une bande de 152 px ; elle tient
   maintenant le coin d une ligne, ou la hauteur est ce qu on paie a chaque
   pixel de defilement. Bulle, puis pseudo et @handle empiles a sa droite,
   puis la roue.

   ⚠️ C EST UNE GRILLE NOMMEE PAR COLONNES, ET LE DOM SUIT L ORDRE VISUEL.
   La roue a ete DEPLACEE dans le balisage, elle n est pas reordonnee en CSS :
   une inversion visuelle que le document ne porte pas est une inversion que
   le clavier et le lecteur d ecran ne voient pas (V45, V53).

   ⚠️ LA SECONDE RANGEE DISPARAIT TOUTE SEULE quand il n y a pas de handle :
   `[hidden]` le sort du flux, la grille n a plus qu un rang, et le pseudo se
   centre sur la bulle. Rien a declarer pour ce cas. */
/* ⚠️ V78 (B3) - CE N EST PLUS UN CONTENEUR, C EST LE BOUTON. La bulle etait
   une signature posee a cote d une roue cliquable (V59) ; elle redevient LA
   cible - et la roue part EN ENTIER avec sa mise en forme, parce que garder
   son dessin laisserait a l ecran l apparence d une commande qui n existe
   plus (piege nº 4, version interface : le geste de la gelule .profile-btn).

   ⚠️⚠️ IL N A NI BORDURE NI PADDING VERTICAL, ET C EST MESURE, PAS UN GOUT.
   Un cadre de 1 px et .14rem de padding donnaient une bulle de 46,5 px au lieu
   de 40 : la ligne figee passait de 45 a 51,9 px - c est-a-dire que B3 aurait
   repris 7 px de ce que B2 venait d arracher, sur la seule bande qui se paie a
   CHAQUE pixel de defilement (V53). L anneau de l etat ouvert est donc un
   `box-shadow: inset`, qui ne coute rien a la mise en page, et le padding est
   horizontal seul. Mesure apres : ligne a 45 px, comme en V77. */
.profile-id {
  appearance: none; cursor: pointer; text-align: left;
  display: grid;
  grid-template-columns: auto minmax(0, auto) auto;
  align-items: center;
  column-gap: var(--sp-2);
  padding: 0 .3rem 0 .1rem;
  background: transparent; border: 0; border-radius: var(--r-pill);
  transition: background-color .16s, box-shadow .16s;
}
.profile-id:hover { background: var(--ink-2); }
.profile-id:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.profile-id[aria-expanded="true"] {
  background: var(--ink-2);
  box-shadow: inset 0 0 0 1px var(--paper);
}
.profile-id .profile-avatar { grid-column: 1; grid-row: 1 / span 2; }
.profile-id .profile-name   { grid-column: 2; grid-row: 1; align-self: end; }
.profile-id .profile-handle { grid-column: 2; grid-row: 2; align-self: start; }
.profile-id .profile-caret  { grid-column: 3; grid-row: 1 / span 2; }

/* LE CARET : le signe de « Filtres » (V54), pose DANS le bouton. Ce n est pas
   une seconde cible - c est le dessin qui dit qu un menu s ouvre. */
.profile-caret { display: grid; place-items: center; color: var(--text-dim); }
.profile-caret svg { display: block; width: 15px; height: 15px; transition: transform .16s; }
.profile-id[aria-expanded="true"] .profile-caret svg { transform: rotate(180deg); }

/* ------------- LE MENU DE PROFIL (V78 - B3) ------------------------------
   ⚠️ IL EST LE FRERE DE `.fmenu-pop` (V54), DONC IL EN REPREND LES JETONS :
   `--ink-3`, `--frame`, `--r-3`, `--lift-2`. Un popover flotte, c est
   l echelle d elevation de la V44 - pas des valeurs choisies ici.

   ⚠️ ET IL EST EN `position: absolute`, DONC IL NE PESE PAS UN PIXEL dans la
   hauteur de la ligne figee. C est ce qui laisse `--head-h` valoir ce qu elle
   vaut, menu ouvert comme ferme.

   ⚠️ IL S ANCRE A DROITE, contrairement a celui de « Filtres » : la boite
   d identite tient le coin haut-droit (V77). Ancre a gauche, il sortirait de
   la fenetre sur un ecran etroit. */
.pmenu-pop {
  position: absolute; top: calc(100% + .45rem); right: 0; z-index: 4;
  min-width: 210px; padding: .35rem;
  display: flex; flex-direction: column; gap: 1px;
  background: var(--ink-3);
  border: var(--frame); border-radius: var(--r-3);
  box-shadow: var(--lift-2);
}
.pmenu-item {
  appearance: none; cursor: pointer; text-align: left;
  display: flex; align-items: center; gap: .5rem;
  width: 100%; padding: .5rem .58rem;
  background: transparent; border: 0; border-radius: var(--r-2);
  color: var(--text);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-s); letter-spacing: .03em;
  transition: background-color .16s, color .16s;
}
.pmenu-item:hover { background: var(--ink-2); color: var(--paper); }
.pmenu-item:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.pmenu-sep { height: 0; margin: .3rem .2rem; border: 0; border-top: var(--frame-dim); }

/* ⚠️ LE SEUL ROUGE DU MENU, ET IL EST SUR LE DERNIER. « Se deconnecter » est
   le seul geste de la liste qui RETIRE quelque chose : la teinte le dit, le
   filet le separe, et le petit logo le montre. */
.pmenu-item--out { color: var(--brand); }
.pmenu-item--out:hover { background: color-mix(in srgb, var(--brand) 14%, transparent); color: var(--brand); }
.pmenu-ico { display: grid; place-items: center; flex: 0 0 auto; }
.pmenu-ico svg { display: block; width: 17px; height: 17px; }

/* LE PSEUDO SOUS LA BULLE, centre sur son axe - et il grandit avec elle. */
.profile-name {
  max-width: 16ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 800;
  font-size: var(--fs-base); letter-spacing: .01em; line-height: 1.15;
  color: var(--paper);
}

/* ⚠️ V73 - LE @HANDLE SOUS LE PSEUDO, ET IL EST EN ITALIQUE D'ARCHIVO.
   Le pseudo est le NOM, l'identifiant est l'ADRESSE : le contraste se fait
   par l'echelle, la teinte et la pente - jamais par une phrase. C'est la
   regle « titre blanc, annee petite » (V5.1), transposee d'un cran plus bas.

   ⚠️ ET C'EST BIEN ARCHIVO, PAS LITERATA, MALGRE L'ITALIQUE. L'appariement
   de la V44 donne l'interface a Archivo et la prose a Literata : un
   identifiant se REPERE, il ne se lit pas en phrases. L'italique est donc
   synthetique - et la V44 l'ecrit noir sur blanc : « sur une grotesque ca
   passe ; sur un serif, dont l'italique est un dessin a part, c'est le
   defaut herite ». Ici c'est une grotesque.

   ⚠️ SA BORNE EST PLUS LARGE QUE CELLE DU PSEUDO (18ch contre 16), et ce
   n'est pas une inattention : un handle fait au plus 20 signes et il n'y a
   aucun interet a couper une adresse qu'on doit pouvoir dicter. Il est aussi
   plus petit, donc 18ch y prennent moins de place que 16 au-dessus.

   ⚠️ ET IL COUTE 13,4 px DE HAUTEUR FIGEE, MESURES : le HUD passe de 152 a
   165 px avec un profil, la reserve figee totale de 201 a 215. C est le prix
   de la demande, et il se dit - une bande figee se paie a CHAQUE pixel de
   defilement (V53). La marge negative et l interligne serre en rendent 3,5 :
   sans eux la ligne en coutait 16,9. */
.profile-handle {
  max-width: 18ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--display); font-style: italic; font-weight: 500;
  font-size: var(--fs-xs); letter-spacing: .015em; line-height: 1.15;
  color: var(--text-dim);
}

/* ---------------- Identite : DANS le HUD (V21), en grille (V52) ---------- */
/* Elle vivait en `position: fixed` dans le coin de la fenetre, donc elle
   passait SUR le HUD colle en haut des qu'on defilait. Elle est depuis la V21
   un enfant du HUD : figee avec lui, jamais par-dessus lui.

   ⚠️ ELLE N EST PLUS EN `position: absolute` (V52). Elle l etait pour ne pas
   peser sur le centre des trois cases, qui devaient rester centrees sur la
   PAGE. Cette invariante est levee : les cases sont maintenant une COLONNE
   posee a gauche, et l identite occupe une case de la grille - tout l espace
   qui reste a leur droite. Elle s y centre sur DEUX axes, et c est la grille
   qui les tient (voir "L EN-TETE DE PROFIL"). */

/* ⚠️ V77 (B2) - ELLE TIENT LE COIN HAUT-DROIT DE LA LIGNE FIGEE. Elle etait
   centree sur la page (V59) et l essai de la V67 l a nommee comme une gene :
   le centrage disparait ici.

   ⚠️ UNE SEULE BOITE, DEUX CONTENUS - jamais deux boites dont une masquee.
   Deux boites ont deux hauteurs, donc la ligne sauterait au moment precis ou
   l on cree son compte. Le plancher de hauteur le rend impossible PAR
   CONSTRUCTION : c est le geste du panneau d apercu unique de la V52,
   applique a une bande figee. */
/* ⚠️ V78 (B3) - ELLE DEVIENT L ANCRE DU MENU, D OU LE `position: relative`.
   Le popover s y accroche a droite ; sans cette ligne il se calerait sur le
   premier ancetre positionne - la bande figee - et sortirait de l ecran. */
/* V425 - la boite de la cloche et des amis n existe qu en telephone */
.identity-soc { display: contents; }
.identity {
  position: relative;
  grid-column: 3; grid-row: 1; justify-self: end; z-index: 2;
  display: flex; align-items: center; justify-content: flex-end; gap: .5rem;
  /* V441 - le plancher de la V77 suit l echelle de la bande, comme le reste */
  min-height: calc(40px * var(--tete-ech, 1));
}
/* ⚠️ V441 - LE COIN DE PROFIL, LE DRAPEAU ET LA RECHERCHE GRANDISSENT AVEC LA
   BANDE. Tout passe par `--pastille` (donc par `--tete-ech`) : aucun de ces
   nombres n est recopie. */
.topbar-inner .profile-id .profile-avatar {
  width: calc(28px * var(--tete-ech, 1)); height: calc(28px * var(--tete-ech, 1));
}
.topbar-inner .langue-btn { min-height: var(--pastille); }
.topbar-inner .langue-drapeau svg {
  width: calc(24px * var(--tete-ech, 1)); height: calc(16px * var(--tete-ech, 1));
}

/* ⚠️ LES DEUX BOUTONS S EMPILENT DESORMAIS, ET C EST LA V21 QUI SE PERIME.
   « Une pile verticale doublerait la hauteur du HUD » etait vrai quand
   l identite etait une pastille d une seule ligne ; le bloc profil en fait
   deux (pseudo, puis @handle) depuis la V73, donc la pile ne coute RIEN en
   hauteur - et elle rend ~100 px de largeur. ⚠️ Ce sont ces 100 px qui
   effacent le debordement de 43 px releve a 375 px depuis l essai de la V67
   (retour nº 3). */
.identity-cta { display: flex; flex-direction: column; align-items: stretch; gap: .1rem; }

.cta-main, .cta-sub {
  appearance: none; cursor: pointer; width: 100%;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 800;
  letter-spacing: .01em;
  transition: filter .16s, color .16s, border-color .16s;
}
/* ⚠️ V77 - LES DEUX BOUTONS SONT TAILLES POUR QUE LA BOITE FASSE LA MEME
   HAUTEUR QUE LE BLOC PROFIL (40 px, celle de la bulle). Sans ca la ligne
   figee sauterait de quelques pixels au moment precis ou l on cree son
   compte - c est ce que « une seule boite, deux contenus » veut dire. */
.cta-main {
  /* ⚠️ V296 - .35rem et non plus .38rem : les deux boutons passent au plancher
     FONCTIONNEL de 11 px (ils etaient a 10,9 et 10,6), et ce pixel gagne par
     le texte est rendu par le remplissage - sinon le bloc passait a 41 px et
     `--head-h` a 46 (mesure, banc d ecran). */
  padding: .35rem .85rem; font-size: var(--fs-xs);
  /* ⚠️⚠️ V321 - PLUS D APLAT ROUGE NI DE HALO (critique UX, P2 : deux rouges
     pleins dans la meme bande, ROAD TO et S INSCRIRE, se disputaient l oeil).
     ROAD TO reste le SEUL bloc rouge plein (c est le titre) ; S inscrire est
     CERNE. ⚠️ Le trait est un `box-shadow` INSET, jamais une `border` : une
     bordure ajouterait 2 px a la boite de 40 px et `--head-h` passerait a 46
     (le prix deja paye en V296). ⚠️ Le trait est `--brand` (rouge lisible
     sur fond sombre, V230), le texte reste blanc. */
  background: transparent; color: var(--paper);
  border: 0; border-radius: var(--r-pill);
  box-shadow: inset 0 0 0 1.5px var(--brand);
}
/* V321 - le survol se REMPLIT d un voile de la teinte, sans jamais redevenir
   un aplat : le texte blanc reste lisible sur un fond aussi sombre. */
.cta-main:hover { background: color-mix(in srgb, var(--brand-plein) 22%, transparent); }
.cta-sub {
  padding: .1rem .3rem; font-size: var(--fs-xs);
  background: transparent; border: 0;
  color: var(--text-dim); text-decoration: underline; text-underline-offset: 3px;
}
.cta-sub:hover { color: var(--paper); }
.cta-main:focus-visible, .cta-sub:focus-visible { outline: 2px solid var(--paper); outline-offset: 3px; }

/* ⚠️ V227 - LES DEUX BOUTONS SE PARTAGENT LE BLOC DE 40 px, ET LEURS CIBLES
   AUSSI. Mesure : « S inscrire » faisait 24 px de haut, « Se connecter » 13 -
   le second presque impossible au doigt. Les boites ne bougent pas (la bande
   figee ne doit pas sauter, V77) ; leurs ZONES se reglent sur l espace libre :
   - « S inscrire » monte jusqu au bord de l ecran (6 px) et descend jusqu a
     son voisin (1 px) : 31 px, au-dessus du minimum WCAG 2.5.8 ;
   - « Se connecter » descend de 20 px dans le vide sous la bande - mesure :
     ce n est que le conteneur de la rangee, aucun bouton n y vit a cette
     abscisse - et atteint 34 px. */
.cta-main, .cta-sub { position: relative; }
.cta-main::after { content: ""; position: absolute; inset: -6px 0 -1px; }
.cta-sub::after  { content: ""; position: absolute; inset: -1px 0 -20px; }
/* ⚠️ SOUS 620 px LE VIDE DU DESSOUS N EXISTE PLUS : la bande se replie et le
   champ de recherche est a 2 px sous « Se connecter » (mesure a 320 px - la
   zone de 20 px le recouvrait sur 18). Mais a cette largeur le bloc prend
   TOUTE la rangee (280 px) : les deux boutons passent COTE A COTE, et chacun
   dispose alors de la hauteur entiere du bloc (40 px, inchangee - donc
   `--head-h` aussi). Les zones se centrent dans cette hauteur : 34 px chacune,
   sans rien recouvrir. */
/* ⚠️ V416 - ET DES 900 px AUSSI (critique du 18 sept. : la zone de
   « S inscrire » ne faisait que 31 px sur bureau, empilee). Entre 621 et 899,
   la rangee porte la capsule, Filtres, la recherche ET ce bloc : empiles, ils
   laissent sa largeur au champ. */
@media (max-width: 620px), (min-width: 900px) {
  .identity-cta { flex-direction: row; align-items: center; gap: .7rem; }
  .cta-main, .cta-sub { width: auto; }
  /* V394 - -6 et non plus -5 : mesure, la zone faisait 33 px, un de moins que
     le plancher. 23 + 12 = 35, toujours dans le bloc de 40 px, rien recouvert. */
  .cta-main::after { inset: -6px 0; }
  .cta-sub::after  { inset: -11px -4px; }
}

/* L'embleme prend la couleur choisie : lisible a 20 px comme a 42 px.

   ⚠️⚠️ V199 - LE CADRE EST PARTI, ET C EST `--bare` (V62) QUI DEVIENT LE
   DEFAUT. La variante existait depuis la V62 pour l apercu du panneau, avec
   son argument : « deux cercles concentriques autour d un signe deja rond,
   c est une decoration de trop ». Le retour l etend a TOUS les porteurs ; la
   variante n a donc plus rien a distinguer et elle PART EN ENTIER (piege
   nº 4) - avec elle, `border-radius: 50%` et le fond `--ink-2`, qui ne
   servaient qu au cadre.

   ⚠️⚠️ LA COULEUR NE PART PAS AVEC LE CADRE, ET ELLE HERITE DE SA CHARGE.
   C est le cadre qui portait `border-color` ; l embleme, lui, lit deja
   `--avatar-color`. Sans cadre, la couleur devient la SEULE chose qui
   identifie une personne d un coup d oeil - donc le PLANCHER DE CONTRASTE
   (`couleurLisible()`, V117) cesse d etre une precaution et devient la
   garantie. Il est deja pose par `fillAvatar()`, sur TOUS les porteurs.

   ⚠️ LA BOITE NE BOUGE PAS D UN PIXEL - c est le DESSIN qui grandit, et il
   grandit dans le JS, la ou sa taille a toujours ete ecrite (une seule
   autorite). La bulle du HUD vit dans la bande figee : une boite plus large
   aurait fait grandir `--head-h`, mesuree a 45 px depuis la V77. */
.profile-avatar {
  display: grid; place-items: center; flex: 0 0 auto;
  width: 28px; height: 28px;
  border: 0; background: transparent;
  color: var(--avatar-color, var(--brand));
}
/* ⚠️ V201 - `--lg` N A QU UN PORTEUR, l apercu du panneau, et il grandit
   avec la fenetre. */
.profile-avatar--lg { width: 96px; height: 96px; }
/* ⚠️ V77 (B2) - LA BULLE REDESCEND A 40 px, ET `--xl` DISPARAIT AVEC SON
   EMPLOI (piege nº 4). La V59 l avait portee a 72 px parce qu elle etait « la
   signature au centre de la page » ; elle tient maintenant le coin d une
   bande figee, ou 72 px de bulle se paieraient a chaque pixel de defilement.
   Elle redevient une pastille de coin - ce qu elle etait avant la V59. */
.profile-avatar--md { width: 40px; height: 40px; }
/* ⚠ V209 - LA TAILLE DE L EMBLEME DANS LA LIGNE DES NOTES. Elle vaut celle
   d une etoile de fiche (`.score-star`, 21 px) : une case plus haute que ses
   voisines aurait fait grandir tout le rang, et la ligne des notes est le seul
   endroit du site ou c est le PANNEAU qui s elargit pour son contenu (V22). */
/* ⚠ V224 - 21 -> 25 px sur retour d usage (« le logo est assez petit ») ;
   mesure de la ligne a refaire a chaque changement. */
.profile-avatar--score { width: 25px; height: 25px; }
/* ⚠ V224 - TROIS TAILLES DE PLUS, MEME GESTE : un modificateur, jamais une
   `width` sur une classe voisine de meme poids (piege nº 2, V209).
   `--band` vaut une etoile de carte (14 px) a deux pixels pres - le bandeau
   fait une ligne, et un embleme plus haut la ferait grandir sur 142 cartes.
   `--grpline` suit la ligne de 34 px du menu, `--grptete` la tete. */
.profile-avatar--band { width: 16px; height: 16px; }
.profile-avatar--grpline { width: 18px; height: 18px; }
.profile-avatar--grptete { width: 34px; height: 34px; }

/* --------------------------- Consentement ------------------------------- */

.consent {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 90;
  background: color-mix(in srgb, var(--ink-0) 97%, transparent); backdrop-filter: blur(10px);
  border-top: 1px solid var(--rule);
  box-shadow: 0 -14px 40px rgba(0, 0, 0, .8);
  padding: .95rem 0 calc(.95rem + env(safe-area-inset-bottom));
}
.consent-inner { display: flex; flex-direction: column; gap: .7rem; }
.consent-text { margin: 0; font-size: var(--fs-m); line-height: 1.55; color: var(--text); }
.consent-text strong { color: var(--paper); }
.consent-actions { display: flex; flex-wrap: wrap; gap: .5rem; }
.consent-actions .sheet-btn { flex: 0 1 auto; min-width: 168px; }
.consent-note { margin: 0; font-size: var(--fs-xs); line-height: 1.55; color: var(--text-dim); max-width: 78ch; }
.consent-note strong { color: var(--paper); font-weight: 700; }

/* ------------------------------ Le panneau ------------------------------ */

.sheet {
  position: relative; z-index: 1;
  /* ⚠️ V142 (2c) - MEME REPLI "vh" -> "dvh" QUE LA FICHE, ET MEME RAISON :
     ce panneau porte le parcours d inscription, c est-a-dire l ecran ou un
     bas de fenetre hors champ coute le plus cher. */
  /* ⚠️⚠️ V201 - LA FENETRE GRANDIT, ET SES ELEMENTS AVEC ELLE. Le retour
     le demande explicitement pour ce panneau. La borne haute monte de 452 a
     600 px ; la borne basse ne bouge PAS d une ligne - `calc(100vw - 2.2rem)`
     tient toujours l ecran etroit, donc rien a mesurer de ce cote.
     ⚠️ CE QUI GRANDIT EST DECLARE, JAMAIS DEDUIT : le titre, l embleme, le
     champ, les pastilles. Les pastilles passent par `--pastille`, un jeton
     pose ici et lu par CINQ endroits (la pastille, la roue, la barre et les
     deux marges de rentree) - ils portaient chacun leur « 34px » a la main,
     piege nº 6 avec cinq copies. */
  --pastille: 42px;
  width: min(600px, calc(100vw - 2.2rem));
  max-height: 90vh;
  max-height: 90dvh;
  overflow-y: auto; overscroll-behavior: contain;
  padding: 1.7rem 1.9rem 1.5rem;
  background: var(--panel);
  border: var(--frame-dim);   /* V463 - plus de filet de couleur a gauche */
  border-radius: var(--r-3);
  box-shadow: var(--lift-2),
              0 0 34px color-mix(in srgb, var(--phase-accent, var(--brand)) 22%, transparent);
  animation: pop .18s ease-out;
}

/* ⚠️ V201 - LE TITRE ET LA CROIX PARTAGENT UN RANG : c est ce rang qui met
   la croix « en haut a droite », sans une position absolue a tenir d accord
   avec le rembourrage du panneau. */
.sheet-head { display: flex; align-items: flex-start; gap: .9rem; }
.sheet-title {
  margin: 0; color: var(--paper); flex: 1 1 auto; min-width: 0;
  font-family: var(--display); font-stretch: var(--ch-affiche); font-weight: 800;
  letter-spacing: -.008em; font-size: var(--fs-2xl); line-height: 1.1;
}
/* ⚠️ LA CROIX GARDE SON DESSIN ET SA CIBLE (`--pastille`, 34 px depuis la
   V277) ; seule sa POSITION change, parce qu ici le panneau est lui-meme le
   defileur. ⚠️ Le rang fait 45,06 px de haut, commandes par le TITRE : la
   croix n y decide d aucune hauteur (mesure en V277). */
.sheet-head .modal-close { position: static; flex: 0 0 auto; margin-top: -.15rem; }
.sheet-intro { margin: .5rem 0 1.1rem; font-size: var(--fs-s); line-height: 1.55; color: var(--text-dim); }

/* ⚠️ `.facts` / `.fact*` / `.perks*` SONT PARTIS AVEC L'ETAPE 1 DU PANNEAU
   (V68) - les trois chiffres et les trois promesses. La page d'accueil porte
   deja tout ca ; garder leur CSS aurait ete le piege nº 4 en version feuille. */

/* --- Etape 2 : profil ---------------------------------------------------- */

.sheet-preview { display: flex; align-items: center; gap: .75rem; margin-bottom: 1.1rem; }
/* ⚠️⚠️ V200 - C EST UN `<input>` DEPUIS QUE LE CHAMP EST LE VISUEL. Il
   garde la typographie du nom rendu - c est ce qui fait qu on lit une
   IDENTITE et pas un formulaire - mais il garde AUSSI un fond pose et prend
   une bordure au survol et au focus : la lecon de la V152, appliquee avant
   d avoir a la reapprendre. Une valeur qu on peut modifier doit se voir comme
   telle. */
.sheet-preview-name {
  flex: 1 1 auto; min-width: 0;
  padding: .52rem .7rem;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 800;
  font-size: var(--fs-xl); color: var(--paper);
  background: var(--ink-2); border: 1px solid transparent; border-radius: var(--r-2);
  text-overflow: ellipsis;
  transition: border-color .16s;
}
.sheet-preview-name::placeholder { color: var(--text-dim); font-weight: 700; }
.sheet-preview-name:hover { border-color: var(--rule); }
.sheet-preview-name:focus-visible {
  border-color: transparent;
  outline: 2px solid var(--phase-accent, var(--brand)); outline-offset: 1px;
}

/* ⚠️ V336 (S8) - `.sheet-label` PORTE DES `<label for>`, donc du texte
   FONCTIONNEL : c est le SECOND plancher (11 px, V279), pas celui des
   etiquettes (10,5 px). Mesure : 10,56 px avant, `--fs-xs` = 11,04 px apres -
   « un plancher se franchit, il ne se frole pas ». */
.sheet-label {
  display: block; margin: 0 0 .35rem;
  font-family: var(--display); font-size: var(--fs-xs); font-weight: 700; font-stretch: var(--ch-ui);
  letter-spacing: .16em; text-transform: uppercase; color: var(--paper);
}

/* ⚠️ V200 - `.sheet-field` EST PARTIE AVEC SON UNIQUE PORTEUR : le bloc
   qui entourait le champ pseudo, fusionne avec son visuel. Une classe sans
   porteur part en entier (piege nº 4). */

.sheet-picks { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 1rem; }
/* Six par rangee, deux rangees pleines : une grille, pas un retour a la
   ligne au hasard selon la largeur du panneau. */
.sheet-picks--colors { display: grid; grid-template-columns: repeat(6, 1fr); gap: .2rem; justify-items: center; margin-bottom: .6rem; }

.pick {
  appearance: none; cursor: pointer;
  width: 50px; height: 50px; display: grid; place-items: center;
  background: var(--ink-2); border: var(--frame-dim); border-radius: var(--r-2);
  color: var(--swatch, var(--text-dim));
  transition: border-color .16s, transform .16s;
}
.pick svg { width: 30px; height: 30px; }
.pick:hover { transform: translateY(-2px); }
.pick.is-on { border-color: var(--swatch); box-shadow: 0 0 0 1px var(--swatch) inset; }
.pick:focus-visible { outline: 2px solid var(--swatch); outline-offset: 2px; }

/* CHAQUE couleur porte son code. Une teinte qu'on ne peut pas nommer ne peut
   pas etre redemandee ailleurs - et c'est la moitie de l'interet d'avoir une
   couleur a soi. */
/* Largeur libre : la pastille doit occuper UNE colonne de la meme grille que
   la rangee du dessous, sinon la roue et la barre ne tombent pas dessous. */
.swatch { display: flex; flex-direction: column; align-items: center; gap: .16rem; width: 100%; }
.pick--color {
  width: var(--pastille); height: var(--pastille); border-radius: 50%;
  background: var(--swatch); border-color: transparent;
}
.pick--color.is-on { box-shadow: 0 0 0 2px var(--panel) inset, 0 0 0 2px var(--swatch); }
.swatch-hex {
  font-size: var(--fs-micro); letter-spacing: .02em; color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.swatch.is-on .swatch-hex { color: var(--paper); }

/* La ROUE est le selecteur natif : le seul qui soit deja traduit, accessible
   au clavier et adapte au tactile sur chaque plateforme. */
.custom-color { display: flex; align-items: flex-end; gap: .6rem; margin-bottom: 1.15rem; }
/* ⚠️ V116 - « .custom-color-wheel » ET « .custom-color-hex » SONT PARTIES : le
   balisage de la V17 les a remplacees par « .wheel-btn », « .hex-input » et
   « .color-bar », qui sont les seules classes que « index.html » porte
   aujourd hui. Deux jeux de regles pour un seul objet, dont un que rien ne
   selectionne - piege nº 4 en version feuille de style. */

/* --- Etape 3 : compte ---------------------------------------------------- */

/* =================== LE COMPTE SERVEUR (V64, refait en V65) =============
   ⚠️ LA LISTE DE METHODES N'EXISTE PLUS, ET SA MISE EN FORME PART AVEC ELLE.
   `.methods`, `.method`, `.method-ico`, `.method-body` et `.method--wide`
   habillaient DEUX entrees - « cle d'acces » et « lien e-mail ». Il n'en
   reste aucune : le compte se cree par un identifiant et quatre chiffres, le
   lien e-mail est abandonne. Garder une liste a un element aurait laisse a
   l'ecran l'APPARENCE d'un choix sur ce qui n'en est plus un - le geste exact
   de la gelule `.profile-btn` en V59, et le piege nº 4 en version interface.

   ⚠️ `.method-soon` SURVIT PARCE QU'ELLE A D'AUTRES PORTEURS : la pastille
   « Bientot » sert encore au bloc de compte et aux DEUX cartes de
   demonstration du duo et de l'equipe. ⚠️ `.method-note` EST PARTIE (V68) :
   elle ne fermait que l'etape 3 de l'accueil, supprimee avec le compte
   obligatoire. On ne supprime que ce qui n'a plus personne. */

.compte {
  padding: .7rem .8rem;
  border: var(--frame-dim); border-radius: var(--r-2); background: var(--ink-2);
}
.compte-head { display: flex; align-items: center; gap: .7rem; }
/* ⚠️⚠️ V149 - `.compte-ico` ET `.compte-body` SONT PARTIES AVEC LEURS DEUX
   SPANS. Le cadenas et les deux lignes de presentation (« Compte en ligne »,
   « Un identifiant public et quatre chiffres ») REDISAIENT le titre de la
   rubrique et les deux champs qu on voit juste dessous. Une classe sans
   porteur part en entier.
   ⚠️ `.compte-head` RESTE : c est la rangee qui porte `#cptSoon`, la seule
   chose que ce bloc affiche hors ligne ou sans base. **La degradation ne
   part pas avec la decoration.** */

.method-soon {
  flex: 0 0 auto; align-self: flex-start;
  padding: .16rem .42rem; border-radius: var(--r-pill);
  font-family: var(--display); font-size: var(--fs-micro); font-weight: 800;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--tier-bonus); border: 1px solid color-mix(in srgb, var(--tier-bonus) 50%, transparent);
}

/* Le corps du bloc s'ouvre SOUS son en-tete : c'est une seconde rangee, pas
   une colonne qui se dispute la place avec l'icone et le libelle. */
.compte-act { margin-top: .7rem; }

.cpt-form { margin-top: .8rem; }

/* Le « @ » est un signe, pas un champ : il est POSE dans la boite, sur la
   ligne de saisie, exactement comme il se lira ensuite. */
.cpt-at {
  display: flex; align-items: center; gap: .1rem;
  padding: .4rem .6rem; margin-bottom: .7rem;
  background: var(--ink-1); border: var(--frame-dim); border-radius: var(--r-2);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-base); color: var(--text-dim);
}
.cpt-at:focus-within { border-color: color-mix(in srgb, var(--brand) 60%, transparent); }
.cpt-at input {
  flex: 1 1 auto; min-width: 0;
  background: none; border: 0; outline: none; padding: 0;
  font: inherit; color: var(--paper); text-transform: lowercase;
}

/* ⚠️ LE CODE NE S'ETALE PAS SUR LA LARGEUR DU BLOC. Quatre chiffres dans un
   champ de 660 px se liraient comme une phrase ; borne a 7 signes de chasse,
   la boite dit d'elle-meme combien on attend. Les deux champs de la creation
   se posent cote a cote - le second REDEMANDE le premier, et deux boites
   voisines sont ce qui rend la comparaison evidente. */
.cpt-pins { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-bottom: .7rem; }
.cpt-pin {
  flex: 0 0 auto; width: 9ch;
  padding: .4rem .6rem;
  background: var(--ink-1); border: var(--frame-dim); border-radius: var(--r-2);
  outline: none;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-l); letter-spacing: .34em; text-align: center;
  color: var(--paper);
}
/* ⚠️ LA BOITE PASSE DE 7ch A 9ch, ET C'EST UNE MESURE, PAS UN GOUT. Le
   placeholder ne dit plus « •••• » mais « 4 chiffres » : une consigne lue
   avant de cliquer est une consigne oubliee en cliquant. Dix signes ne
   tiennent pas dans sept, et un placeholder rogne ne dit rien. Neuf reste
   loin du champ-phrase que la V65 refusait - la boite continue d'annoncer
   d'elle-meme qu'on attend peu.
   ⚠️ ET LE PLACEHOLDER PERD L'INTERLETTRAGE DU CODE : il est du TEXTE, pas
   des chiffres alignes. */
.cpt-pin::placeholder {
  color: var(--ink-7); letter-spacing: 0; font-size: var(--fs-xs); font-weight: 600;
}
.cpt-pin:focus-visible { border-color: color-mix(in srgb, var(--brand) 60%, transparent); }

/* ⚠️ L'OEIL - CIBLE DE 34 px, celle du coeur de favori (V6) et de la roue du
   profil (V59). En dessous c'est impossible au doigt, et c'est deja ecrit
   trois fois dans CLAUDE.md.
   ⚠️ LA BARRE OBLIQUE EST L'ETAT « MASQUE », donc l'etat de DEPART : elle
   s'efface quand le code est a l'ecran. Un oeil barre veut dire « c'est
   cache », pas « cliquer pour cacher ». */
.cpt-eye {
  flex: 0 0 auto;
  width: 34px; height: 34px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; cursor: pointer;
  color: var(--text-dim);
  transition: color .18s ease;
}
.cpt-eye svg { width: 20px; height: 20px; display: block; }
.cpt-eye:hover { color: var(--paper); }
.cpt-eye:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: var(--r-1); }
.cpt-eye[aria-pressed="true"] { color: var(--brand); }
.cpt-eye[aria-pressed="true"] .eye-bar { opacity: 0; }

/* Le handle se lit a voix haute et se donne : meme traitement que le code de
   compte (V48), pour la meme raison. */
.cpt-who {
  margin: 0 0 .15rem;
  font-family: var(--display); font-stretch: var(--ch-affiche); font-weight: 800;
  font-size: var(--fs-xl); letter-spacing: .04em; color: var(--paper);
  user-select: all; -webkit-user-select: all;
}

/* Se deconnecter n'est pas une action de meme rang que les deux au-dessus :
   c'est un lien, pas un bouton plein. */
.cpt-out {
  margin-top: .7rem; padding: 0;
  background: none; border: 0; cursor: pointer;
  font-family: var(--display); font-size: var(--fs-xs); letter-spacing: .02em;
  color: var(--text-dim); text-decoration: underline; text-underline-offset: 3px;
}
.cpt-out:hover { color: var(--brand); }
.cpt-out:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* ⚠️ LA GOUTTIERE EST A DROITE DU PREMIER, PAS A GAUCHE DU SECOND. Un
   `margin-left` sur `.cpt-out + .cpt-out` marchait tant que les deux tenaient
   sur une ligne ; sous 375 px ils passent a la ligne, et le second se
   retrouvait alors RENTRE de 17,6 px sous le premier - mesure. Un decalage
   qui n'existe qu'a une largeur donnee est le pire genre de reglage. */
.cpt-out { margin-right: 1.1rem; }
/* ⚠️ V78 (B3) - « SUPPRIMER MON COMPTE » VIT MAINTENANT DANS L ONGLET, et il
   y est SEUL de son rang : il n a plus de voisin dont il faille l ecarter, donc
   la gouttiere de droite ne lui sert plus a rien. Le filet du dessus le detache
   de la porte du compte - un geste irreversible ne se colle pas au bouton qui
   le precede. */
.account-danger {
  margin-top: 1.4rem; padding-top: 1.1rem;
  border-top: var(--frame-dim);
}
.account-danger .cpt-out { margin-top: 0; margin-right: 0; }

.cpt-out--danger { color: var(--brand); }
.cpt-out--danger:hover { color: var(--paper); }

/* ================= LE VOCABULAIRE DE SIGNES (V212 - partie 5, V205) =======
   La legende du retour n est pas une liste de trois icones, c est un
   VOCABULAIRE : la coche verte dit TOUS les « Accepter », la croix rouge dit
   TOUS les refus ET toutes les suppressions, le « + » dit « inviter dans un
   groupe ». Il vaut sur QUATRE ecrans - la liste d amis, la cloche, l ecran du
   groupe, le panneau d invitation - et c est pour ca que le trace vit en UN
   exemplaire dans js/profile.js (`MCU_PROFILE.signe`) et l habillage en UNE
   regle ici.

   ⚠⚠ `.sig` NEUTRALISE CE QUE `.cpt-out` ET `.nmenu-acte` DONNAIENT A DU
   TEXTE, ELLE NE LES REMPLACE PAS. Les deux portent encore leur focus, leur
   etat desactive et leurs marges de liste ; un troisieme bouton de base aurait
   ete un dessin de plus a tenir d accord (V45, V85). C est pourquoi elle est
   ecrite APRES eux dans la feuille : elle vaut (0,1,0) comme `.cpt-out`, et a
   egalite l ordre de source tranche - ici il tranche DANS LE BON SENS, et cette
   ligne est la pour qu on ne deplace pas le bloc sans le savoir (piege nº 2).

   ⚠ 34 px DE COTE : c est le plancher tactile du site - le coeur (V6), la roue
   (V59), le bouton Progression (V82), la cloche (V166). Un signe seul n y fait
   pas exception ; c est meme le cas ou il mord le plus, parce qu un mot large
   donnait la cible que le dessin ne donne plus.
   ⚠ ET LES DEUX SIGNES RESTENT COTE A COTE, PAS EMPILES : empiles, il leur
   faudrait 68 px de haut dans une ligne qui en fait 32 - donc soit une ligne
   deux fois plus haute pour 67 lignes d amis, soit deux cibles sous le
   plancher. Le retour laisse le choix ouvert (« la ligne grandit, ou les deux
   signes se posent cote a cote ») ; c est le second, et il se mesure a 320 px.
   ========================================================================= */
.sig {
  display: inline-grid; place-items: center;
  width: 34px; height: 34px; min-height: 34px; padding: 0;
  margin: 0; border-radius: var(--r-2);
  text-decoration: none; letter-spacing: normal;
  transition: color .16s, background-color .16s, border-color .16s;
}
.sig svg { width: 21px; height: 21px; display: block; }
/* ⚠ LE FOND N ARRIVE QU AU SURVOL, comme `.ruban-bulle` et `.emblem-btn`
   depuis la V199 : au repos le signe EST la commande, il n a pas besoin d une
   surface sous lui pour se lire - il est deja le seul objet colore de sa
   cellule. */
.sig:hover { background: var(--ink-3); }
/* ⚠ LE VERT EST `--doom`, deja celui de `--tier-must` et de ce qui est acquis
   (V203) ; LE ROUGE EST `--brand`, le rouge general du site. AUCUNE septieme
   famille de couleur : le retour demande deux signes, pas deux teintes neuves. */
.sig--oui { color: var(--doom); }
.sig--oui:hover { color: var(--doom); }
.sig--non { color: var(--brand); }
.sig--non:hover { color: var(--brand); }
.sig--plus { color: var(--text-2); }
.sig--plus:hover { color: var(--paper); }
/* ⚠ LA GOUTTIERE DE `.cpt-out` NE VAUT PLUS : elle ecartait deux MOTS, et
   `.amis-actions` porte deja son `gap`. Une marge de plus ferait deriver les
   deux signes hors de la colonne d actions. */
.amis-actions .sig { margin-right: 0; }
/* ⚠ ET DANS LA CLOCHE, `.nmenu-acte` GARDE SA BOITE : seul son remplissage
   change. Le `padding` horizontal du texte laisserait une cible de 58 px de
   large pour un dessin de 21. */
.nmenu-acte.sig { padding: 0; }


/* Le temps d'un aller-retour reseau, les boutons ne doivent ni repondre ni
   pretendre le contraire. */
.compte.is-busy .compte-act { opacity: .55; pointer-events: none; }

/* ⚠️ V67 - LA CONNEXION EN DEUX ETAPES N'EST QU'UN MASQUAGE. Les deux
   etapes sont deux blocs freres ; il n'y a ni transition, ni hauteur figee,
   ni position. Une etape qui se glisserait sur l'autre demanderait de tenir
   une hauteur commune a la main, c'est-a-dire le piege nº 6. */
.cpt-step { display: block; }
.cpt-who--sm { margin: 0 0 .55rem; font-size: var(--fs-m); }

/* ⚠️ V73 - LES DEUX REPLIS DE MODIFICATION. Presque rien : `.fold`,
   `.fold-head` et `.fold-sign` sont GLOBAUX depuis la fiche (V20), et
   `.cpt-at`, `.cpt-pins`, `.cpt-pin`, `.cpt-eye`, `.sheet-label` et
   `.sheet-hint` servent deja aux deux formulaires d'a cote. Il ne reste
   qu'un filet au-dessus du premier et un peu d'air - c'est exactement ce qui
   avait rendu gratuit le demenagement de l'onglet Compte en V61.

   ⚠️ LE FILET DU HAUT VIENT DE `.fold:first-of-type`, ET IL TOMBE ICI AU
   BON ENDROIT : il separe « qui je suis » de « ce que je peux changer ». */
.cpt-fold { margin-top: .2rem; }
.cpt-change { padding: .1rem 0 .9rem; }

/* ⚠️ LE BOUTON D'ACTION NE PREND PAS TOUTE LA LARGEUR. `.sync-actions`
   etale ses boutons a deux ; seul, celui-ci s'etirerait sur les 400 px du
   panneau pour un geste qu'on fait une fois par an. */
.cpt-change .sync-actions { margin-top: .2rem; }
.cpt-change .sync-actions .sync-btn { flex: 0 1 auto; }

/* ⚠️ V113 - LE SEUL `.cpt-out` DU SITE QUI AIT UN SUCCESSEUR, ET IL NE LUI
   LAISSAIT QUE 1 px. Mesure : 22,38 px AU-DESSUS du lien (la marge basse de
   `.cpt-pins` PLUS sa propre marge haute, qui se cumulent), 1 px EN DESSOUS -
   et ce pixel n'est meme pas une marge, c'est le fond de la boite de ligne
   d'un `inline-block`. L'intitule NOUVEAU CODE ouvre pourtant la seconde
   moitie du formulaire : il etait colle a ce qui ferme la premiere.

   ⚠️ LA VALEUR EST CELLE DU FORMULAIRE, PAS UN NOMBRE ACCORDE A LA MAIN :
   chaque `.sheet-label` d'ici est precede d'un bloc a `.7rem` de marge basse
   (`.cpt-pins`, `.cpt-rescue`). Le lien devient le troisieme, il n'invente
   rien. Le 1 px de boite de ligne s'ajoute par-dessus et ne se compte pas.

   ⚠️ ET C'EST L'ESPACE DONNE AU PREDECESSEUR, PAS UNE MARGE HAUTE POSEE
   SUR L'INTITULE - c'est le modele que la V108 a etabli en retirant le
   `margin-top` negatif de `.sheet-hint`.

   ⚠️⚠️ ELLE EST SCOPEE A `.cpt-change`, ET CE N'EST PAS DE LA PRUDENCE. Les
   trois autres `.cpt-out` du site sont DERNIERS de leur boite - `#cptForgot`,
   `#cptDel`, `#duoRompre` - plus ceux qu'`amis.js` et `duo.js` fabriquent, ou
   `.amis-actions .cpt-out` remet deja les marges a zero. Une marge basse
   GLOBALE y aurait pose `.7rem` de RESERVE VIDE derriere le dernier enfant,
   c'est-a-dire le padding vide que la V110 a passe une version a retirer - et
   il aurait fallu l'annuler a trois endroits, soit TROIS exceptions pour une
   correction. `.cpt-change` en demande UNE.

   ⚠️ ELLE AJOUTE, ELLE NE REPARE PAS UN DEFAUT, ET C'EST LA CATEGORIE QUE LA
   V108 A GARDEE (elle citait `.amis-vis .sheet-hint`, parti en V148 avec son
   balisage ; l'exception, elle, reste de la meme famille). Son test - « une
   regle locale qui
   rend une valeur a son defaut est le signe que le defaut est faux » - ne mord
   pas ici : la marge basse de `.cpt-out` vaut zero PARCE QU'IL EST DERNIER
   PARTOUT AILLEURS.

   ⚠️ ET ELLE EST SCOPEE PLUTOT QU'ADJACENTE (`.cpt-out + .sheet-label`) : une
   regle de voisinage cesse de s'appliquer EN SILENCE le jour ou quelqu'un
   glisse un noeud entre les deux. (0,2,0), donc elle gagne par specificite et
   non par ordre de source - piege nº 2. */
.cpt-change .cpt-out { margin-bottom: .7rem; }

/* ================== LE CODE DE SECOURS (V74) ==========================
   ⚠️ SA BOITE N'EST PAS CELLE DU CODE A QUATRE CHIFFRES, ET C'EST VOULU.
   `.cpt-pin` est bornee a 9ch parce qu'elle dit d'elle-meme qu'on attend
   peu ; onze signes tirets compris n'y tiennent pas, et un champ rogne ne
   dit rien (c'est la mesure de la V66, dans l'autre sens). Elle reste
   BORNEE quand meme - 15ch, pas la largeur du bloc - pour la meme raison
   qu'elle : un champ etale sur 660 px se lit comme une phrase.

   ⚠️ ET ELLE PORTE LA MEME MATIERE QUE TOUT LE RESTE DU BLOC : le creux de
   la rampe, le filet sourd, le rayon de panneau. Rien de neuf a decider. */
.cpt-rescue {
  display: block; width: 15ch; max-width: 100%;
  padding: .4rem .6rem; margin-bottom: .7rem;
  background: var(--ink-1); border: var(--frame-dim); border-radius: var(--r-2);
  outline: none;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-l); letter-spacing: .01em; text-align: center;
  color: var(--paper);
}
.cpt-rescue::placeholder {
  color: var(--ink-7); letter-spacing: .06em; font-size: var(--fs-s); font-weight: 600;
}
.cpt-rescue:focus-visible { border-color: color-mix(in srgb, var(--brand) 60%, transparent); }

/* ⚠️ L'ECRAN DU CODE FRAIS N'A PAS DE CADRE, ET CE N'EST PAS UN OUBLI. Il
   est SEUL dans le bloc pendant qu'il est la - il ne cohabite avec rien,
   donc il n'y a rien dont il faille le detacher. Un cadre autour de la seule
   chose a l'ecran est de la decoration. */
.cpt-fresh { padding: .1rem 0 .2rem; }
/* ⚠️ V149 - L ECRAN DU CODE FRAIS PORTE LE MEME `.account-code`, MAIS SUR UN
   `<p>`, PAS DANS UN BOUTON. Il n a donc pas le padding du bouton : la marge
   haute que `.account-code` portait jusqu ici (`.5rem`) lui revient, la ou
   elle sert. **Une classe partagee ne doit pas dependre du TYPE de l element
   qui la porte** (lecon de `.amis-jauge-fill`, V88) - ce qui depend du
   contexte se scope au contexte. */
.cpt-fresh .account-code { margin: .5rem 0 .2rem; }
.cpt-fresh .sync-actions .sync-btn { flex: 0 1 auto; }


/* ------------------------------- Actions -------------------------------- */

.sheet-actions { display: flex; gap: .55rem; margin-top: 1.1rem; }
.sheet-btn {
  appearance: none; cursor: pointer; flex: 1 1 0;
  padding: .62rem .8rem;
  background: transparent; color: var(--text-dim);
  border: var(--frame-dim); border-radius: var(--r-2);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-xs); letter-spacing: .01em;
  transition: color .16s, border-color .16s, background .16s;
}
.sheet-btn:hover { color: var(--paper); border-color: var(--paper); }
.sheet-btn--primary {
  background: var(--phase-accent, var(--brand)); border-color: transparent; color: var(--on-light);
}
.sheet-btn--primary:hover { color: var(--on-light); filter: brightness(1.1); }

/* ⚠️ LE REPLI D IDENTITE DE LA V21 EST SUPPRIME (V52), ET IL FAUT DIRE
   POURQUOI : sous 1120 px, l identite repassait dans le flux, prenait sa
   propre ligne au-dessus des cases (`order: -1`) et s alignait a DROITE.
   Ces quatre declarations ne parlaient que de l ancienne rangee flex - il n y
   a plus ni `position: absolute` a defaire, ni ordre a inverser.

   ⚠️ ET IL NE FAISAIT PAS QUE DORMIR : `justify-content: flex-end` gagnait
   sur le `center` de `.identity`, donc entre 700 et 1120 px la pastille
   restait collee au bord droit au lieu de se centrer dans l espace qui lui
   revient. Mesure a 900 px : centre a 782 px au lieu de 535. Une regle qu on
   croit morte parce que son objet a disparu continue de s appliquer.

   Le repli vit desormais a 700 px, dans le bloc de l en-tete : la grille
   passe a une colonne et l identite descend sous la legende. */

@media (max-width: 560px) {
  .sheet-actions { flex-direction: column; }
  .consent-actions .sheet-btn { flex: 1 1 100%; min-width: 0; }
}

/* La banniere de consentement n'appartient a aucune phase : son bouton
   principal prend le ROUGE DE MARQUE, pas l'accent courant. */
.consent .sheet-btn--primary { background: var(--brand-plein); color: #FFFFFF; border-color: transparent; }
.consent .sheet-btn--primary:hover { color: #FFFFFF; }

/* ---- Profil : emblème-bouton, titre, palette conventionnelle (V17) ------ */

/* ⚠️ V199 - `.profile-avatar--bare` EST PARTIE : son argument (« deux
   cercles concentriques autour d un signe deja rond ») vaut desormais pour
   TOUS les porteurs, donc elle n a plus rien a distinguer. Une classe sans
   objet part en entier, ici comme ailleurs (piege nº 4). L apercu du panneau
   reprend la taille de `--lg`, qu elle annulait par un `width: auto`. */

/* ⚠️⚠️ V199 - LE CADRE DE L EMBLEME-BOUTON PART AUSSI, ET C EST LE SEUL
   ENDROIT OU LA QUESTION SE POSAIT. Ailleurs l embleme est un signe ; ici il
   est une COMMANDE, et la V152 dit qu on ne depouille pas une commande au
   point qu elle cesse de se lire comme telle. Ce qui la sauve est qu elle
   porte deja son affordance AILLEURS QUE DANS SON CADRE : le chevron de
   `.emblem-btn-hint` dit « ceci ouvre une liste », et il reste. Le fond
   revient au survol, comme sur `.ruban-bulle`.
   ⚠️ LA BOITE GRANDIT AVEC LE DESSIN (58 -> 66 px) : le dessin passe a 50 px
   et le chevron mord son coin. Une boite de 58 px l aurait rogne.
   ⚠️⚠️ V201 - ET ELLE PASSE A 104 px, DESSIN A 88 : le retour demande que
   « l embleme soit plus large que le pseudonyme ». Pris a la lettre dans un
   rang, cela donnerait un champ plus etroit que 104 px, c est-a-dire
   inutilisable ; ce qui est demande est une DOMINANCE, et c est elle qu on
   livre - l embleme est desormais l element le plus grand du panneau, et le
   nom se lit a cote de lui. */
.emblem-btn {
  appearance: none; cursor: pointer; position: relative;
  display: grid; place-items: center; flex: 0 0 auto;
  width: 104px; height: 104px; padding: 0;
  background: transparent; border: 0; border-radius: var(--r-3);
  color: inherit;
  transition: background-color .16s;
}
.emblem-btn:hover { background: var(--ink-2); }
.emblem-btn:focus-visible { outline: 2px solid var(--phase-accent, var(--brand)); outline-offset: 3px; }

/* ⚠️⚠️ V201 - LE PANNEAU AGRANDI SE REPLIE SOUS 400 px, ET C EST UNE
   MESURE, PAS UNE PRECAUTION. Six pastilles de 42 px plus leurs cinq
   gouttieres font 268 px ; a 320 px la colonne de contenu n en offre que 224,
   donc la sixieme sortait du panneau - et sans que rien ne casse, parce que
   `.sheet` ne defile qu en HAUTEUR (releve : scrollWidth 298 pour un
   clientWidth de 281). Le repli rend au jeton sa valeur de partout, et a
   l embleme sa taille d avant. Le seuil est calcule : il faut 268 px de
   contenu, donc 268 + 35,2 + 60,8 = 364 px de fenetre - 400 laisse la marge.
   ⚠️ LE DESSIN SUIT SA BOITE PAR LA CSS, comme `.ruban-bulle svg` (V199) :
   sa taille de base est ecrite dans `js/profile.js`, ceci est une surcharge
   responsive et non un second jeu de valeurs. */
@media (max-width: 400px) {
  .sheet { --pastille: 34px; }
  .sheet-title { font-size: var(--fs-xl); }
  .emblem-btn { width: 76px; height: 76px; }
  /* ⚠️ PAS `.emblem-btn svg` : ce selecteur (0,1,1) attraperait AUSSI le
     chevron de `.emblem-btn-hint svg`, de MEME specificite - a egalite, seul
     l ordre de source les departagerait, et une regle qui ne gagne que par
     ordre de source ne gagne pas, elle attend (piege nº 2). */
  .emblem-btn > .profile-avatar svg { width: 62px; height: 62px; }
  .profile-avatar--lg { width: 68px; height: 68px; }
}
.emblem-btn-hint {
  position: absolute; right: -2px; bottom: -2px;
  width: 22px; height: 22px; display: grid; place-items: center;
  background: var(--panel); border: var(--frame-dim); border-radius: 50%;
  color: var(--text-dim);
}
.emblem-btn-hint svg { width: 15px; height: 15px; }
.emblem-btn[aria-expanded="true"] .emblem-btn-hint { transform: rotate(180deg); }

/* La liste flotte : elle ne doit pas pousser le reste du panneau vers le bas
   a chaque ouverture, sinon le pseudo saute sous le doigt. */
.sheet-preview { position: relative; }
.emblem-pop {
  position: absolute; top: calc(100% + .45rem); left: 0; z-index: 5;
  display: flex; gap: .4rem; padding: .45rem;
  background: var(--ink-1); border: var(--frame-dim); border-radius: var(--r-2);
  box-shadow: var(--lift-1);
}

/* ⚠️ V200 - `.sheet-preview-id` ET `.sheet-preview-name.is-empty` SONT
   PARTIES : la premiere empilait un nom et rien d autre, la seconde disait
   « vide » sur un affichage qui n existe plus. C est le `::placeholder` du
   champ qui porte desormais cet etat, et il le porte par construction. */
.field-error {
  margin: -.7rem 0 1rem;
  font-size: var(--fs-xs); line-height: 1.45; color: var(--brand);
}

/* La rangee sous la palette reprend EXACTEMENT ses six colonnes : la roue
   tombe sous la premiere pastille, la barre s'arrete au bord de la derniere. */
.custom-color {
  display: grid; grid-template-columns: repeat(6, 1fr);
  align-items: center; gap: .45rem .2rem; margin-bottom: 1.15rem;
}

/* Icone de roue chromatique : le degrade conique est porte par le LABEL, et
   l'input natif est pose dessus, transparent. On garde donc le selecteur du
   systeme (traduit, accessible) avec l'icone qu'il faut. */
.wheel-btn {
  position: relative; justify-self: center; cursor: pointer;
  width: var(--pastille); height: var(--pastille); border-radius: 50%;
  background:
    radial-gradient(closest-side, var(--panel) 34%, transparent 35%),
    conic-gradient(#FF0000, #FFFF00, #00FF00, #00FFFF, #0000FF, #FF00FF, #FF0000);
  box-shadow: 0 0 0 1px var(--ink-5);
}
.wheel-btn input[type="color"] {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer; border: 0; padding: 0;
}
.wheel-btn:focus-within { box-shadow: 0 0 0 2px var(--paper); }

/* ⚠️⚠️ V188 - LE `#` A QUITTE LE CHAMP POUR LA BOITE QUI L ENTOURE, ET
   C EST LA SEULE FORME QUI TIENT. Le retour demande qu il « ne puisse pas
   etre supprime » : un caractere qu on empeche d effacer est un caractere
   qu on empeche mal - il reste une selection, un collage, un Ctrl+A pour
   l emporter, et chacun de ces chemins demanderait sa parade. Un caractere
   qui n est PAS dans le champ ne peut pas en etre efface du tout.
   ⚠️ LE CADRE PASSE SUR `.hex-field`, ET L ANNEAU DE FOCUS AVEC LUI
   (`:focus-within`) : laisse sur le champ, il aurait dessine un second cadre
   A L INTERIEUR du premier.
   ⚠️ ET LE GROUPE RESTE CENTRE : c est la BOITE qui centre, pas le texte -
   un `text-align: center` sur le champ aurait fait flotter les six signes
   loin de leur diese. */
.hex-field {
  grid-column: span 2; min-width: 0;
  display: flex; align-items: center; justify-content: center; gap: .04rem;
  padding: .42rem .3rem;
  background: var(--ink-2);
  border: var(--frame-dim); border-radius: var(--r-2);
  font-family: var(--body); font-size: var(--fs-s); line-height: 1.2;
}
.hex-field::before { content: "#"; color: var(--text-dim); }
.hex-field:focus-within { outline: 2px solid var(--phase-accent, var(--brand)); outline-offset: 1px; }
/* ⚠️ L ECRITURE EST BLANCHE (`--text`), pas sourde : c est une valeur qu on
   relit, pas une legende. Le diese, lui, reste sourd - il n est pas saisi. */
.hex-input {
  width: 6.6ch; min-width: 0; flex: 0 0 auto;
  padding: 0; margin: 0;
  background: transparent; color: var(--text);
  border: 0; border-radius: 0;
  font: inherit; text-align: left;
  text-transform: uppercase; font-variant-numeric: tabular-nums;
}
.hex-input:focus-visible { outline: none; }

/* Barre de rendu : elle ne fait que MONTRER la couleur retenue, on ne peut
   pas cliquer dessus - c'est la roue et le code qui la commandent. */
.color-bar {
  grid-column: span 3; height: var(--pastille); border-radius: var(--r-2);
  border: 1px solid var(--ink-5); pointer-events: none;
}

/* Les pastilles sont des cercles CENTRES dans leur colonne : le champ et la
   barre doivent donc rentrer d'une demi-gouttiere pour tomber pile sous le
   bord des couleurs. `100% / n` vaut exactement une colonne, la barre en
   couvrant n. */
/* ⚠️⚠️ V201 - « 34px » ETAIT ECRIT CINQ FOIS, ET LES CINQ DEVAIENT
   RESTER EGALES : la pastille, la roue, la hauteur de la barre et ces deux
   rentrees. Le piege nº 6 mot pour mot - une regle ecrite est le symptome
   d une variable manquante. Le jeton vaut 34 px partout (`:root`) et 42 px
   dans le panneau, qui vient de grandir : le menu de couleur de la barre,
   qui reemploie `.custom-color` dans un popover etroit, garde donc SA
   taille sans une seule regle de plus. */
.hex-field { margin-left: calc(((100% - .2rem) / 2 - var(--pastille)) / 2); }
.color-bar { margin-right: calc(((100% - .4rem) / 3 - var(--pastille)) / 2); }

/* ⚠️ `.profile-btn-id` EST PARTI AVEC LA GELULE (V59) : il n existait que
   pour empiler le pseudo a DROITE de l avatar. Le pseudo est maintenant sous
   la bulle, et c'est `.profile-id` qui empile. */
/* ======================================================================== *
 *  V18 - pastille de profil agrandie, avertissement anonyme, onglets,
 *  favoris ordonnes, progression, apercu duo / equipe
 * ======================================================================== */

/* ⚠️ `.profile-gear` A DISPARU AVEC LA ROUE (V78, B3), comme la gelule
   `.profile-btn` avait disparu avec le bouton qu elle habillait (V59). Le
   dessin d une commande qui n existe plus est du code mort qu on VOIT. */

/* -------------------- Avertissement "sans compte" ----------------------- */

.anon-warn {
  /* ⚠ V101 (D3) - IL SE POSE SOUS LE GRAND BOUTON, PAS SOUS LA BANDE SEULE.
     Le bouton pend de `--roadto-drop` et vit au `z-index` de la bande (40),
     donc il aurait recouvert le haut de cet avertissement - qui est justement
     ce qu on ne doit pas cacher. */
  position: sticky; top: calc(var(--head-h)); z-index: 35;
  background: color-mix(in srgb, var(--tier-bonus) 14%, var(--ink-1));
  border-top: 1px solid color-mix(in srgb, var(--tier-bonus) 45%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--tier-bonus) 45%, transparent);
  padding: .6rem 0;
}
.anon-warn-inner { display: flex; align-items: center; gap: .7rem; }
.anon-warn-ico { flex: 0 0 auto; color: var(--tier-bonus); }
.anon-warn-ico svg { width: 22px; height: 22px; display: block; }
.anon-warn-text { flex: 1 1 auto; margin: 0; font-size: var(--fs-s); line-height: 1.45; color: var(--text); }
.anon-warn-text strong { color: var(--paper); }
.anon-warn-cta {
  flex: 0 0 auto; appearance: none; cursor: pointer;
  padding: .45rem .9rem; border-radius: var(--r-pill); border: 0;
  background: var(--tier-bonus); color: var(--on-light);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 800;
  font-size: var(--fs-xs); letter-spacing: .01em;
}
.anon-warn-cta:hover { filter: brightness(1.1); }
.anon-warn-close {
  flex: 0 0 auto; appearance: none; cursor: pointer;
  background: transparent; border: 0; color: var(--text-dim);
  font-size: var(--fs-xl); line-height: 1; padding: 0 .2rem;
}
.anon-warn-close:hover { color: var(--paper); }

/* ⚠️ « .sheet-tabs » EST PARTI AVEC LA BARRE D ONGLETS (V62). Le panneau
   MON PROFIL n a plus qu un contenu - pseudo, embleme, couleur - donc plus
   rien a choisir. Une mise en forme d onglets sans onglets laisserait a
   l ecran l apparence d un mecanisme qui n existe plus, le piege nº 4 en
   version interface, comme la gelule « .profile-btn » en V59.
   ⚠️⚠️ V116 - « .tab-note » PART A SON TOUR, ET SA RAISON DE RESTER EST TOMBEE
   EN V96. La V62 l avait gardee comme « phrase d ouverture des explications de
   duo et d equipe, qui ont simplement change d ecran » ; la V96 a supprime le
   cercle a point d interrogation ET ces explications - expliquer le duo a cote
   de l ecran qui le FAIT serait redire ce qui est deja a l ecran (V41). Le
   dernier porteur est parti avec elles. */

/* ⚠️ « .fav-list », « .fav-row », « .fav-rank », « .fav-poster »,
   « .fav-body », « .fav-tools » et « .fav-move » SONT PARTIS AVEC L ONGLET
   FAVORIS DU PANNEAU (V62). Le classement se lit et se reordonne sur le
   PODIUM de la rangee principale depuis la V60, avec ses propres regles.
   ⚠️ A NE PAS CONFONDRE AVEC « .fav-btn » : c est le coeur pose sur
   l affiche, et il ne bouge pas. */

/* ⚠️ « .prog-* » EST PARTI AVEC L'ONGLET BIBLIOTHEQUE (V48) - c'etait sa
   barre de progression, et son seul usage. La progression du parcours se lit
   dans le HUD, en haut de chaque page ; la redire dans un panneau, c'etait
   deux endroits a tenir d'accord sur le meme chiffre. */

/* ================ LE CODE DE SECOURS, A L ECRAN (V48, V74, V149) ========
   Neuf signes qu on relit a voix haute : ils sont donc poses GROS, espaces,
   et dans la police d affichage.
   ⚠️ LA COMPARAISON D ORIGINE A DISPARU AVEC SON TERME : la V48 opposait ce
   traitement au « champ gris du code de progression, un bloc qu on copie
   sans jamais le lire ». Ce champ n existe plus (V147). **Ce qui reste vrai
   est la regle, pas l exemple** : ce qui se LIT et ce qui se COPIE ne se
   presentent pas pareil - c est la meme distinction que le titre blanc et
   l annee sourde d une carte (V5.1). */
/* ⚠️⚠️ V149/V152 - LE BOUTON EST LE CODE, ET IL PORTE MAINTENANT UN CADRE.
   La V149 l'avait depouille de toute apparence de bouton pour ne laisser
   QUE le code ; l'auteur demande explicitement un encadrement, pour que le
   bloc se lise comme un objet a part et non comme du texte flottant sous
   le titre. Il reprend le meme cadre que les autres surfaces cliquables du
   site - fond POSE (`--ink-2`), bordure fine, coins arrondis - avec une
   gouttiere interne qui empeche le code de toucher le cadre.
   ⚠️ IL GARDE SES DEUX AFFORDANCES ORIGINALES : le curseur, et un anneau de
   focus visible. Un bouton sans etat de focus est un bouton que le clavier
   perd. */
.account-code-btn {
  display: block; width: 100%;
  /* ⚠️ 34 px, LA CIBLE DU SITE. Mesure sans cadre a 320 px, le bouton
     faisait 31 px de haut, sous le plancher tactile du cœur, de la roue et
     du bouton Progression. Avec le cadre et sa gouttiere interne il reste
     mesure a chaque livraison. */
  min-height: 34px;
  margin: 0; padding: .6rem .8rem;
  background: var(--ink-2);
  border: var(--frame-dim); border-radius: var(--r-2);
  text-align: left;
  cursor: pointer; color: inherit; font: inherit;
  transition: border-color .16s ease, background .16s ease;
}
.account-code-btn:hover { border-color: var(--paper); background: var(--ink-3); }
.account-code-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* ⚠️ LE SURVOL COLORE AUSSI LE CODE, EN PLUS DU CADRE : deux signaux plutot
   qu un, sur un bouton qui donne acces a un secret. */
.account-code-btn:hover .account-code { color: var(--brand); }

/* ================== LE PROFIL PUBLIC (V266) ==============================
   ⚠️ LA RUBRIQUE EMPRUNTE `.account` (le motif filet + marge des trois autres
   blocs de la section, V152) : un quatrieme ecartement aurait rendu la
   section irreguliere.
   ⚠️ L INTERRUPTEUR PORTE `.rduo-switch` EN PLUS DE LA SIENNE, il ne se
   redessine pas : c est le seul coulissant du site, et un second trace aurait
   diverge au premier reglage (piege nº 6, geste de `.amis-item` reemploye par
   trois fichiers). Seule la LIGNE qui le porte est locale - celle du duo vit
   dans un popover de 20 rem, celle-ci dans une colonne de 44 rem. */
.cpt-switch-ligne {
  display: flex; align-items: center; gap: .8rem;
  margin: 0 0 .6rem;
}
.cpt-switch-terme {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-xs); letter-spacing: .01em;
  color: var(--text-2);
}
.cpt-switch { flex: 0 0 auto; }
/* V417 - le second reglage ne se colle plus au lien du profil public */
.cpt-switch-ligne.cpt-partage { margin-top: var(--sp-5); }
/* ⚠️ L ADRESSE EST PLUS LONGUE QU UN CODE DE NEUF SIGNES : elle descend d un
   cran, perd l interlettrage et CASSE plutot que de deborder. Un lien rogne
   est un lien qu on recopie faux.
   ⚠️⚠️ ET LA REGLE EST EN (0,2,0), JAMAIS `.cpt-pub-url` SEULE : `.account-code`
   vaut la meme specificite et vit PLUS BAS dans la feuille - a egalite, c est
   l ordre de source qui tranche, donc la borne n aurait pas gagne, elle aurait
   attendu (piege nº 2, paye en V45, V52, V59). */
.account-public .account-code {
  font-size: var(--fs-m); letter-spacing: .02em;
  overflow-wrap: anywhere;
}
.account-public .account-code-btn { margin-top: .2rem; }

.account-code {
  display: block; margin: 0;
  font-family: var(--display); font-stretch: var(--ch-affiche); font-weight: 800;
  font-size: var(--fs-xl); letter-spacing: .14em;
  color: var(--paper);
  transition: color .18s ease;
  /* Selectionnable au doigt : c'est un identifiant qu'on donne. */
  user-select: all; -webkit-user-select: all;
}

/* ⚠️ LA CONSIGNE EST EN LITERATA, PAS EN ARCHIVO, ET C EST LA REGLE DE
   L APPARIEMENT : elle se LIT EN PHRASES. L italique vient du `<em>` du
   balisage - une vraie fonte italique, pas une oblique synthetique. */
.account-secours {
  margin: .5rem 0 0;
  font-family: var(--body); font-size: var(--fs-s); line-height: 1.55;
  color: var(--text-dim);
}
/* ⚠️ V152 - « Ne l'envoyez a personne » DEVIENT UN SECOND PARAGRAPHE,
   PAS LA SUITE DU MEME. Il tombait auparavant a la ligne par un simple
   effet de largeur, jamais par construction : sur un ecran assez large,
   les deux phrases se seraient suivies sur une seule ligne. Deux `<p>`
   distincts le garantissent, avec un ecart plus serre entre eux qu avec le
   bouton au-dessus : les deux forment UN groupe de lecture. */
.account-secours + .account-secours { margin-top: .2rem; }
/* ⚠️⚠️ V152 - LE FILET QUI MANQUAIT ENTRE « MODIFIER INFORMATIONS » ET LE
   CODE DE SECOURS. Les trois blocs de cette section (Modifier informations,
   Code de secours, Supprimer mon compte) doivent se lire comme TROIS
   rubriques distinctes ; `.account-modif` et `.account-danger` portaient
   deja le meme motif (filet + marge + interligne) pour se detacher de ce
   qui les precede - `.account` en etait le seul a ne pas le porter, donc le
   code de secours restait COLLE au formulaire au-dessus de lui.
   ⚠️ MEME MOTIF, MEMES VALEURS QUE `.account-modif` : un troisieme filet
   avec un autre ecartement aurait rendu la section irreguliere - trois
   rubriques, un seul rythme. */
.account {
  margin-top: clamp(1.6rem, 4vw, 2.6rem);
  padding-top: clamp(1.1rem, 3vw, 1.7rem);
  border-top: var(--frame-dim);
  margin-bottom: 1.1rem;
}
/* ⚠️ V149 - `.account .sync-actions` EST PARTIE : le bloc du code de secours
   n a plus de rangee de boutons, le code EST le bouton. */

/* --------------------------- Duo et equipe ------------------------------ */

/* ⚠️ V132 - LES CINQ « .duo-demo* » SONT PARTIES, ELLES AUSSI. Elles
   montraient ce qui n existait pas encore ; l etoile du duo existe, et
   montrer en apercu ce que l ecran FAIT est la faute de l onglet Collection
   (V41). ⚠️ Et « .duo-demo-note » cesse d attendre sa couleur : elle etait
   jaune depuis la V63 faute d ecran pour la porter - la vraie etoile prend
   « --star-duo », la couleur du profil du partenaire, passee par le
   plancher de contraste (V117). */


/* ⚠️⚠️ V179 - LA COURONNE EST UNE FORME, JAMAIS UN LOGO REPRIS, et elle
   prend la couleur du mot par `currentColor` : une seconde valeur ici, et le
   jour ou l accent du groupe change, la couronne resterait sur l ancien
   (piege nº 6). Sa taille se cale sur le corps du mot en `em`, donc elle
   suit la pastille sans un second reglage. */
.equipe-couronne { width: 1.05em; height: .8em; flex: 0 0 auto; }

/* ⚠️ V214 - LES DEUX CLASSES DE LA DEVISE SONT PARTIES - celle du champ et
   celle de son rendu sur la carte : « une classe sans porteur part en entier » (piege nº 4).
   `verif-selecteurs.js` le confirme.

   ================= CREER UN GROUPE (V214 - partie 5, V207) ================
   ⚠️ LE BOUTON GARDE SON LIBELLE, contrairement aux signes de la V212 : ceux-la
   vivent dans une ligne dont on sait deja de qui elle parle, celui-ci n a aucun
   voisin qui le desambiguise. Un dessin seul y serait une devinette.
   ⚠️ IL EST CENTRE ET SEUL SUR SON RANG - c est le rang 2 de la maquette, et
   il tiendra la meme place que « Ajouter un membre » en V215. */
.equipe-creer-btn {
  appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: .55rem;
  min-height: 40px; padding: .4rem .95rem;
  background: var(--ink-2); color: var(--text-2);
  border: var(--frame-dim); border-radius: var(--r-2);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-s); letter-spacing: .04em;
  transition: color .16s, border-color .16s, background-color .16s;
}
.equipe-creer-btn:hover { color: var(--paper); border-color: var(--paper); background: var(--ink-3); }
.equipe-creer-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.equipe-creer-ico { display: grid; place-items: center; }
.equipe-creer-ico svg { width: 30px; height: 24px; display: block; }
/* ⚠️ LE PIED EST CENTRE, ET C EST UNE DEMANDE EXPLICITE (« un bouton *Creer*
   centre »). Il ne reprend pas la rangee du panneau de profil, qui aligne son
   bouton a droite : deux ecrans, deux intentions. */
.equipe-creer-pied { display: flex; justify-content: center; margin-top: 1.1rem; }
/* ⚠⚠ LA FENETRE N A PRESQUE RIEN A DECLARER, ET C EST LE POINT : elle
   reemploie `.sheet` (largeur, fond, rayon, defilement), `.sheet-head`,
   `.modal-close`, `.sheet-preview` avec son `position: relative`,
   `.sheet-preview-name`, `.emblem-btn`, `.emblem-pop`, `.custom-color` et
   `.sheet-btn`. Un second habillage aurait diverge au premier reglage.
   ⚠️ LE BOUTON *Creer* NE S ETIRE PAS : `.sheet-btn` porte `flex: 1 1 0`
   parce que le panneau de profil l aligne dans un rang de deux ; seul ici, il
   prendrait toute la largeur - et le retour demande un bouton CENTRE, pas une
   barre. */
.equipe-creer-pied .sheet-btn { flex: 0 0 auto; min-width: 11rem; }

/* ================ LES TROIS FENETRES DE LA COUCHE SOCIALE (V219) ==========
   « Tout est trop compact et pas lisible », « il y a des chevauchements » -
   retour du 11 septembre 2026. Trois causes, mesurees avant d ecrire :

   1. « Ajouter un ami » et « Ajouter un membre » posaient leur contenu
      DIRECTEMENT dans `.modal-panel`, qui n a aucun rembourrage : c est
      `.modal-body` qui le porte sur la fiche. Le titre touchait le cadre et
      la croix, en `position: absolute`, recouvrait « Envoyer ». Elles gagnent
      `.modal-body` et le rang `.sheet-head` : la croix est dans le FLUX, donc
      elle ne recouvre plus rien PAR CONSTRUCTION.
   2. « Creer un groupe » posait un `.sheet` (600 px, bordure, ombre) DANS un
      `.modal-panel` borne a 600 px : deux cadres pour une fenetre, et le
      second debordait du premier de la largeur de ses filets.
   3. Sa rangee de couleur heritait de la GRILLE A SIX COLONNES du panneau de
      profil et de ses deux RENTREES en demi-gouttiere, calculees pour tomber
      sous les six pastilles (V17). Ici il n y a pas de pastilles : la roue
      tombait sous l embleme et le code se serrait dans deux colonnes. Elle
      redevient une rangee - la roue, le code, la barre qui prend le reste.
   ⚠️ LES RENTREES SONT ANNULEES PAR (0,2,0), JAMAIS PAR ORDRE DE SOURCE
   (piege nº 2). */
.amis-win .sheet-head { margin-bottom: 1.1rem; }
.amis-win .modal-panel > .sheet { width: auto; border: 0; box-shadow: none; animation: none; }
.custom-color.equipe-couleur { display: flex; align-items: center; gap: .7rem; margin-bottom: 0; }
.equipe-couleur .hex-field { margin: 0; flex: 0 0 auto; padding-inline: .65rem; }
.equipe-couleur .color-bar { margin: 0; flex: 1 1 auto; }

/* ⚠️ LES DEUX FENETRES « AJOUTER » GRANDISSENT AU CORPS DU TITRE DE « Creer un
   groupe » (`.sheet-title`, 1,5 rem) : trois fenetres de la meme couche qui
   n auraient pas la meme echelle se liraient comme trois sites. Le champ et le
   bouton passent a 48 px - au-dessus du plancher tactile, a la hauteur d un
   champ qu on remplit et non d une etiquette. */
.amis-ajout-win .modal-body { padding: 1.6rem 1.8rem 1.8rem; margin: 0; }
.amis-ajout-win .sheet-hint { margin: -.35rem 0 1.1rem; font-size: var(--fs-m); }
.amis-ajout-win .amis-addline { gap: .7rem; }
/* V371 - la liste d amis de « Ajouter un membre », au-dessus de la barre */
.amis-ajout-win .amis-list { margin: 0 0 1rem; max-height: 40vh; overflow-y: auto; }
.amis-ajout-win .amis-addline .cpt-at { padding: .7rem .9rem; font-size: var(--fs-l); }
.amis-ajout-win .amis-addline .sheet-btn { min-height: 48px; min-width: 9rem; }
.amis-ajout-win .sync-msg { margin-top: .9rem; }

/* ⚠️ LE FILET SEPARE *Dissoudre* DE CE QUI ENREGISTRE : un geste irreversible
   ne se colle pas au bouton qui sauvegarde - c est le motif de la section
   Compte (V78, V152), reemploye. */
.equipe-danger {
  margin-top: 1.1rem; padding-top: .9rem;
  border-top: 1px solid var(--rule);
  display: flex; justify-content: center;
}
.equipe-danger .cpt-out { margin: 0; }
.equipe-couleur { margin-top: .6rem; }

/* ⚠️ V222 - LES REGLES DE LA BIBLIOTHEQUE PARTAGEE SONT PARTIES AVEC ELLE
   (decision de l auteur). Une classe sans porteur part en entier (piege nº 4). */

/* V373 - l ancien ecran du groupe (V133 -> V371) est parti avec ses regles :
   carte, tete, roue, classement, rang, pourcentage, partage, avertissement. */

/* ⚠️⚠️ V116 - « .route-switch » ET « .route-opt » SONT PARTIES, ET CELLES-LA
   SONT DU CODE MORT « EN PREPARATION » : elles dessinaient le selecteur
   « Route solo | Route duo » de C2c, qui n a JAMAIS eu de balisage. C est
   exactement ce que le piege nº 4 interdit - « ne jamais ecrire de code mort
   en preparation » - et la meme faute que le champ « account », reste a
   « null » de la V19 a la V48, et que le champ « route » que la V96 a refuse
   d ecrire pour cette raison. ⚠️ C2c les redessinera avec son ecran, quand
   ses deux renversements auront ete tranches. */

/* ⚠️⚠️ V133 - LES « .team-demo* » ET LES TROIS « .demo-dot--* » SONT PARTIES
   AVEC LEUR PORTEUR : l ecran des equipes EXISTE. Une demonstration laissee
   a cote de ce qu elle annonce serait la faute de l onglet Collection
   (V41), et une classe sans porteur laisse a l ecran l apparence d un
   mecanisme qui n existe plus - piege nº 4 (V59, V97, V116, V132).
   ⚠️ « .team-card* » RESTE, ET ELLE CHANGE D EMPLOI SANS CHANGER DE FORME :
   elle habillait la carte d ANNONCE d une equipe, elle habille desormais
   une equipe REELLE. C est la regle inverse - une classe garde sa place
   tant qu elle a un porteur (.soon-flag, V96 ; .account-note, gardee en V97
   pour son dernier porteur - et partie en V149 quand il a disparu). */


/* Le code des couleurs doit se LIRE : c'etait 7,7 px, c'est trop petit pour
   un code qu'on est cense pouvoir recopier. */
.swatch-hex { font-size: var(--fs-micro); letter-spacing: -.01em; }

@media (max-width: 700px) {
  .anon-warn-inner { flex-wrap: wrap; }
  .anon-warn-text { flex: 1 1 100%; order: 2; }
  .anon-warn-cta { order: 3; }
  .swatch-hex { font-size: var(--fs-micro); }
}

/* La liste d'emblemes s'enroule : neuf signes en ligne debordaient du
   panneau. Cinq par rangee, largeur bornee. */
/* ⚠️ LA LISTE PASSE EN CATEGORIES DEPLIANTES (V40). Elle n'est donc plus une
   grille de boutons mais une COLONNE de <details> : `display: block`, et la
   grille descend d'un cran dans `.emblem-grid`. Sans hauteur maximale, six
   categories ouvertes depassaient la fenetre - la liste flotte en absolu,
   elle ne pousse rien, donc rien ne l'aurait rattrapee. */
.emblem-pop {
  display: block; flex-wrap: initial;
  width: max-content; max-width: min(300px, calc(100vw - 3.4rem));
  max-height: min(58vh, 400px); overflow-y: auto;
  padding: .1rem .55rem .3rem;
}
/* ⚠️⚠️ V83 - CES DEUX REGLES NE DESSINAIENT DEJA RIEN, ET LES RETIRER EST CE
   QUI EMPECHE LE MENAGE DE B5 DE SE RETOURNER. Une categorie d'emblemes est
   un `.fold`, et `.fold + .fold { border-top: 0 }` - deux classes, donc
   (0,2,0) - battait `.emblem-cat` (0,1,0) : aucune categorie n'a jamais
   porte ce filet. En supprimant la regle des rubriques, on l'aurait donc
   ALLUME, c'est-a-dire ajoute des barres horizontales dans la version qui
   les efface, SANS qu'aucune erreur ne se produise. C'est le piege nº 2 pris
   par l'autre bout : une regle qui ne gagnait que par specificite disparait
   avec son adversaire. */
.emblem-cat > .fold-head { padding: .5rem 0; gap: .5rem; }
.emblem-cat-name { flex: 1 1 auto; }
/* Le compte se lit SANS deplier : c'est lui qui dit ou il reste a prendre.
   Meme geste que le compte des categories de la Collection (V19). */
.emblem-cat-count {
  margin-left: auto; color: var(--text-dim); letter-spacing: .06em;
  font-variant-numeric: tabular-nums;
}
.emblem-grid {
  display: flex; flex-wrap: wrap; gap: .4rem;
  padding: 0 0 .55rem;
}

/* ======================================================================== *
 *  V19 - Bibliotheque, Collection en deux categories, surnoms de duo
 * ======================================================================== */

/* ------------------------- Onglet Collection ---------------------------- */
/* ⚠️ « .lib-* » EST PARTI AVEC L'ONGLET BIBLIOTHEQUE (V48). */

/* --------------------------- Surnoms de duo ----------------------------- */
/* ⚠️ V116 - LES CINQ CLASSES « .duo-name* » SONT PARTIES AVEC LEUR
   DEMONSTRATION. La V96 l a retiree en livrant le surnom : « montrer en apercu
   ce que l ecran fait trois blocs plus haut aurait ete la faute de l onglet
   Collection » (V41). Le balisage est parti, la mise en forme etait restee -
   piege nº 4, dans le sens que la V97 a corrige pour « .archive-note ». */

/* Les quatre rubriques se REPARTISSENT la hauteur restante de la colonne :
   fermee, la fiche se termine donc exactement avec l'affiche, sans la reserve
   vide qu'on avait dessous. Des qu'une rubrique s'ouvre il n'y a plus d'espace
   a distribuer : la regle s'annule d'elle-meme et la fenetre grandit. */
.folds {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column; justify-content: space-between;
}

/* ======================================================================== *
 *  V54 - BARRE D'OUTILS : "Filtres" en menu ouvrable, et la recherche
 * ======================================================================== */

/* ======================= LA BARRE D'OUTILS (V54) =========================
   ⚠️ ELLE NE SE COUPE PLUS EN DEUX : ELLE TIENT EN UNE LIGNE.

   La V41 refusait de figer une barre de 90 px, la V43 l'a coupee en deux et
   n'en a fige que la premiere ligne - 50 px, la recherche et les vues. Le
   partage suivait l'usage : ce qu'on manie EN LISANT reste sous la main, ce
   qu'on pose UNE FOIS vit dans le flux.

   ⚠️ LA V54 GARDE LA REGLE ET CHANGE LA CONCLUSION. Ce qui doit rester sous
   la main, c'est l'ACCES a la commande, pas la commande DEPLIEE : un bouton
   fige qui ouvre un menu tient les deux bouts. Ce qui partait, c'etait une
   rangee de six pastilles posee en permanence sous les yeux de quelqu'un
   qui, neuf fois sur dix, ne filtre rien.

   ⚠️ V77 (B2) - SA BANDE FIGEE N'EXISTE PLUS. Le bouton et le champ ont
   rejoint la ligne des onglets, a gauche : `.toolbar-stick`,
   `.toolbar-stick-inner` et la variable `--tools-h` qui la mesurait sont
   SUPPRIMES. Ce qui part, part en entier (piege nº 4) - une bande dont la
   mise en forme survit a son contenu laisse a l'ecran l'apparence d'un objet
   qui n'existe plus. ⚠️ ET `--tools-h` N'A PAS ETE REMPLACEE PAR UN
   LITTERAL dans les sommes qui la comptaient : c'est le piege nº 6.

   ⚠️ CE QUI NE CHANGE PAS : le menu reste en `position: absolute`, donc il ne
   pese pas un pixel dans la hauteur de la bande - et le compte des filtres
   actifs reste sur le bouton, seule chose qui empeche un filtre oublie de
   devenir muet. */

/* Le flux ne porte plus que le compte de resultats, masque le reste du
   temps : le conteneur ne doit donc rien reserver quand il est vide. */
.toolbar { margin: 0; }

/* ------------------------------ Recherche -------------------------------- */

/* ⚠️ ELLE S ETIRE DANS SA COLONNE ET ELLE SAIT SE RESSERRER (V77). Elle
   vivait dans une rangee flex centree, bornee a 420 px ; elle occupe
   desormais ce qui reste de la premiere colonne de l'en-tete, a cote du
   bouton "Filtres". Le plancher tombe de 200 a 120 px : la colonne vaut la
   moitie de ce que les onglets laissent, et un plancher trop haut aurait
   fait deborder la ligne au lieu de la faire respirer. */
.search {
  /* V441 - 232 px : la recherche se resserre encore d un cran (retour de
     l auteur). C est un plafond, pas une largeur - elle reste elastique. */
  position: relative; flex: 1 1 auto; max-width: 232px; min-width: 120px;
  display: flex; align-items: center;
}
.search-ico {
  position: absolute; left: .6rem; display: grid; place-items: center;
  color: var(--text-dim); pointer-events: none;
}
.search-ico svg { width: 17px; height: 17px; }
.search-input {
  /* V394 - 33 px mesures : le plancher de 34 se franchit, il ne se frole pas.
     V441 - et c est le JETON, pas le nombre : dans la bande il vaut 34 fois
     l echelle, partout ailleurs il vaut 34. */
  min-height: var(--pastille);
  width: 100%; padding: .5rem 2rem .5rem 2.1rem;
  background: var(--ink-1); color: var(--text);
  border: var(--frame-dim); border-radius: var(--r-pill);
  font-family: var(--body); font-size: var(--fs-m);
}
.search-input::placeholder { color: var(--text-dim); }
.search-input:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 1px; border-color: transparent;
}
/* La croix native de type="search" est masquee : elle n'existe que sur
   certains navigateurs, donc elle rendait le champ different d'un poste a
   l'autre. La notre est toujours la, ou jamais. */
.search-input::-webkit-search-cancel-button { display: none; }
.search-clear {
  position: absolute; right: .45rem;
  appearance: none; cursor: pointer; border: 0; background: transparent;
  width: 24px; height: 24px; border-radius: var(--r-pill);
  color: var(--text-dim); font-size: var(--fs-l); line-height: 1;
}
.search-clear:hover { color: var(--paper); }

/* --------------------- LE MENU "FILTRES" (V54) ---------------------------
   ⚠️ LE BOUTON PORTE LE COMPTE DES FILTRES ACTIFS, et ce n'est pas une
   decoration. Un menu referme rend un filtre actif INVISIBLE : c'est la
   seule information que la rangee de pastilles donnait gratuitement, et un
   filtre qu'on a oublie d'enlever est precisement la panne muette que ce
   projet traque depuis le piege n° 5. Le compte la rend a nouveau visible,
   et il coute une pastille. */

.fmenu { position: relative; flex: 0 0 auto; }

.fmenu-btn {
  appearance: none; cursor: pointer;
  display: flex; align-items: center; gap: .4rem;
  min-height: var(--pastille);   /* V441 - il suit la bande, comme le champ */
  padding: 0 .55rem;
  background: var(--ink-1); color: var(--text-dim);
  border: var(--frame-dim); border-radius: var(--r-pill);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-xs); letter-spacing: .01em;
  transition: color .16s, border-color .16s, background-color .16s;
}
/* ⚠️ V99 (D5) - LE MOT "FILTRES" TOMBE A TOUTES LES LARGEURS, ET C EST LE
   RETRAIT D UNE MEDIA QUERY, PAS UNE REGLE NEUVE. La V54 l avait deja ecrit
   sous 560 px, avec son argument : l icone dit la meme chose, et le COMPTE
   reste - c est lui qui empeche un filtre oublie de devenir muet (piege
   nº 5). Le retour du 1er septembre demande que ce soit vrai partout.
   ⚠️ IL EST MASQUE VISUELLEMENT, JAMAIS RETIRE DU DOM : c est lui qui donne
   son NOM ACCESSIBLE au bouton. Le supprimer laisserait un bouton que rien
   ne nomme. */
.fmenu-label {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.fmenu-btn:hover { color: var(--paper); border-color: var(--paper); }
/* ⚠️ V227 - 31 px DE HAUT MESURES : la zone gagne 2 px en haut et en bas
   (35 px) sans que le bouton - donc la bande figee - grandisse.
   ⚠️ UN PSEUDO-ELEMENT SE CALE SUR LA BOITE DE REMPLISSAGE, PAS SUR LA
   BORDURE : avec le filet de 1 px, `-3px` donne 2 px au-dela du bord (releve
   a la mesure - `-2px` n en donnait qu un). */
.fmenu-btn { position: relative; }
.fmenu-btn::after { content: ""; position: absolute; inset: -3px -1px; }
.fmenu-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.fmenu-ico, .fmenu-caret { display: grid; place-items: center; }
.fmenu-ico svg { width: 16px; height: 16px; }
.fmenu-caret svg { width: 15px; height: 15px; transition: transform .16s; }
.fmenu-btn[aria-expanded="true"] { color: var(--paper); border-color: var(--paper); }
.fmenu-btn[aria-expanded="true"] .fmenu-caret svg { transform: rotate(180deg); }

/* Un filtre est pose : le bouton le DIT, meme referme. */
.fmenu-btn.is-armed { color: var(--paper); border-color: var(--brand); }
.fmenu-tally {
  display: grid; place-items: center;
  min-width: 18px; height: 18px; padding: 0 .25rem;
  border-radius: var(--r-pill);
  background: var(--brand-plein); color: var(--paper);
  font-size: var(--fs-micro); letter-spacing: 0;
}

/* ⚠️ UN POPOVER FLOTTE, DONC `--r-3` ET `--lift-2` : c'est l'echelle de
   rayons et l'echelle d'elevation de la V44, pas des valeurs choisies ici. */
.fmenu-pop {
  position: absolute; top: calc(100% + .45rem); left: 0; z-index: 3;
  min-width: 224px;
  padding: .6rem;
  background: var(--ink-3);
  border: var(--frame); border-radius: var(--r-3);
  box-shadow: var(--lift-2);
  /* ⚠️⚠️ V394 - IL DEFILE DESORMAIS PLUTOT QUE DE SORTIR DE L ECRAN. Treize
     puces portees a 34 px le font passer d environ 510 a 575 px : sur un
     telephone de 667 px de haut, sous une bande de 97, son bas tombait HORS
     de la fenetre - et un panneau pose dans une bande figee ne se rattrape pas
     en faisant defiler la page. Le plafond reprend le calcul de la bulle de
     profil (bande + marge), avec `dvh` pour la barre d adresse mobile. */
  max-height: calc(100vh - var(--head-h) - 1.5rem);
  max-height: calc(100dvh - var(--head-h) - 1.5rem);
  overflow-y: auto; overscroll-behavior: contain;
}
.fmenu-head {
  margin: .15rem 0 .35rem; padding: 0 .3rem;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-micro); letter-spacing: .16em; text-transform: uppercase;
  color: var(--text-dim);
}
.fmenu-sep {
  height: 0; margin: .55rem 0; border: 0; border-top: var(--frame-dim);
}

/* -------------------- Les trois vues, en LISTE (V54) ---------------------
   ⚠️ MEME BALISAGE, MEME JS : `.view-switch` et `data-view` sont ceux de la
   V45, seule leur mise en forme change. Ce n'etait un interrupteur que parce
   qu'il vivait dans une bande horizontale ; dans un menu, c'est une liste de
   choix - et la maquette D les place AVANT "Tout", parce qu'une vue decide
   de la forme et un filtre du contenu. */

/* ⚠⚠ V144 (2e) - LE BLOC « VUE » A UN PORTEUR, ET IL NE CREE PAS DE BOITE.
   L intitule, les deux boutons et le filet se masquent ENSEMBLE sous
   *Archives*, ou les vues n ont pas d objet ; un seul `hidden` plutot que
   trois, sinon une vue ajoutee demain en oublierait un.
   ⚠️ `display: contents` PARCE QUE `.fmenu-pop` EST UNE COLONNE FLEX : un
   vrai bloc y aurait mange le `gap` entre les trois morceaux. Et `[hidden]`
   - le seul `!important` du site - bat `display: contents`, donc le masquage
   marche quand meme. C est exactement le geste de `.topbar-tools` (V77). */
.fmenu-vue { display: contents; }

.view-switch { display: flex; flex-direction: column; gap: 2px; }
.view-btn {
  appearance: none; cursor: pointer; border: 0; background: transparent;
  min-height: 34px;                 /* V394 - voir `.chip-f` */
  width: 100%; padding: .38rem .3rem;
  display: flex; align-items: center; gap: .5rem;
  border-radius: var(--r-2); color: var(--text-dim);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-xs); letter-spacing: .03em; text-align: left;
  transition: color .16s, background-color .16s;
}
.view-btn svg { width: 17px; height: 17px; fill: currentColor; flex: 0 0 auto; }
.view-btn:hover { color: var(--paper); background: var(--ink-4); }
.view-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.view-btn.is-on { color: var(--on-light); background: var(--paper); }

/* ------------------------- Les filtres, en LISTE -------------------------
   ⚠️ CHAQUE FILTRE ACTIF PREND LA COULEUR DE CE QU IL MONTRE : les
   indispensables s'allument en vert Doomsday, les recommandes en orange.
   C'est l'echelle des statuts, donc rien de neuf a apprendre - et ca dit
   lequel des six est en cours sans avoir a le lire. */

.filters { display: flex; flex-direction: column; gap: 2px; }
.chip-f {
  appearance: none; cursor: pointer;
  /* ⚠️ V394 - 29 px mesures, SANS zone etendue : les seuls controles du site
     prives du plancher. Ici pas de `::after` possible - `::before` porte deja
     le rond, et les puces sont empilees a 2 px : une zone invisible
     recouvrirait sa voisine. La boite grandit donc pour de bon, et le menu
     recoit un plafond de hauteur pour ne pas sortir de l ecran (voir
     `.fmenu-pop`). */
  min-height: 34px;
  width: 100%; padding: .38rem .3rem;
  display: flex; align-items: center; gap: .5rem;
  background: transparent; color: var(--text-dim);
  border: 0; border-radius: var(--r-2);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-xs); letter-spacing: .03em; text-align: left;
  transition: color .16s, background-color .16s;
}
/* Le rond de la maquette D : vide tant que le filtre dort, plein des qu'il
   travaille. Il est dessine par la bordure, donc il ne bouge pas d'un pixel
   entre les deux etats - meme mecanique que la pastille "vu". */
.chip-f::before {
  content: ""; flex: 0 0 auto;
  width: 13px; height: 13px; border-radius: 50%;
  border: 2px solid currentColor; background: transparent;
  transition: background-color .16s;
}
.chip-f:hover { color: var(--paper); background: var(--ink-4); }
.chip-f:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.chip-f.is-on { color: var(--paper); }
.chip-f.is-on::before { background: currentColor; }
.chip-f--must.is-on  { color: var(--statut-requis); }
.chip-f--reco.is-on  { color: var(--statut-suggere); }
.chip-f--bonus.is-on { color: var(--statut-optionnel); }

/* ---- V400 - LES DEUX AXES COTE A COTE ---------------------------------------
   Statut a gauche, Visionnage a droite : deux colonnes egales, separees par
   la gouttiere et non par un cadre (la proximite groupe, V54). Le panneau
   s elargit pour les porter, sans jamais depasser la fenetre - il part du
   bouton Filtres, a une gouttiere du bord. Un libelle long (« Vu par eux,
   pas par moi ») passe a la ligne dans sa colonne au lieu de l elargir. */
.fmenu-pop { min-width: min(21rem, calc(100vw - 2rem)); }
.filters--axes { flex-direction: row; align-items: flex-start; gap: var(--sp-3); }
.filtre-axe { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.fmenu-sep--axe { margin: .35rem .3rem; }

.toolbar-count {
  margin: .6rem 0 0; text-align: center;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700;
  font-size: var(--fs-xs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--text-dim);
}
.toolbar-count.is-empty { color: var(--brand); }

/* ------------------------- L etat FILTRE ---------------------------------
   ⚠️ LE FILTRE FORCE L OUVERTURE DES PLIS, et il le fait EN CSS - sans jamais
   toucher a la classe is-open ni a is-folded. C est ce qui permet de retrouver
   exactement l etat d avant en effacant la recherche : rien n a ete modifie,
   seule une classe posee sur la roadmap change ce qui s affiche.
   Un resultat cache derriere un "+" serait un resultat perdu. */

/* ⚠️ LA SAGA N A PLUS RIEN A FORCER ICI (V52) : elle ne se plie plus, donc
   elle est deja ouverte - exactement comme l'encart depuis la V49. Une regle
   de moins parce qu'un pli de moins, pas parce qu'on l'a contournee. */
/* ⚠️⚠️ V363 - CES TROIS REGLES PASSENT A `.is-cherche`, ET LA SUIVANTE
   RESTE SUR `.is-filtering`. La classe faisait DEUX choses : cacher ce qui
   est ecarte, et DEPLIER tout pour qu aucun resultat ne se cache derriere un
   `+`. Depuis que la PORTEE ecarte aussi des cartes (et elle a une valeur par
   defaut), les deux causes se separent :
     - CHERCHER ou FILTRER veut tout deplier - un resultat cache derriere un
       bouton est un resultat perdu, et c est la lecon de la V43 ;
     - CHANGER DE PORTEE ne le veut PAS : on n a rien cherche, on a dit de
       quoi on parle. Deplier les sept routes pour ca serait un ecran qui
       repond a une question qu on n a pas posee.
   ⚠️ LES DEUX CLASSES SONT POSEES AU MEME ENDROIT (`appliquerA`), donc elles
   ne peuvent pas se desynchroniser. */
.roadmap.is-cherche .phase.is-folded .phase-chain { display: flex; }
.roadmap.is-cherche .drawer-slot .drawer-items { display: flex; }
/* ⚠️ L'ENCART N'A RIEN A FORCER SOUS FILTRE (V49) : il est deja ouvert. La
   V48 devait annuler ici toutes les regles de la fenetre du Sonyverse - un
   resultat enferme derriere une fenetre a ouvrir est un resultat perdu.
   L'encart supprime le probleme au lieu de le contourner. */
/* Le signe des plis ne ment pas : sous filtre, tout est ouvert. */
.roadmap.is-cherche .phase-sign::after,
.roadmap.is-cherche .drawer-plus-v { display: none; }

/* Trois classes, donc plus specifique que .roadmap.is-list .card qui suit :
   une carte filtree disparait quelle que soit la vue. */
.roadmap.is-filtering .is-off { display: none; }

/* ⚠⚠ V144 (2e) - LA GRILLE D ARCHIVES A SA PROPRE LIGNE, ET C EST UNE
   NECESSITE, PAS UNE COPIE. La regle du dessus est portee par ".roadmap" :
   marquer ".is-off" sur une carte d archive n aurait donc EU AUCUN EFFET
   VISIBLE - la classe posee, la carte toujours la, et rien pour le dire.
   C est la moitie invisible du travail : le JS marque, mais c est la CSS qui
   montre.
   ⚠️ ELLE NE DUPLIQUE RIEN D AUTRE : la grille n a ni pli, ni tiroir, ni
   encart, ni bandeau a forcer sous filtre. Les quatre regles qui precedent
   n ont pas d objet ici, et on ne les recopie donc pas « par symetrie ». */
.archive-grid.is-filtering .is-off { display: none; }

/* ====================== V41 - L AFFICHAGE EN LISTE =======================
   ⚠️ CE N EST PAS UNE AUTRE PAGE, C EST LA MEME EN UNE COLONNE. Aucun
   re-rendu : les cartes sont celles de la mosaique, reflechies en grille. Tout
   ce qui les accompagne continue de fonctionner - la pastille "vu", la note,
   l ouverture de la fiche. C est ce qui garantit qu aucune des deux vues ne
   prendra du retard sur l autre.
   ==================================================================== */

.roadmap.is-list .chain-line {
  flex-direction: column; align-items: stretch;
  gap: var(--sp-1); width: min(760px, 100%); margin-inline: auto;
}

/* ⚠️ CINQ COLONNES POUR CINQ ENFANTS. La carte en porte cinq - affiche,
   bandeau de notes, titre, annee, pastille - et une grille a quatre colonnes
   renvoyait la pastille sur une seconde rangee : la ligne faisait 118 px au
   lieu de 68. On declare donc les cinq, et on remet les enfants dans l ordre
   de lecture d une liste (titre AVANT les notes) par `order` : le DOM ne
   bouge pas, donc la mosaique n est pas touchee. */
.roadmap.is-list .card {
  display: grid; width: 100%;
  /* ⚠️ V360 - 34 px -> 68 : la derniere colonne porte DEUX boutons de 30 px
     et leur ecart. La liste occupe toute la largeur, la place existe. */
  grid-template-columns: 42px minmax(0, 1fr) auto auto 68px;
  align-items: center; gap: .8rem;
  padding: .34rem .6rem;
  background: var(--ink-1); border: var(--frame-dim); border-radius: var(--r-2);
}
.roadmap.is-list .poster-wrap { order: 1; }
.roadmap.is-list .card-title  { order: 2; }
.roadmap.is-list .card-year   { order: 3; }
.roadmap.is-list .card-band   { order: 4; }
.roadmap.is-list .watch-row   { order: 5; margin-top: 0; }   /* V360 */
.roadmap.is-list .card:hover { border-color: var(--ink-6); }
.roadmap.is-list .poster-wrap { width: 42px; }
/* L affiche tombe a 42 px : le coeur et le "i" n y tiennent plus, et une cible
   de 34 px posee sur une vignette de 42 est intouchable. Le "i" ne manque
   pas - toute la carte ouvre deja la fiche. */
.roadmap.is-list .fav-btn,
.roadmap.is-list .info-btn,
.roadmap.is-list .card-flags { display: none; }
/* ⚠️ V480 - SOUS UNE VIGNETTE DE 42 px, LA LANGUETTE NE GARDE QUE LE STATUT.
   Deux nombres de trois signes et un carre de couleur dans 42 px se
   chevaucheraient ; le statut, lui, tient. Les notes ne sont pas masquees par
   defaut ailleurs - elles n existent pas ICI, sur ces deux vues qui ont deja
   retire le coeur, le « i » et la pile d etiquettes pour la meme raison. */
.roadmap.is-list .aff-notes,
.roadmap.is-cols .aff-notes { display: none; }
.roadmap.is-list .aff-ruban,
.roadmap.is-cols .aff-ruban { min-height: 0; padding: 2px 0; background: none; }

/* ⚠️ LE DECOMPTE NE SUIT PAS EN LISTE NI EN COLONNES (V84), ET CE N'EST PAS
   UN OUBLI. La carte y redevient une LIGNE - une grille de cinq colonnes ou
   chaque enfant a son `order` (V45) : un sixieme enfant sans place declaree
   repartirait sur une SECONDE RANGEE et doublerait la hauteur de la ligne,
   sans qu'aucune erreur ne se produise. C'est le geste exact du coeur, du
   « i » et des etiquettes, masques ici pour la meme raison de place. */
.roadmap.is-list .card-countdown { display: none; }

/* ⚠️⚠️ V159 (5c) - LES DEUX NOMS SUIVENT LE DECOMPTE ET LA SALLE : masques en
   vue LISTE et en COLONNES. Ce n est pas un choix esthetique, c est une
   MESURE : en colonnes, la ligne du nom rendait les hauteurs de carte
   NON UNIFORMES (62 px et 70 px la ou il n y en avait qu une seule valeur),
   parce que la carte y est une grille de cinq colonnes dont le bandeau est
   l une des cellules. C est la meme famille que le piege du sixieme enfant
   (V84), par un autre bout : ce n est pas un enfant de plus, c est un enfant
   qui grandit.
   ⚠️ ET L INFORMATION NE SE PERD PAS : les deux etoiles gardent leur COULEUR
   (celle de chaque profil) et leur nom accessible, que `syncRatingUI` ecrit
   deja. C est exactement ce que la V105 fait de « Modifie ailleurs » sous
   1200 px - l etat survit par la teinte et par le nom accessible.
   ⚠️ LA MOYENNE, ELLE, RESTE ABSENTE EN ROUTE DUO DANS TOUTES LES VUES : elle
   n est pas construite (js/app.js), donc il n y a rien a masquer ici - et
   c est justement ce qui garde la carte cohérente d un ecran a l autre. */
.roadmap.is-list .band-qui { display: none; }
.roadmap.is-list .card-band.is-nomme .band-note {
  flex-wrap: nowrap; width: auto;
}

.roadmap.is-list .card-title {
  height: auto; margin: 0; text-align: left; align-items: flex-start;
}
.roadmap.is-list .card-title span { white-space: normal; }
.roadmap.is-list .card-year { margin: 0; font-size: var(--fs-xs); }
.roadmap.is-list .card-band { margin: 0; }
.roadmap.is-list .watch-dot { margin: 0; }

/* Le cadre d un jalon ne peut pas garder ses marges negatives dans une pile :
   elles etaient calculees sur la hauteur d une affiche de 122 px. Il redevient
   un simple filet a la couleur de la route - ce qu il a toujours voulu dire. */
.roadmap.is-list .milestone-frame { margin: 0; padding: 0; border-width: 0 0 0 3px; }

/* Le tiroir reste un bouton, mais son decalage vertical n a plus rien a
   compenser dans une colonne. */
.roadmap.is-list .drawer-slot { margin-top: 0; }
.roadmap.is-list .drawer-items { margin-top: .3rem; flex-direction: column; }
/* ⚠️ EN LISTE, L'ENCART EST UNE COLONNE COMME LE RESTE. Il perd sa
   geometrie de cadre de jalon - il n'y a plus d'axe d'affiches a tenir -
   mais il garde son filet et son aplat : c'est la seule chose qui dise
   encore que ces cinq lignes viennent d'ailleurs. */
.roadmap.is-list .encart {
  margin: 0; padding: .4rem; width: 100%;
  align-items: stretch; gap: .3rem;
}
.roadmap.is-list .encart-body { flex-direction: column; gap: .3rem; }
.roadmap.is-list .encart-row { flex-direction: column; gap: .3rem; }

/* ==================== V45 - LA VUE COLONNES ==============================
   Les routes se suivent HORIZONTALEMENT, chacune posee debout : une rangee
   de livres. Chaque colonne porte son bandeau en tete et sa route dessous.

   ⚠️ CE N EST PAS UNE TROISIEME VUE, C EST LA VUE LISTE POSEE AUTREMENT.
   setView pose is-list ET is-cols : le rendu de carte est le meme dans les
   deux dispositions, et layoutChain n a pas une ligne a changer - sa sortie
   teste is-list, que les deux modes posent. C est la promesse de la V41
   tenue une seconde fois : deux rendus separes auraient diverge au premier
   ajout, et il y en aurait alors TROIS a maintenir au lieu d un.

   ⚠️ TOUT LE BLOC EST BORNE A 901 px, ET C EST LE REPLI LUI-MEME. Sous
   cette largeur is-cols ne declare plus rien : il ne reste que is-list,
   c est-a-dire exactement la vue liste. Une colonne y ferait moins de 215 px
   - titre sur quatre lignes, etagere qui defile horizontalement, soit le
   defaut que la V32 a passe une version a tuer. ⚠️ Le repli ne DEFAIT donc
   rien : il n y a rien a defaire, et aucune regle a tenir en double.
   La preference, elle, n est pas touchee - on retrouve ses colonnes en
   elargissant la fenetre.
   ==================================================================== */

@media (min-width: 901px) {

  /* L etagere. Elle prend la gouttiere du site : en mosaique c est la rangee
     qui va au bord, ici c est une grille de colonnes - elle doit se caler sur
     le HUD et les bandeaux de saga, pas les deborder.

     ⚠️ LES SAGAS GARDENT LEUR NIVEAU, et c est ce qui donne 3 + 4 au lieu
     de sept colonnes d affilee. Les aplatir demanderait un re-rendu - la
     seule chose que la bascule d affichage ne fait jamais - et ferait perdre
     le palier gagne en V38. Les sept routes tiennent a l ecran, sur DEUX
     etageres au lieu d une : une rangee de livres par saga. Sept colonnes
     d affilee auraient de toute facon donne 228 px chacune a 1680 px, ou un
     titre de carte ne tient plus ; a quatre, la colonne fait 405 px. */
  /* ⚠️ LE `:not(.is-folded)` A DISPARU AVEC LE PLI (V52), ET AVEC LUI LE
     BUG QU IL CORRIGEAIT : `.saga.is-folded .saga-body { display: none }`
     avait exactement la meme specificite que la regle ci-dessous, ecrite
     2000 lignes plus bas - une saga repliee se serait RALLUMEE en passant en
     colonnes. Plus de pli, plus de classe, plus d arbitrage par ordre de
     source. Le second bloc, qui redonnait l etagere a une saga forcee
     ouverte par un filtre, tombe pour la meme raison. */
  .roadmap.is-cols .saga-body {
    display: flex; align-items: flex-start; justify-content: center;
    gap: clamp(.6rem, 1.2vw, 1.3rem);
    width: var(--shell); margin-inline: auto;
    overflow-x: auto; scrollbar-width: thin;  /* filet de securite, jamais plus */
  }
  .roadmap.is-cols .phase { flex: 1 1 0; min-width: 0; margin-bottom: 0; }

  /* ⚠️ UNE ROUTE REPLIEE SE REDUIT A SA TRANCHE, et c est la seule chose
     que la vue colonnes apprend au pli. Ailleurs une route repliee laisse une
     barre pleine largeur, parce qu elle s empile ; ici sept barres de 528 px
     dont six vides seraient un menu pose sur le cote - exactement le defaut
     que la V34 a retire de la barre de route. En rendant la main aux routes
     ouvertes, le pli devient ce que la metaphore promettait : un livre ferme
     ne montre que son dos. Mesure : la tete tient sur UNE ligne (22 px) a
     163-225 px selon le titre, et `justify-content: center` garde l etagere
     sur l axe de la page quand tout est referme. */
  .roadmap.is-cols .phase.is-folded { flex: 0 1 auto; }

  /* Le bandeau devient la TETE DE COLONNE : il perd la coquille, desormais
     portee par l etagere, et son echelle suit la largeur reduite. */
  .roadmap.is-cols .phase-header { width: 100%; margin-bottom: .5rem; }
  .roadmap.is-cols .phase-fold { min-height: 62px; padding: .4rem 1.7rem 1.45rem .7rem; }
  .roadmap.is-cols .phase-compte { top: auto; bottom: 0; left: .55rem; transform: none; line-height: 22px; }
  .roadmap.is-cols .phase-tout { top: auto; bottom: 0; right: .2rem; transform: none; padding: 0; height: 22px; }
  /* V401 - une tete de colonne repliee mesure 101 a 134 px : le mot n y tient
     pas a cote du compte. L icone reste, le nom accessible dit tout. */
  .roadmap.is-cols .phase-tout-mot { display: none; }
  /* V392 - meme cible de 34 px que sur telephone : voir la regle jumelle. */
  .roadmap.is-cols .phase-tout::after { content: ""; position: absolute; inset: -12px -3px 0; }
  .roadmap.is-cols .phase-title { font-size: clamp(.95rem, 1.25vw, 1.2rem); }
  .roadmap.is-cols .phase-sign { left: .55rem; width: 14px; height: 14px; }
  .roadmap.is-cols .phase-sign::before { top: 6px; }
  .roadmap.is-cols .phase-sign::after  { left: 6px; }
  /* Une route repliee ne laisse plus qu une tranche : sa tete. C est le meme
     pli qu ailleurs - il n a rien eu a apprendre de neuf. */
  .roadmap.is-cols .phase.is-folded { margin-bottom: 0; }

  .roadmap.is-cols .phase-chain { align-items: stretch; overflow-x: visible; }
  .roadmap.is-cols .chain-line { width: 100%; margin-inline: 0; gap: .28rem; padding: 0; }

  /* --------------------------- LA CARTE ---------------------------------
     ⚠️ UNE COLONNE N EST PAS UNE LIGNE. La carte de liste etale ses cinq
     enfants sur UNE ligne, ce qui lui demande 760 px ; ici elle en a 405 au
     mieux. Elle se plie donc en DEUX rangs - le titre sur toute la largeur,
     l annee et les notes dessous - au lieu de perdre un de ses cinq enfants.
     La grille est NOMMEE : le DOM ne bouge pas, donc ni la mosaique ni la
     liste ne sont touchees.

     ⚠️ L AFFICHE ET LA PASTILLE ENJAMBENT LES DEUX RANGS. Sans ca la
     pastille - un bouton de 30 px - se serait posee sur un rang de 14 px, et
     la vignette aurait ete etiree par align-items: center. */
  .roadmap.is-cols .card {
    /* ⚠️ V360 - 30 px -> 64 : la case porte desormais la RANGEE. Mesure a
       320 px : `docX` reste nul et les hauteurs de carte restent uniformes. */
    grid-template-columns: 34px minmax(0, 1fr) auto 64px;
    grid-template-areas:
      "p t t d"
      "p y b d";
    align-items: center;
    gap: .1rem .5rem;
    padding: .3rem .45rem;
  }
  .roadmap.is-cols .poster-wrap { grid-area: p; width: 34px; align-self: center; }
  .roadmap.is-cols .card-title {
    grid-area: t; align-self: end; font-size: calc(var(--title-fs) * .88);
  }
  .roadmap.is-cols .card-year {
    grid-area: y; align-self: start; height: auto; justify-content: flex-start;
  }
  .roadmap.is-cols .card-band {
    grid-area: b; align-self: start; width: auto;
    justify-content: flex-end; gap: .5rem; font-size: var(--fs-xs);
  }
  .roadmap.is-cols .watch-row { grid-area: d; align-self: center; margin-top: 0; }   /* V360 */

  /* Le cadre du jalon reste le filet de la vue liste ; il n a qu a s etirer
     sur la largeur de la colonne au lieu de se centrer dedans. */
  .roadmap.is-cols .milestone-frame { width: 100%; }

  /* Tiroirs et encarts : leurs decalages negatifs etaient calcules sur la
     hauteur d une affiche de RANGEE. Devant une vignette de 34 px ils n ont
     plus rien a compenser - et une marge negative de --gap sur une colonne de
     405 px sort le bouton de sa colonne. */
  .roadmap.is-cols .drawer-slot,
  .roadmap.is-cols .encart {
    flex-direction: column; align-items: stretch; align-self: stretch;
    width: 100%; gap: .28rem;
    margin-left: 0; margin-right: 0; margin-top: 0;
  }
  .roadmap.is-cols .drawer-items { margin-top: 0; }
}

@media (max-width: 560px) {
  /* ⚠️ LE MENU ET LE CHAMP RESTENT SUR UNE LIGNE. Le champ prenait 100 % de
     la largeur : dans une bande figee, ca renvoyait le second element a la
     ligne et DOUBLAIT la hauteur de la bande - exactement ce qu'on cherche a
     eviter en ne figeant qu'une ligne. Il s'etire donc au lieu de pousser.
     ⚠⚠ V141 - ".topbar-tools { gap: .4rem }" EST PARTIE D ICI, ET ELLE N A
     JAMAIS RIEN ESPACE : sous 620 px le conteneur est en "display: contents",
     donc il ne cree aucune boite ou un "gap" puisse s appliquer - et ce bloc
     est TOUT ENTIER sous ce seuil. L espacement du couple se pose desormais
     sur le "column-gap" de la grille, la ou il y a une grille. */
  .search { flex: 1 1 auto; min-width: 0; }
  /* ⚠️ V99 (D5) - LES DEUX REGLES DU MENU SONT REMONTEES A LA BASE. Le
     libelle masque et le padding resserre valaient sous 560 px depuis la
     V54 ; ils valent desormais a toutes les largeurs. Les laisser ici
     aurait ete DEUX endroits a tenir d accord pour une seule valeur -
     piege nº 6. Ce bloc ne garde que ce qui est propre au telephone. */
}

/* ===================== V41 - LE CODE DE PROGRESSION ====================== */

/* ⚠️⚠️ V108 - UNE CONSIGNE NE REMONTE PLUS JAMAIS, ET C EST LA CORRECTION
   D UN DEFAUT VIEUX DE SOIXANTE-SEPT VERSIONS. Elle portait
   `margin-top: -.55rem` depuis la V41, et le commentaire qui l accompagnait
   disait pourquoi : « elle suit d ordinaire une `.sheet-label` ». C ETAIT LA
   PREMISSE FAUSSE - une `.sheet-label` ne porte que `.35rem` de marge basse,
   donc la consigne la CHEVAUCHAIT de 3,2 px, MESURES, sur cinq ecrans ; et
   posee apres une rangee de boutons (marge basse NULLE) elle la chevauchait
   de 8,8 px.
   ⚠️ LE DEFAUT NE POUVAIT PAS SE VOIR AUTREMENT QU A L OEIL : rien ne casse,
   rien ne leve d erreur, deux lignes de texte se touchent - piege nº 5.
   ⚠️⚠️ ET IL ETAIT DEJA RATTRAPE SIX FOIS, LOCALEMENT : `.duo-surnom`,
   `.amis-state`, `.amis-add`, `.cpt-need`/`.cpt-on`, `.cpt-change` et
   `.sync-fold` (parti en V147) reposaient CHACUN la meme marge haute.
   SIX COPIES D UN MEME CORRECTIF SONT LE SIGNE QUE LE CORRECTIF EST LE
   PROBLEME (piege nº 2 de `CLAUDE.md`, leçon de la quinzieme copie de
   `[hidden]` en V39). Les six sont retirees ; la valeur vit ici, une fois.
   ⚠️ CE QUI DECIDE DE L ESPACE EST DESORMAIS LA MARGE BASSE DE CE QUI
   PRECEDE, et c est la bonne autorite : un intitule donne ses `.35rem`, un
   bloc de champ ses `.7rem`, une rangee de boutons rien. Aucune valeur n est
   tenue a la main de ce cote-ci - piege nº 6.
   ⚠️ L UNIQUE EXCEPTION QUI SURVIVAIT, `.amis-vis .sheet-hint` (`.6rem`), EST
   PARTIE EN V148 avec le reglage « qui peut me voir ». Elle n annulait pas un
   chevauchement, elle DEMANDAIT une respiration de plus sous deux boutons
   radio - et une exception qui ajoute n est pas une exception qui repare.
   ⚠️ IL N Y A DONC PLUS AUCUNE EXCEPTION : la valeur vit ici, une seule fois,
   pour tout le site. */
.sheet-hint {
  margin: 0 0 .7rem; font-size: var(--fs-s); color: var(--text-dim);
}
.sync { display: flex; flex-direction: column; }
.sync-actions { display: flex; flex-wrap: wrap; gap: .5rem; }
.sync-btn { flex: 1 1 0; min-width: 140px; }
.sync-msg {
  margin: .8rem 0 0; font-size: var(--fs-s); color: var(--doom);
}
.sync-msg.is-bad { color: var(--brand); }

.chain-line--off { display: none; }

/* ===================== V51 - LA PAGE D'ACCUEIL ===========================
   Un ecran plein, pose PAR-DESSUS le site, jamais a la place : c'est ce qui
   permet a l'apercu d'etre le site lui-meme et non une capture a maintenir.

   ⚠️ z-index 70, et le nombre est une decision : SOUS le panneau de profil
   et la fiche (80), SOUS la banniere de consentement (90). La banniere passe
   donc devant si les deux se croisent - mais elles ne se croisent pas :
   l'accueil ne s'ouvre qu'une fois le consentement tranche, deux panneaux
   empiles, c'est deux refus (regle V18).
   ==================================================================== */

/* ⚠️⚠️ V501 - L ENTREE SE LIT COMME UNE AFFICHE DE SORTIE (planche E3,
   retenue par l auteur). Le site parle de films : son premier ecran en prend
   la grammaire - le nom gigantesque EN HAUT, l accroche en petites capitales
   espacees juste dessous, et les portes en bas de page, la ou une affiche met
   sa date de sortie, sur un bloc de mentions.
   ⚠️ DES DEUX VARIANTES PROPOSEES - « Snapline tout en haut centre » ou « au
   centre » - l auteur m a laisse choisir : c est LE HAUT. Une affiche porte
   son titre en haut ; le mettre au milieu aurait garde la disposition d avant
   en changeant seulement la taille, c est-a-dire contredire la planche qu on
   applique.
   ⚠️⚠️ ET LE MUR RESTE ENTIER : c est lui qui FAIT l affiche. Il perd son
   inclinaison (une affiche est droite) et son fondu lateral, qui n avait de
   sens que tant que le panneau tenait le flanc gauche. */
.landing {
  position: fixed; inset: 0; z-index: 70;
  display: flex; align-items: stretch; justify-content: center;
  background: var(--ink-0);
  overflow: hidden;
}
/* ⚠️ LE VOILE EST UN FRERE, PAS UN FOND POSE SUR LE PANNEAU : il doit passer
   AU-DESSUS du mur et EN DESSOUS du texte, ce qu un fond ne sait pas faire.
   Il est sombre en haut (le titre s y detache), clair au milieu (on voit les
   affiches), sombre en bas (le bloc des portes s y pose). */
.landing::after {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(to bottom,
              color-mix(in srgb, var(--ink-0) 88%, transparent) 0,
              color-mix(in srgb, var(--ink-0) 34%, transparent) 26%,
              color-mix(in srgb, var(--ink-0) 72%, transparent) 62%,
              color-mix(in srgb, var(--ink-0) 98%, transparent) 100%);
}

/* ----------------------------- L'APERCU --------------------------------
   Les affiches des rangees, en colonnes inclinees. C'est la meme regle que
   le defile (V35) et que les emblemes (V25) : ce qui figure sur une affiche
   officielle ne peut rien divulguer.
   ⚠️⚠️ V294 - IL BOUGE, ET C EST UN RENVERSEMENT DEMANDE PAR L AUTEUR (la
   V51 disait « rien ne bouge », pour la raison de la V35). Ce qui tient de
   l argument : le mouvement est LENT, il vit HORS du panneau (le masque
   l efface sous le texte), et `prefers-reduced-motion` comme le reglage
   *Animations coupees* l arretent par la regle globale.
   ⚠️ Le fondu est un MASQUE et non une opacite : il faut que l'apercu soit
   PLEIN a droite et ABSENT sous le panneau, pas gris partout. */
/* ⚠️ V501 (E3) - DROIT ET PLEIN CADRE. L inclinaison et le fondu LATERAL
   servaient une composition en deux colonnes - panneau opaque a gauche,
   affiches a droite - qui n existe plus. Le fondu devient VERTICAL : il
   degage le haut pour le titre et le bas pour les portes. */
.landing-wall {
  position: absolute; inset: 6% 0 22%;
  display: flex; gap: .7rem;
  pointer-events: none;
  opacity: .42;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 16%, #000 62%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 16%, #000 62%, transparent 100%);
}
.landing-col { flex: 1 1 0; min-width: 0; overflow: hidden; }
/* ⚠️ LE RAIL PORTE SA LISTE DEUX FOIS et remonte de 50 % : la seconde moitie
   prend exactement la place de la premiere, donc la boucle ne saute pas.
   Une colonne sur deux descend, et chacune a sa vitesse - quatre bandes au
   meme pas se liraient comme un seul bloc qui glisse. */
.landing-rail {
  display: flex; flex-direction: column;
  animation: landing-defile 150s linear infinite;
  will-change: transform;
}
.landing-col:nth-child(2) .landing-rail { animation-duration: 180s; animation-direction: reverse; }
.landing-col:nth-child(3) .landing-rail { animation-duration: 135s; }
.landing-col:nth-child(4) .landing-rail { animation-duration: 165s; animation-direction: reverse; }
@keyframes landing-defile {
  from { transform: translateY(0); }
  to   { transform: translateY(-50%); }
}
.landing-wall img {
  width: 100%; display: block; border-radius: var(--r-2);
  border: var(--frame-dim);
  margin-bottom: .7rem;              /* jamais un gap : voir `.landing-rail` */
  aspect-ratio: 2 / 3; object-fit: cover; background: var(--ink-2);
}

/* ----------------------------- LE PANNEAU -------------------------------
   Opaque a gauche, fondu vers la droite : c'est lui qui rend l'apercu
   visible sans jamais laisser une affiche passer sous le texte. */
.landing-panel {
  position: relative; z-index: 1;
  flex: 1 1 auto; width: 100%; max-width: 860px;
  display: flex; flex-direction: column; align-items: center; text-align: center;
  /* ⚠️ `safe center` ET overflow, pas l un ou l autre : un contenu centre en
     flex qui deborde se fait rogner PAR LE HAUT, et la partie rognee est
     inatteignable au defilement. Mesure a 1280x430 avant correctif : le titre
     commencait a -71 px. C est la meme parade que `.phase-chain`. */
  justify-content: safe center; overflow-y: auto;
  padding: clamp(2rem, 5vw, 3.4rem) 1.5rem clamp(1.6rem, 4vw, 2.6rem);
  /* ⚠️ V501 - LE HAUT NE DESCEND JAMAIS SOUS LA RESERVE DU SELECTEUR DE
     LANGUE : son coin, sa cible tactile, puis un souffle. « max » et non une
     media query - la collision ne depend pas de la largeur mais du rapport
     entre deux valeurs, et une media query aurait choisi un seuil au hasard. */
  padding-top: max(clamp(2rem, 5vw, 3.4rem), calc(var(--langue-haut) + var(--pastille) + .7rem));
  /* ⚠️⚠️ V145 - LA RESERVE DE LA BANNIERE DE CONSENTEMENT. L accueil s ouvre
     maintenant avant qu on ait repondu, et la banniere est une bande de bas
     d ecran de ~300 px sur telephone : mesure a 375 x 667 puis a 320 x 568,
     elle recouvrait « S inscrire » ET « Continuer sans compte ».
     ⚠️ UNE VARIABLE, PAS UNE MEDIA QUERY NI UN ":has()" : "--consent-h" vaut
     0 des qu on a repondu, donc cette ligne retombe d elle-meme sur le
     "2rem" du raccourci ci-dessus. Rien a defaire, rien a tenir d accord -
     c est le geste de "--warn-h", reemploye tel quel.
     ⚠️ ET C EST CE PANNEAU QUI LA PORTE PARCE QUE C EST LUI QUI DEFILE
     ("overflow-y: auto", V90) : posee sur ".landing", qui est en
     "overflow: hidden", elle n aurait rien rendu d atteignable. */
  padding-bottom: calc(clamp(1.6rem, 4vw, 2.6rem) + var(--consent-h, 0px));
  /* ⚠️⚠️ V501 (E3) - PLUS DE FOND SUR LE PANNEAU. Le degrade lateral etait ce
     qui rendait le mur lisible DERRIERE un texte cale a gauche ; le voile
     vertical de `.landing::after` fait desormais ce travail pour toute la
     page. Deux voiles superposes auraient efface le mur qu on veut montrer.
     ⚠️ ET LE PANNEAU TIENT TOUTE LA PAGE, borne a 860 px : centre, en trois
     temps - le titre, l accroche, le bloc du bas. */
  background: none;
}

/* ⚠️ V501 (E3) - LE TITRE D AFFICHE. `text-indent` VAUT L ESPACEMENT : une
   lettre espacee traine son espace APRES elle, donc un mot centre se retrouve
   decale d un demi-espace vers la gauche. Rendre l espace au debut remet le
   mot sur l axe - sans quoi le titre le plus grand de la page serait le seul
   a ne pas etre centre. */
.landing-eyebrow {
  margin: 0; font-family: var(--display); font-weight: 800; font-stretch: var(--ch-affiche);
  text-transform: uppercase; letter-spacing: .22em; text-indent: .22em;
  font-size: clamp(2.4rem, 9vw, 5.6rem); line-height: 1;
  color: var(--paper);
}
/* ⚠️ L ACCROCHE DEVIENT UNE LIGNE DE CREDITS, pas un paragraphe : c est ce
   qui distingue une affiche d une page de presentation. Le TEXTE ne change
   pas d un mot (V333) - seule sa voix change. */
.landing-tagline {
  margin: 1.2rem auto 1.6rem; max-width: 52ch;
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 600;
  text-transform: uppercase; letter-spacing: .2em;
  font-size: clamp(.68rem, 1.3vw, .82rem); line-height: 2;
  color: var(--paper); opacity: .78;
}

/* ------------------------------ LE CERCLE -------------------------------
   ⚠️ `aspect-ratio: 1` et non une hauteur ecrite : le cercle doit rester un
   cercle quelle que soit la longueur des trois libelles. Le padding lateral
   est en POURCENTAGE du diametre pour que le contenu reste dans la corde et
   non dans le carre englobant. */
/* ⚠️⚠️ V501 (E3) - LE CERCLE N EST PLUS UN CERCLE, ET C EST AUSSI LA REPONSE
   A UNE DEMANDE EXPLICITE : « la fenetre de connexion/inscription ne doit pas
   avoir d angle arrondi ». Le disque etait la derniere forme ronde du site
   apres la V493, et il l etait par un `border-radius: 50%` ecrit a la main,
   donc hors de l echelle des rayons - la V493 ne pouvait pas l atteindre.
   ⚠️ IL DEVIENT LE BAS DE PAGE D UNE AFFICHE : un filet en haut, rien
   d autre. Pas de cadre, pas de fond, pas d ombre - le voile vertical porte
   deja le contraste, et un panneau pose dessus redeviendrait une fenetre.
   ⚠️ `margin-top: auto` LE POUSSE EN BAS SANS ENVELOPPER QUOI QUE CE SOIT.
   Un `space-between` aurait reparti les QUATRE enfants du panneau - titre,
   langue, accroche, bloc - a intervalles egaux, ce qui aurait decolle la
   langue du titre auquel elle appartient. */
.landing-circle {
  width: min(560px, 100%); margin-top: auto;
  display: flex; flex-direction: column; justify-content: center; gap: .6rem;
  padding: 1.2rem 1.4rem 0;
  border: 0; border-top: 1px solid color-mix(in srgb, var(--paper) 22%, transparent);
  border-radius: 0;
  background: none; box-shadow: none;
}

/* ⚠️ LE BLOC DE COMPTE SE POSE ICI, ET IL S'Y ALLEGE (V67). Un cadre, une
   ligne de titre et le couple « Creer / J'ai deja un compte » ne tiennent
   pas dans la corde d'un disque de 400 px - et ils n'ont rien a y faire : le
   cercle EST le cadre, le nom du site est deja au-dessus de lui, et
   « S'inscrire » est juste dessous.
   ⚠️ C'EST DU MASQUAGE, PAS UN SECOND BALISAGE : le meme bloc redevient
   entier des qu'il rentre dans la fenetre. Et ce qui se masque, ce sont les
   EXPLICATIONS - jamais un message, jamais la pastille « Bientot », qui
   sont les deux choses que cet ecran doit pouvoir dire. */
.landing-slot { width: 100%; text-align: center; }   /* V501 (E3) */
/* ⚠️ V501 - ET LA RANGEE DE TETE SE CENTRE AVEC : « text-align » ne
   deplace pas un enfant de flex. La pastille « Bientot » - la seule chose
   que ce creux affiche hors ligne - restait calee a gauche sous un bloc
   entierement centre. */
.landing-slot .compte-head { justify-content: center; }
.landing-slot .compte { border: 0; background: none; padding: 0; }
/* ⚠️ LA PASTILLE « BIENTOT » RESTE, ET ELLE SEULE. Hors ligne ou sans base,
   c est la SEULE chose que le bloc affiche : masquer la ligne de titre en
   entier laissait un cercle vide - un ecran qui ne dit rien la ou il devait
   dire pourquoi il ne peut rien faire.
   ⚠️⚠️ V149 - LES DEUX REGLES QUI MASQUAIENT `.compte-ico` ET `.compte-body`
   SONT PARTIES AVEC LES DEUX SPANS : le logo et les deux lignes de
   presentation ont quitte le balisage, ici comme dans l onglet Compte. **La
   rangee `.compte-head`, elle, RESTE** - c est elle qui porte `#cptSoon`, et
   la supprimer aurait emporte la degradation avec la decoration. */
.landing-slot .cpt-choix { display: none; }
.landing-slot .compte-act,
.landing-slot .cpt-form { margin-top: 0; }
.landing-slot .sheet-hint { display: none; }
.landing-slot .cpt-need > .sheet-hint { display: block; }
.landing-slot .sync-actions { margin-top: .55rem; }

/* ⚠️ LES TROIS BOUTONS ONT LA MEME LARGEUR ET LE MEME CORPS, et ce n'est pas
   de l'uniformite : c'est la regle "pas de mur" (V18). Un "Continuer sans
   compte" ecrit en petit gris sous deux boutons pleins est un peage deguise.
   Ce qui distingue "S'inscrire", c'est SON FOND - un seul bouton plein. */
.landing-btn {
  width: 100%; padding: .5rem .7rem;
  font-family: var(--display); font-weight: 700; font-stretch: var(--ch-ui);
  text-transform: uppercase; letter-spacing: .14em;   /* V501 (E3) */
  font-size: var(--fs-s); line-height: 1.2;
  border-radius: var(--r-2); border: var(--frame);
  background: transparent; color: var(--text);
  cursor: pointer;
  transition: background .18s ease, border-color .18s ease, color .18s ease;
}
.landing-btn:hover { border-color: var(--ink-7); background: var(--ink-3); }
.landing-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.landing-btn--main {
  background: var(--brand-plein); border-color: var(--brand-plein); color: #fff;
  padding-block: .8rem;   /* V501 (E3) - la porte principale porte la date de sortie */
}
/* V230 - le survol FONCE (il eclaircissait en #F04A4A : 3,6:1 sous le blanc). */
.landing-btn--main:hover { filter: brightness(.9); }

.landing-slot .method-soon { display: inline-block; }

/* ⚠️ SOUS 900 px LE CERCLE N'EST PLUS UN CERCLE, et c'est voulu : sur un
   telephone, un disque de 400 px laisse 74 % de sa surface vide et ecrase
   les trois portes dans sa corde. La forme sert la lisibilite ou elle ne
   sert a rien. L'apercu passe DERRIERE, plein cadre et tres sourd. */
/* ⚠️⚠️ V501 - CE REPLI N A PRESQUE PLUS RIEN A FAIRE, ET C EST LA MEILLEURE
   PREUVE QUE LA PLANCHE TIENT. Il existait pour DEFAIRE la composition en
   deux colonnes : `display: block` pour casser le flanc, un fond pose sur le
   panneau, un recentrage du texte, un cercle qui cessait d en etre un. La E3
   est centree et en une colonne A TOUTES LES LARGEURS - il ne reste donc que
   ce qui est vraiment une question de place : un mur plus sourd derriere un
   texte qui occupe toute la largeur, et un bloc de portes plus etroit.
   ⚠️ LE MUR GARDE SON FONDU VERTICAL ICI AUSSI : sur telephone c est le haut
   et le bas qui portent le titre et les portes, exactement comme sur grand
   ecran. */
@media (max-width: 900px) {
  .landing-wall { inset: 4% 0 26%; opacity: .2; }
  .landing-panel { overflow-y: auto; }
  .landing-circle { width: min(380px, 100%); padding-inline: .6rem; }
}

/* ================== LA LANGUE (V375 - js/langue.js) =====================
   Un drapeau et un caret, a droite du profil derriere un filet ; le meme
   bouton sur l accueil. Le menu est le frere de `.pmenu-pop` : memes jetons,
   `position: absolute` (la bande figee ne grandit pas d un pixel). */
.langue { position: relative; display: flex; align-items: center; gap: .5rem; flex: 0 0 auto; }
.langue-filet { width: 1px; height: 28px; background: var(--rule); }
.langue-btn {
  appearance: none; cursor: pointer;
  display: flex; align-items: center; gap: .3rem;
  min-height: 34px; padding: 0 .35rem;
  background: transparent; border: 0; border-radius: var(--r-pill);
  color: var(--text-dim);
  transition: background-color .16s, box-shadow .16s;
}
.langue-btn:hover { background: var(--ink-2); }
.langue-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.langue-btn[aria-expanded="true"] { background: var(--ink-2); box-shadow: inset 0 0 0 1px var(--paper); }
.langue-drapeau { display: grid; place-items: center; flex: 0 0 auto; }
.langue-drapeau svg {
  display: block; width: 24px; height: 16px; border-radius: 2px;
  box-shadow: 0 0 0 1px var(--ink-6);
}
.langue-caret svg { display: block; width: 14px; height: 14px; transition: transform .16s; }
.langue-btn[aria-expanded="true"] .langue-caret svg { transform: rotate(180deg); }

.langue-pop {
  position: absolute; top: calc(100% + .45rem); right: 0; z-index: 5;
  width: min(270px, calc(100vw - 32px)); padding: .5rem;
  display: flex; flex-direction: column; gap: .35rem;
  background: var(--ink-3); color: var(--text); text-align: left;
  border: var(--frame); border-radius: var(--r-3);
  box-shadow: var(--lift-2);
}
.langue-tete { display: flex; align-items: center; justify-content: space-between; padding: 0 .2rem; }
.langue-titre {
  margin: 0; font-family: var(--display); font-weight: 800; font-stretch: var(--ch-ui);
  font-size: var(--fs-xs); letter-spacing: .01em; color: var(--text-dim);
}
.langue-aide-btn {
  appearance: none; cursor: pointer; width: 24px; height: 24px; padding: 0;
  display: grid; place-items: center;
  background: transparent; border: var(--frame); border-radius: var(--r-pill);
  color: var(--text-dim); font-family: var(--display); font-weight: 800; font-size: var(--fs-xs);
}
.langue-aide-btn:hover, .langue-aide-btn[aria-expanded="true"] { color: var(--paper); border-color: var(--paper); }
.langue-aide-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.langue-explique, .langue-etat {
  margin: 0; padding: .45rem .5rem;
  font-size: var(--fs-s); line-height: 1.45; color: var(--text-2);
  background: var(--ink-2); border-radius: var(--r-2);
}
.langue-liste { display: flex; flex-direction: column; gap: 1px; }
.langue-item {
  appearance: none; cursor: pointer; text-align: left;
  display: flex; align-items: center; gap: .55rem;
  width: 100%; padding: .5rem .55rem;
  background: transparent; border: 0; border-radius: var(--r-2);
  color: var(--text);
  font-family: var(--display); font-stretch: var(--ch-ui); font-weight: 700; font-size: var(--fs-s);
}
.langue-item:hover { background: var(--ink-2); color: var(--paper); }
.langue-item:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.langue-item[aria-pressed="true"] { box-shadow: inset 0 0 0 1px var(--paper); color: var(--paper); }
.langue-item-nom { flex: 1 1 auto; }
.langue-bientot {
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: .04em; color: var(--text-dim);
}
.langue-autre { display: flex; flex-direction: column; gap: .25rem; padding: .35rem .2rem 0; border-top: var(--frame-dim); }
.langue-autre-mot { font-size: var(--fs-xs); color: var(--text-dim); }
.langue-champ {
  width: 100%; min-height: 36px; padding: .4rem .55rem;
  background: var(--ink-2); color: var(--text);
  border: var(--frame); border-radius: var(--r-2);
  font: inherit; font-size: var(--fs-s);
}
.langue-champ:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

/* Sur l accueil : en haut a droite du panneau, sans filet. */
/* ⚠️⚠️ V501 - SON COIN EST UN JETON, PARCE QUE LE PANNEAU DOIT LUI FAIRE
   DE LA PLACE. Mesure a 375 px apres la E3 : le titre, passe a 9vw et pleine
   largeur, remontait SOUS le drapeau - le selecteur se posait sur le « E » de
   SNAPLINE. Avant la E3 le titre etait petit et cale a gauche, la question ne
   se posait pas. Le panneau reserve donc le haut du selecteur (voir
   .landing-panel), et il le LIT au lieu de le recopier. */
.landing { --langue-haut: .9rem; }
.langue--accueil { position: absolute; top: var(--langue-haut); right: var(--langue-haut); z-index: 2; }
.langue-etat-btn {
  appearance: none; cursor: pointer; display: block; margin-top: .45rem;
  padding: .4rem .7rem; border-radius: var(--r-2);
  background: transparent; color: var(--paper); border: 1px solid var(--paper);
  font-family: var(--display); font-weight: 800; font-size: var(--fs-xs);
  letter-spacing: .01em;
}
.langue-etat-btn:hover { background: var(--ink-3); }
.langue-etat-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* Traduction automatique en cours : un point sous le drapeau le dit. */
.langue-btn.is-auto .langue-drapeau { position: relative; }
.langue-btn.is-auto .langue-drapeau::after {
  content: ""; position: absolute; right: -3px; bottom: -3px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--paper); box-shadow: 0 0 0 2px var(--ink-1);
}
