/*
 * LA FEUILLE DE STYLE DE L'APP — sortie du document le 13/09/2026.
 *
 * 1 878 lignes et 119 Ko qui vivaient dans un `<style>` au milieu des
 * 12 508 lignes de `app/index.html`. Le document tombe a ~557 Ko, et le CSS
 * s'ouvre, se cherche et se relit seul.
 *
 * ⚠️⚠️ CE FICHIER NE PORTE PAS LE SPLASH. Le bloc qui pose le fond noir reste
 * INLINE et AVANT le script Tailwind : Tailwind est bloquant, et un style
 * pose apres lui laisse la page BLANCHE au premier chargement — sur une app
 * dont toute la DA est un noir profond. `test/splash.test.js` tient cet ordre.
 *
 * ⚠️ Les polices sont chargees par un `<link>` du document, plus par un
 * `@import` d'ici : dans une feuille EXTERNE, un `@import` ne se decouvre
 * qu'une fois la feuille telechargee — une requete en serie sur le chemin
 * critique, donc un flash de police systeme sur les titres Montserrat 900.
 *
 * ⛔ ET IL DOIT FIGURER DANS LA LISTE EXPLICITE DE `scripts/build-pages.js`.
 * Un `href` vers un fichier qui n'y est pas donne un 404 muet : c'est ainsi
 * que la page d'accueil du site a servi du Times sur fond blanc pendant des
 * mois. Une garde balaye desormais toutes les references des trois pages.
 */
        /* ══ L'ECHELLE DE PROFONDEUR ═══════════════════════════════════════
           Le fond, les ecrans ET les fenetres valaient tous #080808 : une
           fenetre qui s'ouvrait avait EXACTEMENT la couleur de ce qu'elle
           recouvrait, et les cartes etaient du blanc a 4 % pose dessus, soit
           #121212 en pratique. Il n'y avait litteralement rien a distinguer.

           Une seule regle desormais : plus c'est pres de toi, plus c'est clair. */
        :root {
          /* ⭐⭐ DEUX THEMES DEPUIS LE 19/09/2026 — arbitre par Hakim (« trop de
             noir ») : FONCE (anthracite, le defaut) et CLAIR (la creme, avec le
             bandeau jaune de l'icone sur l'accueil). Le choix vit dans le profil
             (« Choix du theme : clair ou fonce ») et suit le compte, comme le
             reste du profil. Contrat : docs/mockups/moins-sombre.html, C et B.

             LA REGLE QUI TIENT TOUT : ce bloc est le theme FONCE, et le bloc
             `html[data-theme="clair"]` juste dessous REDIT chaque jeton. Aucune
             couleur de texte ni de fond ne s'ecrit en dur ailleurs — sinon elle
             ne suit pas, et ca ne se voit que dans l'autre theme.

             ⚠️⚠️ DEUX FAMILLES D'OR, ET ELLES NE SE CONFONDENT PAS :
             · `--accent` / `--or` sont l'or de TEXTE (titres de section,
               categories, montants…) : jaune sur le fonce, ambre sombre
               (#7A5600) sur le clair, ou le jaune ne se lit pas (1,6:1 sur du
               blanc) ;
             · `--jaune` / `--ambre` sont l'or de REMPLISSAGE (boutons, pastilles,
               coches) : le meme jaune dans les deux themes, avec du noir dessus.
             Un `color:` prend le premier, un `background:` le second. */
          --accent: #FFD700;
          --or: #FBBF24;
          --jaune: #FFD700;
          --ambre: #FBBF24;
          /* Les quatre plans, du fond a la fenetre — anthracite depuis le 19/09
             (le fond etait #0B0B0F). Plus c'est pres de toi, plus c'est clair. */
          --n0: #22232B;   /* le fond : l'ecran, derriere tout            */
          --n1: #2C2D37;   /* les cartes posees dessus                    */
          --n2: #343542;   /* dans une carte : reperes, pastilles, champs */
          --n3: #3C3D4A;   /* la fenetre au premier plan                  */
          --vert: #22C55E;
          --vert-fond: #22C55E;  /* le vert de REMPLISSAGE (boutons « Voir », « J'y suis ») : le meme dans les deux themes */
          --orange-texte: #FDA85A;  /* l'avertissement d'une balade (« prevois un bus »). ⚠️ Sur la PAGE il se lit sur une TEINTE : `.trajet-long` le pose sur un orange a 10 % (rgba(251,146,60,.1)), qui eclaircit le fond — #FB923C faisait 4,74 sur --n3 nu mais 4,03 sur la teinte composee dessus, mesure le 20/09 sur la page d'une balade. #FDA85A : 4,74 sur la teinte, 5,58 nu. Le test compose la teinte sur chaque plan avant de mesurer. ⭐ Dans la LISTE (26/09, `.b-signe.bus`), il est nu sur la carte d'un groupe (--n1) : le test le mesure la aussi. ⚠️ Il s'est defini PAR LUI-MEME (`var(--orange-texte)`) du 19/09 au 20/09 — un cycle, donc une valeur invalide, donc l'avertissement de la liste (`.b-flag`, parti le 26/09) heritait la couleur de son parent, sans un mot. Un test refuse desormais tout jeton qui se cite. */
          --rouge: #FB8B8B;    /* ce qui ferme — remonte de #F87171 : 4,68 sur --n3 */
          --lu-fond: rgba(255,215,0,.10);  /* le paragraphe qu'on est en train d'ECOUTER (21/09). ⚠️ Une TEINTE, donc elle eclaircit le plan sous elle : sur --n3 elle compose un #4F4D43 ou --fumee tomberait a 4,44 — c'est pour ca que `.h-recit p.lu` passe le texte en --craie (14,5), et pas seulement pour le mettre en avant. */
          --craie: #FFFFFF;
          --craie2: #D6D6E0;   /* le texte fort secondaire (text-gray-300) */
          --fumee: #BABAC9;    /* secondaire */
          /* ⚠️ Les deux gris sont CALCULES, pas choisis : le plus SOMBRE qui
             passe 4,5:1 sur les quatre plans (--fumee2, 4,57 sur --n3), puis un
             cran au-dessus pour --fumee (1,22 d'ecart entre les deux).
             test/accessibilite.test.js les recalcule a chaque passe. */
          --fumee2: #A8A8B7;   /* tertiaire — AA sur les quatre plans */
          --trait: rgba(255,255,255,.10);
          --ombre-feuille: rgba(0,0,0,.62);
          --ambre-texte: #FBBF24;            /* la punchline */
          --surligneur: transparent;         /* …et ce qui la surligne, sur le clair */
          --marqueur: transparent;           /* le trait sous un titre de section, sur le clair */
          --pastille-fond: transparent;      /* la categorie d'une carte */
          --pastille-texte: #FFD700;
          --dit-trait: #FFD700;              /* la phrase du moment : son filet… */
          --dit-largeur: 2px;
          --dit-fort-fond: transparent;      /* …et ses mots forts */
          --dit-fort-texte: #FFD700;
          --bandeau-fond: transparent;       /* le bandeau de l'accueil : jaune sur le clair */
          --bandeau-texte: inherit;
          --bandeau-sous: #BABAC9;
          --hist-trait: rgba(255,215,0,.22); /* la carte d'une histoire */
          --hist-voile: rgba(255,215,0,.055);
          --ic-fond: rgba(255,215,0,.11);    /* l'oeil d'une histoire */
          --ic-texte: #FFD700;
          --ic-trait: rgba(255,215,0,.2);
          --chip-texte: #FBBF24;             /* la pastille d'univers d'une balade */
          --chip-fond: rgba(251,191,36,.12);
          --tab-fond: rgba(11,11,15,.96);    /* la barre d'onglets : noire dans les DEUX themes (mockup B) */
          --tab-texte: #82828F;             /* le libelle INACTIF de la barre : 5,15 sur le fonce, 4,85 sur le clair — #6A6A78 faisait 3,67 et 3,45, mesure le 19/09 au soir. La barre est noire dans les deux themes, le jeton ne change pas. */
          --tab-fondu: rgba(34,35,43,.92);   /* le fondu au-dessus d'elle : la couleur du fond */
          --tab-h: 56px;                     /* la hauteur de la barre, zone sure en plus (30/09 : 74 → 56, la barre de Snacky). ⚠️ TOUT ce qui se pose au-dessus d'elle part de ce jeton — la reserve des ecrans, le dock de suivi, la bulle, Raconter, Retour, le viseur. Le Comptoir porte le meme (app/comptoir/base.css), un test compare. */
          --voile: rgba(34,35,43,.82);       /* l'attribution de la carte */
          /* ── Le système d'interface (23/09/2026) — voir plus bas, § LE SYSTÈME ── */
          --r-ctl: 14px;                     /* l'arrondi d'un CONTRÔLE : bouton, champ, ligne de choix, tuile */
          --h-btn: 48px;                     /* la hauteur d'un bouton et d'un champ */
          --trait-fort: rgba(255,255,255,.13); /* le bord d'un contrôle : un cran au-dessus du trait d'une carte */
          --rond-fond: rgba(255,255,255,.09);  /* le rond de Fermer / Retour… */
          --rond-fond-presse: rgba(255,255,255,.17);
          --rouge-fond: #FF3B5C;             /* le rouge de REMPLISSAGE (supprimer son compte) : le même dans les deux thèmes, du noir dessus */
          --toast-fond: rgba(11,11,15,.97);  /* le toast : le noir de la barre d'onglets, dans les deux thèmes */
          /* ⭐ (25/09) La poignée d'une feuille et l'interrupteur éteint deviennent des jetons : la poignée
             était du blanc à 28 % EN DUR — sur la feuille blanche du clair, elle n'existait pas ; et
             l'interrupteur éteint, un gris foncé en dur posé sur la crème. */
          --poignee: rgba(255,255,255,.28);
          --tp-vide: rgba(255,255,255,.14);   /* un point de carte pas encore tamponné (26/09 : il était EN DUR, invisible sur le clair) */
          --inter-off: #3a3a45;
          --inter-bouton-off: #A8A8B7;
        }
        /* ══ LE THEME CLAIR — la creme, et le bandeau jaune (19/09/2026) ═══════
           Chaque jeton du bloc ci-dessus, redit. ⚠️ `--jaune` et `--ambre` ne
           sont PAS redits : un remplissage reste jaune, avec du noir dessus.
           ⚠️ Les gris, le vert et le rouge sont les plus CLAIRS qui passent
           4,5:1 sur les quatre plans clairs — le second bloc du test. */
        html[data-theme="clair"] {
          --accent: #7A5600;
          --or: #7A5600;
          --n0: #F4F1EA;
          --n1: #FFFFFF;
          --n2: #EFEBE1;
          --n3: #FFFFFF;
          --vert: #166534;
          --orange-texte: #9A3412;
          --rouge: #B91C1C;
          --lu-fond: rgba(122,86,0,.10);   /* l'ambre sombre du clair : le jaune vif y serait invisible sur la creme */
          --craie: #0B0B0F;
          --craie2: #2A2A35;
          --fumee: #5A5A68;
          --fumee2: #686876;
          --trait: rgba(11,11,15,.09);
          --ombre-feuille: rgba(11,11,15,.22);
          --ambre-texte: #1F1F28;
          --surligneur: rgba(255,215,0,.34);
          --marqueur: #FFD700;
          --pastille-fond: #FFD700;
          --pastille-texte: #0B0B0F;
          --dit-trait: #0B0B0F;
          --dit-largeur: 3px;
          --dit-fort-fond: rgba(255,255,255,.78);
          --dit-fort-texte: #0B0B0F;
          --bandeau-fond: #FFD700;
          --bandeau-texte: #0B0B0F;
          --bandeau-sous: #3D3300;
          --hist-trait: rgba(255,215,0,.75);
          --hist-voile: rgba(255,215,0,.10);
          --ic-fond: #FFD700;
          --ic-texte: #0B0B0F;
          --ic-trait: transparent;
          --chip-texte: #0B0B0F;
          --chip-fond: #FFD700;
          --tab-fondu: rgba(244,241,234,.92);
          --voile: rgba(244,241,234,.88);
          --poignee: rgba(11,11,15,.22);
          --tp-vide: rgba(11,11,15,.14);
          --inter-off: #CFC8BA;
          --inter-bouton-off: #FFFFFF;
          --trait-fort: rgba(11,11,15,.14);
          --rond-fond: rgba(11,11,15,.06);
          --rond-fond-presse: rgba(11,11,15,.13);
        }
        body { background: var(--n0); color: var(--craie); font-family: 'Inter', sans-serif; min-height: 100dvh; margin: 0; overflow: hidden; }
        .heading { font-family: 'Montserrat', sans-serif; text-transform: uppercase; letter-spacing: -1px; }
        /* Les emojis font partie du ton de ce produit, mais ils subissaient le
           `letter-spacing` de leur parent : 29 `class="e"` etaient poses dans le
           HTML sans qu'aucune regle existe derriere. Sous un titre a -1px ou une
           pastille a .08em, l'espace qui suit l'emoji se faisait avaler. */
        .e { letter-spacing: 0; font-style: normal; display: inline-block; }

        /* ══ LE SYSTÈME D'INTERFACE (23/09/2026) ═══════════════════════════════
           Demande d'Hakim : « les boutons fermer, retour, la couleur, leur design,
           tout doit être aligné et homogène… je ne veux pas d'emojis beaucoup trop
           petits ». Contrat : `docs/mockups/systeme-interface.html`.
           L'audit du DOM (252 états, 375 et 320 px, les deux thèmes) comptait QUATRE
           dessins de « Fermer », TROIS de « Retour », SEPT hauteurs de bouton plein
           (44 à 63 px), deux jaunes de remplissage pour la même action, quatre dessins
           de champ, et 293 emojis de 11 à 16 px DANS DES CONTRÔLES — pastilles,
           étiquettes, pieds de carte, toasts.
           UNE famille par rôle désormais :
           · `.i`         une icône du sprite (`<use href="#i-…">`, en tête du corps) ;
           · `.btn-rond`  Fermer, Retour, Partager, Éditer : un rond de 36, une zone de 44 ;
           · `.bouton`    toute action qui porte un mot — 48 px, arrondi 14, Inter 800 ;
           · `.champ`, `.saisie`  toute saisie — 48 px, 16 px de texte (iOS ne zoome pas) ;
           · `.badge`     une étiquette posée sur une carte ;
           · `.f-sec`     tout titre de section, sur tous les écrans.
           ⭐ LA PLACE DIT LE GESTE : Retour en haut à GAUCHE, Fermer en haut à DROITE,
           l'action en BAS — sur chaque écran, chaque feuille, chaque page.
           ⛔ UN EMOJI N'EST PAS UNE ICÔNE — la règle du 07/09 valait pour les
           contrôles, elle vaut pour tout ce que l'app DESSINE elle-même. Les emojis
           restent dans le TEXTE éditorial, qui est du contenu.
           Garde : `test/systeme-interface.test.js`. */
        .sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
        /* Une icône : le trait de 1,8 des tracés de l'app, la couleur du texte autour. */
        .i {
            width: 20px; height: 20px; flex: 0 0 auto; display: inline-block; vertical-align: middle;
            fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
        }
        .i.plein { fill: currentColor; }

        /* ── LE BOUTON — tout ce qui porte un mot ──────────────────────────────
           Il y en avait huit : `.mur-cta` 53, `.btn-next-step` 63, `.p-cta` 52,
           `.f-btn` 48, `.mc-btn` 46, `.arr-btn-*` 53-55, le pied de la création 48,
           le suivi 45 — en #FFD700 ou en #FBBF24 selon l'écran, en capitales ou
           non, en Montserrat ou en Inter. ⭐ La casse de PHRASE est la seule qui
           vaille pour TOUS : les portes Google, Apple et Facebook ne s'écrivent pas
           en capitales, et un bouton ne doit pas crier plus fort que le titre. */
        .bouton {
            display: inline-flex; align-items: center; justify-content: center; gap: 8px;
            height: var(--h-btn); padding: 0 20px; min-width: 0; box-sizing: border-box;
            border-radius: var(--r-ctl); border: 1px solid var(--trait-fort);
            background: var(--n2); color: var(--craie);
            font-family: 'Inter', sans-serif; font-weight: 800; font-size: 15px; line-height: 1.1;
            letter-spacing: -.1px; text-transform: none; text-decoration: none; text-align: center;
            white-space: nowrap; cursor: pointer; -webkit-tap-highlight-color: transparent;
            transition: filter .15s, background-color .15s, opacity .15s;
        }
        /* ⚠️ Le filet : un tracé posé dans un bouton SANS la classe `.i` n'avait aucune
           taille — 300 × 150, par-dessus la feuille (« Lire la suite », vu le 23/09). */
        .bouton > svg { width: 18px; height: 18px; flex: none; }
        .bouton .i { width: 18px; height: 18px; }
        .bouton.large { display: flex; width: 100%; }
        .bouton.plein { background: var(--ambre); border-color: var(--ambre); color: #191100; }
        .bouton.vert { background: var(--vert-fond); border-color: var(--vert-fond); color: #04220F; }
        .bouton.rouge { background: var(--rouge-fond); border-color: var(--rouge-fond); color: #1A0008; }
        /* Compact : la pilule d'action (44 px, rayon plein) — dans une carte, un dock, sur la carte. */
        .bouton.compact { height: 44px; padding: 0 16px; border-radius: 999px; font-size: 14px; }
        /* Carré : l'action sans mot, à côté d'une action qui en porte un. */
        .bouton.carre { width: var(--h-btn); flex: 0 0 var(--h-btn); padding: 0; }
        .bouton.carre .i { width: 20px; height: 20px; }
        /* Nu : l'action discrète. Pas de fond, pas de soulignement — 44 px quand même. */
        .bouton.nu { height: auto; min-height: 44px; padding: 6px 8px; background: none; border-color: transparent; color: var(--fumee); font-weight: 700; font-size: 14px; line-height: 1.3; white-space: normal; }
        .bouton.nu.alerte { color: var(--rouge); }
        /* Lien : l'action d'accent, dans une phrase ou sous un encart. */
        .bouton.lien { height: 44px; padding: 0 2px; background: none; border-color: transparent; color: var(--accent); }
        .bouton[aria-pressed="true"] { color: var(--or); border-color: rgba(251,191,36,.45); background: rgba(251,191,36,.12); }
        .bouton[aria-pressed="true"] .i { fill: currentColor; }
        .bouton:active { filter: brightness(1.1); }
        .bouton.plein:active, .bouton.vert:active, .bouton.rouge:active { filter: brightness(.93); }
        .bouton:disabled, .bouton[aria-disabled="true"] { opacity: .45; cursor: default; filter: none; }
        /* Un bouton PLEIN éteint passe au neutre : l'ambre à 45 % sur l'anthracite
           donnait un kaki boueux (« Envoyer » de Signaler, vu le 23/09). Gris, il dit
           « pas encore » ; il reprend sa couleur dès qu'on peut toucher. */
        .bouton.plein:disabled, .bouton.vert:disabled, .bouton.rouge:disabled,
        .bouton.plein[aria-disabled="true"] { opacity: 1; background: var(--n2); border-color: var(--trait); color: var(--fumee2); }
        /* ⚠️⚠️ `hidden` NE CACHE PLUS RIEN SANS CETTE LIGNE : une classe qui pose
           `display` bat la feuille du navigateur, et les boutons cachés du profil
           (« Me déconnecter » sans compte…) s'affichaient tous. */
        .bouton[hidden], .btn-rond[hidden], .champ[hidden], .saisie[hidden], .badge[hidden], .avatar[hidden] { display: none; }

        /* ── LE ROND — Fermer, Retour, Partager, Éditer ────────────────────────
           Un dessin de 36, une ZONE de 44 (le plancher du produit), une icône de
           20 au trait de 2. Il remplace la croix grise en carré de 14 des feuilles,
           le chevron or sans fond de la page, le « ✕ » en caractère de la fenêtre
           du passage, le « × » de l'étiquette de la carte et la croix nue de la
           bulle. ⭐ Neutre : l'or est pour ce qu'on FAIT, pas pour ce qu'on quitte. */
        .btn-rond {
            position: relative; width: 36px; height: 36px; flex: 0 0 36px; box-sizing: border-box;
            display: inline-flex; align-items: center; justify-content: center;
            margin: 0; padding: 0; border: 0; border-radius: 50%;
            background: var(--rond-fond); color: var(--craie); cursor: pointer;
            -webkit-tap-highlight-color: transparent; transition: background-color .15s;
        }
        .btn-rond::before { content: ''; position: absolute; inset: -4px; border-radius: 50%; }
        .btn-rond .i { width: 20px; height: 20px; stroke-width: 2; }
        .btn-rond:active { background: var(--rond-fond-presse); }
        .btn-rond.petit { width: 28px; height: 28px; flex-basis: 28px; }
        .btn-rond.petit::before { inset: -8px; }
        .btn-rond.petit .i { width: 16px; height: 16px; }
        .btn-rond.alerte { color: var(--rouge); }
        .btn-rond[aria-pressed="true"] { background: var(--ambre); color: #191100; }
        /* Sur une photo : un rond sombre, lisible sur n'importe quelle image. */
        .btn-rond.sur-image { background: rgba(10,10,12,.6); color: #fff; }
        .btn-rond.sur-image:active { background: rgba(10,10,12,.8); }
        /* Sur le jaune de l'arrivée : l'encre du fanion. */
        .btn-rond.sur-jaune { background: rgba(8,8,8,.1); color: #080808; }
        .btn-rond.sur-jaune:active { background: rgba(8,8,8,.18); }
        /* La rangée du haut d'un écran plein (l'onboarding, les partenaires) : le rond
           de Retour à gauche, aligné sur la gouttière — jamais un lien « ← Retour ». */
        .ecran-nav { display: flex; align-items: center; gap: 14px; min-height: 36px; margin: 0 0 18px; }
        .ecran-nav .pas { flex: 1; margin: 0; }
        /* L'étape 1 garde la place du Retour, vide : la barre ne saute pas d'une étape à l'autre. */
        .ecran-nav .btn-rond[hidden] { display: inline-flex; visibility: hidden; }

        /* ── L'EN-TÊTE D'UN ÉCRAN — un titre, une taille ──────────────────────
           LIEUX 30 px, l'accueil 26, BALADES 24, MES CARTES 24 : quatre onglets,
           trois tailles, trois marges. 26 px partout — la taille que le pire titre
           de l'accueil (« MERCREDI APRÈS-MIDI ») supporte sur une ligne. */
        .ecran-tete { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin: 0 0 20px; }
        .ecran-tete .txt { min-width: 0; }
        .ecran-tete h1 {
            font-family: 'Montserrat', sans-serif; font-weight: 900; text-transform: uppercase;
            letter-spacing: -1px; font-size: 26px; line-height: 1.05; margin: 0;
        }
        .ecran-tete .sous { font-size: 14px; font-style: italic; color: var(--fumee); margin: 6px 0 0; line-height: 1.4; }

        /* ── L'AVATAR — le profil, en haut de l'accueil ──────────────────────
           Il portait un emoji de 14 px et le mot « PROFIL » à 11 px dans un rond de
           48 : deux choses minuscules dans un contrôle. L'initiale du prénom, ou la
           silhouette tant qu'on n'en a pas. */
        .avatar {
            position: relative; width: 40px; height: 40px; flex: 0 0 40px; box-sizing: border-box;
            display: inline-flex; align-items: center; justify-content: center;
            border: 0; padding: 0; border-radius: 50%; cursor: pointer;
            background: var(--ambre); color: #191100;
            font-family: 'Montserrat', sans-serif; font-weight: 900; font-size: 17px; line-height: 1; text-transform: uppercase;
            -webkit-tap-highlight-color: transparent;
        }
        .avatar::before { content: ''; position: absolute; inset: -2px; border-radius: 50%; }
        .avatar .i { width: 20px; height: 20px; stroke-width: 2; }
        .avatar:active { filter: brightness(.93); }

        /* ── LE BADGE — une étiquette sur une carte ────────────────────────────
           « 🎁 FREEBIE » à 12 px dans un rectangle de 8, « 💳 FIDÉLITÉ », « À
           PRENDRE » à 8, « IL PAIE POUR ÊTRE ICI » en pilule : un dessin, trois
           teintes. */
        .badge {
            display: inline-flex; align-items: center; gap: 5px; height: 24px; padding: 0 9px;
            border-radius: 999px; font-size: 11px; font-weight: 800; letter-spacing: .06em;
            text-transform: uppercase; white-space: nowrap; line-height: 1;
        }
        .badge .i { width: 13px; height: 13px; stroke-width: 2.1; }
        .badge.vert { background: var(--vert-fond); color: #04220F; }
        .badge.or { background: var(--jaune); color: #191100; }
        .badge.teinte { background: var(--chip-fond); color: var(--chip-texte); }
        .badge.pris { background: rgba(34,197,94,.16); color: var(--vert); }
        .badge.neutre { background: var(--n2); color: var(--fumee); }
        /* Le trajet entre deux étapes, sur la page d'une balade : le tracé du mode. */
        .trajet, .trajet-titre { display: flex; align-items: center; gap: 6px; }
        .trajet .i, .trajet-titre .i { width: 16px; height: 16px; flex: 0 0 16px; stroke-width: 2; }

        /* ── LA TUILE — l'icône d'une ligne de choix ou d'une question ─────────── */
        .tuile {
            width: 40px; height: 40px; flex: 0 0 40px; border-radius: 12px;
            display: inline-flex; align-items: center; justify-content: center;
            background: var(--n2); color: var(--accent);
        }
        .tuile .i { width: 21px; height: 21px; }
        .tuile.petite { width: 34px; height: 34px; flex-basis: 34px; border-radius: 10px; }
        .tuile.petite .i { width: 18px; height: 18px; }

        /* ── LA SAISIE ──────────────────────────────────────────────────────────
           ⚠️ 16 px, jamais moins : sous 16, iOS ZOOME sur le champ au focus et ne
           redézoome jamais. La recherche de LIEUX était à 14 — le défaut ne se voit
           que sur un vrai iPhone. */
        .saisie {
            display: block; width: 100%; box-sizing: border-box;
            background: var(--n2); border: 1px solid var(--trait-fort); border-radius: var(--r-ctl);
            padding: 13px 14px; color: var(--craie); font-family: inherit; font-size: 16px; line-height: 1.45; outline: 0;
            transition: border-color .15s;
        }
        input.saisie { height: var(--h-btn); padding: 0 14px; }
        textarea.saisie { min-height: 96px; resize: vertical; }
        /* Un champ actif : l'anneau du focus le dit (`:focus-visible`, plus haut) — un filet
           en plus ferait deux cadres. */
        .saisie::placeholder { color: var(--fumee2); opacity: 1; }
        .etiquette {
            display: block; font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em;
            color: var(--fumee2); margin: 20px 0 8px;
        }
        .etiquette span { text-transform: none; letter-spacing: 0; font-weight: 600; }

        /* ══ LE FOCUS AU CLAVIER ═══════════════════════════════════════════
           ⚠️⚠️ Il n'existait NULLE PART : mesure le 10/09/2026, `outline-style`
           valait `none` sur les boutons de toute l'application. Naviguer au
           clavier etait donc impossible — on ne voyait jamais ou l'on etait.
           Ce n'est pas un confort : une ville qui achete une app publique a des
           obligations d'accessibilite, et c'est un argument a avoir AVANT la
           negociation.

           ⭐ `:focus-visible` et non `:focus` : le navigateur ne l'allume qu'au
           CLAVIER. Un doigt ou une souris ne laisse donc aucun anneau derriere
           lui, et le rendu de l'app ne change pas d'un pixel a l'usage normal.
           ⚠️ L'or sur le noir de l'app donne 14:1 — l'anneau se voit sur les
           quatre plans, y compris sur un bouton deja jaune, grace au decalage.
           ⚠️ `!important` parce que plusieurs champs posent `outline: 0` pour
           leur propre style de focus : l'anneau doit passer par-dessus. */
        :focus-visible {
            outline: 2px solid var(--accent) !important;
            outline-offset: 2px;
        }
        /* ⭐ AUCUNE exception pour les boutons deja or, et c'est le point :
           `outline-offset` place l'anneau A COTE du bouton, jamais dessus. Il
           tombe donc toujours sur le fond sombre de l'app, ou l'or donne 12:1.
           ⚠️ J'avais d'abord ecrit une liste de selecteurs or a traiter a part :
           elle ratait deja `.p-cta`, et elle aurait rate le suivant. Une regle
           qui n'a pas de liste a tenir ne peut pas devenir incomplete. */
        
        /* Anciennement une maquette de telephone figee a 375x812, bordure et
             fausse encoche comprises. C'est desormais l'application elle-meme :
             elle prend la hauteur reelle du navigateur (dvh suit la barre
             d'URL mobile) et se centre au-dela de 520 px, faute de quoi les
             listes s'etireraient sur toute la largeur d'un ecran de bureau. */
          /* ⚠️⚠️ `clip`, ET SURTOUT PAS `hidden` : un conteneur `overflow: hidden`
             reste DEFILABLE PAR PROGRAMME, et le navigateur s'en sert tout seul
             pour amener dans la vue l'element qui vient de prendre le focus.
             Or `.page` attend hors-champ a `translateX(100%)` avec un simple
             `visibility: hidden`, qui ne la retire PAS de la boite de
             defilement : le cadre mesurait donc 750 px de large pour 375 px
             visibles. Toucher la derniere pastille d'une rangee qui deborde —
             « Mes balades », « + CRÉER » — faisait glisser TOUTE l'application
             de 43 a 120 px vers la gauche, titre coupe, barre d'onglets
             decalee, et RIEN ne la remettait d'aplomb. Mesure du 16/09/2026 :
             `.iphone.scrollLeft` passait de 0 a 119,5 au clic sur « + CRÉER »,
             et la feuille de creation s'ouvrait amputee d'un tiers. Vu aussi en
             production. `clip` interdit ce defilement-la ; `hidden` le precede
             pour les navigateurs qui ne connaissent pas encore `clip`, et une
             garde JS (lib/cablage.js) rattrape ceux-la.
             ⚠️ Sans risque de mise en page : `hidden` cree un contexte de
             formatage de bloc que `clip` ne cree pas, mais `.iphone` est
             `display: flex` — il a deja le sien. */
          .iphone { width: 100%; max-width: 520px; height: 100dvh; margin: 0 auto;
                    background: var(--n0); position: relative;
                    overflow: hidden; overflow: clip;
                    display: flex; flex-direction: column; }
        .notch { display: none; }   /* fausse encoche de la maquette */
        
        /* !important des deux cotes : le CDN Tailwind est charge APRES cette
             feuille, et sa classe utilitaire .flex battait .screen a specificite
             egale. L'ecran de bienvenue la portait : il restait donc affiche en
             permanence, reapparaissait a pleine luminosite pendant les 400 ms du
             fadeIn a chaque changement d'onglet — le clignotement — et captait la
             premiere tabulation de tous les ecrans. Il a ete retire le 10/09/2026,
             la garde reste : toute classe utilitaire posee sur un `.screen`
             refarait exactement ce defaut. */
        .screen { position: absolute; inset: 0; display: none !important; flex-direction: column; background: var(--n0); z-index: 10; padding-bottom: calc(var(--tab-h) + env(safe-area-inset-bottom)); }
        /* Instantane (08/09) : le fondu de 400 ms et la translation de 20 px
           rejoues a chaque onglet etaient le tic d'une page web, pas d'une app.
           Le mouvement est reserve aux feuilles et aux pages. */
        .screen.active { display: flex !important; }
        
        @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }

        /* La carte etait du blanc a 4 % sur du noir — invisible. Elle est
           desormais OPAQUE et se detache du fond. `backdrop-filter` est retire :
           il ne servait a rien sur un fond uni, et le WebView Android
           l'abandonne de toute facon en silence. */
        .glass { background: var(--n1); border: 1px solid var(--trait); border-radius: 24px; }

        /* ══ LES PHOTOS D'UN LIEU ══════════════════════════════════════════
           `photos: []` sur 54 fiches sur 54 : l'absence est le cas NORMAL, pas
           l'exception. Sans photo, la fiche ne change pas d'un pixel — aucun
           cadre vide, aucun « photo a venir », qui seraient 54 promesses non
           tenues sur un ecran qui marche. */
        .m-photos { background: var(--n2); position: relative; }
        .m-piste {
            display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
            scrollbar-width: none; -ms-overflow-style: none;
        }
        .m-piste::-webkit-scrollbar { display: none; }
        /* ⭐⭐ LA PHOTO ENTIÈRE, JAMAIS ROGNÉE (25/09, contrat docs/mockups/photos-entieres.html, B : « ok pour B,
           code le »). Le cadre est FIXE — la fiche ne saute pas d'une photo à l'autre, et une photo en hauteur ne
           prend pas l'écran — et il est en 4:3 (16:9 jusque-là) : un tiers de place en plus pour ce que les
           commerçants envoient. La photo s'y pose ENTIÈRE (`contain`) ; le reste du cadre est la même photo, floutée
           et assombrie (`.m-fond`). Mesuré à 375 : un logo carré 245 × 245 (56 % en 16:9 rogné), une photo de
           téléphone 163 × 245 (37 %), une photo de l'office en 3:2 327 × 217 (85 %). ⚠️ `filter`, jamais
           `backdrop-filter` (mort dans la WebView Android). */
        .m-vue { flex: 0 0 100%; scroll-snap-align: center; position: relative; aspect-ratio: 4 / 3; overflow: hidden; }
        .m-vue img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; display: block; }
        .m-vue img.m-fond { object-fit: cover; filter: blur(16px) brightness(.5) saturate(1.15); transform: scale(1.15); }
        /* Une image qui ne charge pas laisse le fond du bandeau — jamais un
           cadre blanc ni une icone cassee. */
        .m-vue img.absente { display: none; }

        /* ══ LE BANDEAU D'UNE FICHE SANS PHOTO ═════════════════════════════
           Dessine, pas stocke. Chaque univers sa teinte, prise dans la famille
           sombre du produit : on reconnait la categorie avant de lire.
           ⛔ Aucun credit, aucune legende : il n'y a rien a crediter. */
        /* Le vide du filtre de proximite — il nomme sa cause plutot que de
           laisser un ecran blanc, qui se lit comme une panne. */
        .prox-vide {
            border: 1px dashed rgba(255,255,255,.18); border-radius: 16px;
            padding: 22px 18px; text-align: center; margin: 18px 4px;
        }
        .prox-vide .t {
            font-family: 'Montserrat', sans-serif; font-weight: 900; font-style: italic;
            text-transform: uppercase; font-size: 16px; margin: 0 0 8px; color: var(--craie);
        }
        .prox-vide p { font-size: 13px; color: var(--fumee); margin: 0 0 14px; line-height: 1.55; }
        .prox-vide p b { color: var(--craie); }
        /* Son bouton est un `.bouton.plein.large` (23/09). */

        .m-vide {
            /* ⚠️ 21:9, PLUS COURT que le 16:9 d'une photo, et c'est voulu : une
               photo est du contenu, ce bandeau est un decor. A 16:9 il prenait
               24 % de l'ecran pour ne rien apprendre, et repoussait punchline
               et horaires sous la ligne de flottaison. */
            aspect-ratio: 21 / 9; display: flex; align-items: center; justify-content: center;
            position: relative; overflow: hidden;
            background: linear-gradient(145deg, var(--u-a, #1F1F28), var(--u-b, #262631) 62%, #0E0E14);
        }
        /* Une trame tres basse, pour que l'aplat ne soit pas mort. */
        .m-vide::after {
            content: ''; position: absolute; inset: 0;
            background: repeating-linear-gradient(115deg,
                rgba(255,255,255,.028) 0 2px, transparent 2px 9px);
        }
        .m-vide .v-e { position: absolute; top: 12px; left: 15px; width: 26px; height: 26px; opacity: .45; color: #fff; }
        .m-vide .v-i {
            font-family: 'Montserrat', sans-serif; font-weight: 900; font-size: 72px;
            color: rgba(255,215,0,.17); letter-spacing: -3px; line-height: 1;
        }
        .m-vide[data-u="MANGER"]     { --u-a:#2A1F18; --u-b:#3D2A1C; }
        .m-vide[data-u="BOIRE"]      { --u-a:#16202A; --u-b:#1E2C38; }
        .m-vide[data-u="SORTIR"]     { --u-a:#241826; --u-b:#33223A; }
        .m-vide[data-u="CULTURE"]    { --u-a:#2A1620; --u-b:#3A1E2C; }
        .m-vide[data-u="NATURE"]     { --u-a:#152218; --u-b:#1D3022; }
        .m-vide[data-u="EXPLORER"]   { --u-a:#1B1F24; --u-b:#262D34; }
        .m-vide[data-u="STREET_ART"] { --u-a:#12242A; --u-b:#19333A; }

        /* Le credit est pose SUR l'image, jamais a cote : il ne peut donc pas en
           etre separe par une coupure de mise en page, et il ne vole aucune
           ligne a la fiche. Le degrade le tient lisible sur une photo claire —
           un ciel, une facade blanche : mesure a 5,8 dans ce pire cas. */
        .m-ombre {
            position: absolute; left: 0; right: 0; bottom: 0; height: 52px;
            background: linear-gradient(transparent, rgba(6,6,10,.72)); pointer-events: none;
        }
        /* ⚠️⚠️ IL TENAIT SUR UNE LIGNE, ET LES DIX-NEUF ETAIENT COUPES.
           Mesure du 12/09 : boite de 242 px pour un texte qui en demande jusqu'a
           530 — le plus COURT des 19 credits du catalogue fait 43 signes, la
           boite en tenait 40. Ce qui sautait etait toujours la fin, donc la
           LICENCE : « — Public domain, via Wikimedia Commons ».
           ⛔ Le projet refuse en base un credit vide ou creux (`NON_CREDITS`) et
           tronquait a l'ecran celui qui etait complet. Deux lignes suffisent aux
           dix-neuf : 530 px sur 311 de large font 1,7 ligne. */
        .m-credit {
            position: absolute; right: 8px; bottom: 7px; font-size: 11px; letter-spacing: .02em;
            line-height: 1.35; color: rgba(255,255,255,.92); background: rgba(6,6,10,.62);
            padding: 3px 7px; border-radius: 6px; max-width: calc(100% - 16px);
            overflow: hidden; white-space: normal;
            display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
        }
        /* ⚠️ Avec des points, le credit passe AU-DESSUS d'eux : les deux vivaient
           dans la meme bande de 8 px, et la boite du credit couvrait l'indicateur. */
        .m-photos.multi .m-credit { bottom: 24px; }
        /* Les points n'apparaissent qu'a partir de deux photos. Pas de fleches :
           ce seraient deux cibles de plus dans une fiche deja dense. */
        .m-points { position: absolute; left: 0; right: 0; bottom: 8px; display: flex;
                    justify-content: center; gap: 5px; pointer-events: none; }
        .m-point { width: 5px; height: 5px; border-radius: 999px; background: rgba(255,255,255,.38);
                   transition: width .2s, background .2s; }
        .m-point.on { background: var(--craie); width: 14px; }
        .m-legende { font-size: 13px; font-style: italic; color: var(--fumee);
                     margin: 0; padding: 9px 24px 0; }

        /* ══ LA FEUILLE ════════════════════════════════════════════════════
           Contrat : docs/mockups/feuilles.html (08/09). Une seule feuille
           pour la fiche, le groupe, le profil, ses choix, la creation, la
           legende et le suivi. Elle vit au niveau de l'application, SOEUR
           des ecrans — jamais dans un `.screen` (cf. le flou plus haut).
           Elle passe PAR-DESSUS la barre d'onglets : ses actions tombent
           sous le pouce, la ou les onglets etaient. */
        .feuille {
            position: absolute; left: 0; right: 0; top: 0; height: 100%;
            z-index: 200; background: var(--n3);
            /* Le fond qui respire sous la zone fixe. Voir `.f-saisie`. */
            --bleed-fixe: 6px;
            --n1: #2E2E3A; --n2: #383846;      /* l'echelle est relative au plan porteur — le clair la redit plus bas */
            border-radius: 22px 22px 0 0;
            box-shadow: 0 -12px 44px var(--ombre-feuille), 0 0 0 1px var(--trait);
            display: flex; flex-direction: column;
            transform: translateY(100%); will-change: transform;
            touch-action: none; visibility: hidden;
            /* En pleine hauteur, 8 % de la feuille restent sous le bord de l'ecran :
               ce coussin ramene le pied ancre et la fin du corps AU bord. */
            padding-bottom: calc(8dvh + env(safe-area-inset-bottom));
        }
        .feuille.visible { visibility: visible; }
        .feuille.anim { transition: transform .42s cubic-bezier(.2,.9,.3,1); }
        .feuille.au-dessus { z-index: 210; }
        /* ⚠️⚠️ UNE FEUILLE SOUS UNE AUTRE EST UN FOND — elle recule ET elle
           devient INTOUCHABLE, exactement comme l'ecran sous une feuille.
           Le recul visuel etait la depuis le 08/09 ; l'inertie manquait, et
           c'est mot pour mot la regression corrigee ce jour-la un cran plus
           bas. Mesure du 13/09, fiche en pleine hauteur sous « Signaler » :
           sa croix repondait encore, 227 px au-dessus du formulaire — le
           doigt qui croit fermer le formulaire fermait la FICHE, et la
           feuille restait, orpheline, au-dessus de la liste. Idem le profil
           sous une feuille de choix : la question changeait sous le doigt
           sans qu'aucun geste ait touche la feuille.
           ⚠️ Le flou et l'inertie vont ENSEMBLE : un fond net et fige se lit
           comme une panne. Une seule classe les porte, posee par majRecul(). */
        .feuille.recule { transform-origin: 50% 0; filter: brightness(.72); pointer-events: none; }
        .feuille.recule.visible { transition: filter .42s; }
        /* ⚠️⚠️ ELLE PASSE AU-DESSUS DU CORPS, et ce n'est pas decoratif.
           `.f-corps` monte de 4 px (`margin-top: -4px`, voir plus bas) pour
           sauver l'anneau de focus : sa boite de rognage empiete donc sur les
           4 derniers pixels de CETTE zone — et comme elle vient APRES dans le
           document, ce qui defile s'y PEINT PAR-DESSUS.
           Mesure du 13/09, fiche de BPS22 : des 30 px de defilement, la photo
           recouvrait le bas de la pastille « 💳 FIDÉLITÉ » — coin arrondi
           rase, pastille tranchee net. Signale par Hakim : « quand on scroll,
           les pills sont coupes ». Ca valait pour les sept feuilles, partout
           ou la zone fixe se termine par quelque chose qu'on voit.
           ⭐ Un plan, pas un decalage : le contenu ne bouge d'aucun pixel, et
           l'anneau garde ses 4 px — il commence EXACTEMENT ou cette zone finit. */
        .f-saisie { flex: 0 0 auto; cursor: grab; user-select: none; -webkit-user-select: none;
                    position: relative; z-index: 1;
                    /* ⭐ OPAQUE, et le fond vient de la feuille elle-meme. Le plan
                       ci-dessus ne suffisait pas : la ou aucun enfant ne peint —
                       a DROITE de la pastille — le contenu se voyait EN TRANSPARENCE
                       a travers les 4 px d'empietement, et la photo remontait de
                       4 px a cote de la pastille intacte. ⛔ Ni `var(--n3)` recopie
                       ni une valeur en dur : `inherit` suit la feuille, y compris
                       le jour ou l'une d'elles changera de fond.
                       ⚠️ Le RAYON s'herite avec : un fond opaque en tete d'une
                       feuille a coins arrondis carre ses deux coins du haut. */
                    background: inherit; border-radius: inherit;
                    /* ⭐ LE BLEED, demande par Hakim : quelques pixels de fond sous
                       la zone fixe, pour que ce qui defile ne vienne pas se coller
                       au bas d'une pastille. Il COUTE cette hauteur — la place ne
                       peut pas venir d'ailleurs : l'anneau de focus garde ses 4 px
                       au-dessus du premier element du corps, et masquer plus bas
                       que ca mangerait l'anneau au lieu du vide. */
                    padding-bottom: var(--bleed-fixe);
                    transition: box-shadow .15s;
        }
        .f-saisie:active { cursor: grabbing; }
        /* ⭐ LE BORD (25/09, contrat docs/mockups/profil-onglets.html, Q3 : « toutes les feuilles »).
           Ce qui défile passait sous la zone fixe SANS AUCUN BORD : « Sauvegardé il y a 14 min »,
           tranché à mi-hauteur sous « Salut Hakim », se lisait comme un chevauchement. Un filet
           ferme la zone dès que le corps a bougé — posé par le moteur (`majBord`), jamais à la main. */
        .f-saisie.bord { box-shadow: 0 1px 0 var(--trait); }
        .f-poignee { width: 36px; height: 5px; border-radius: 99px; background: var(--poignee); margin: 8px auto 0; }
        /* ⭐ LA TÊTE D'UNE FEUILLE (23/09) : Retour à gauche (une étape en arrière,
           `data-retour`), le titre, Fermer à droite — deux `.btn-rond`, le bord de
           chaque rond sur la gouttière de 24 px, comme le texte dessous. La croix
           mordait 6 px dans la marge et la gouttière de droite valait 20. */
        .f-tete { display: flex; align-items: flex-start; gap: 12px; padding: 12px 24px 0; }
        .f-tete .txt { flex: 1; min-width: 0; }
        .f-tete .btn-rond { margin-top: -2px; }
        /* ⛔ UN titre de feuille, UNE taille : 18, 20, 22 et 26 px vivaient en styles inline. */
        .f-nom { font-size: 22px; line-height: 1.05; margin: 2px 0 0; }
        .f-apercu { padding: 0 24px; }
        /* ⚠️⚠️ LES 4 PX DU HAUT NE SONT PAS UNE MARGE — ils sauvent l'anneau
           de focus. `overflow-y: auto` ROGNE ce qui depasse, et l'anneau
           d'accessibilite deborde de 4 px (2 de trait + 2 d'`outline-offset`,
           qui le place a COTE du champ, sur le fond sombre). Quand le premier
           element du corps est un champ — l'etape 2 de « Raconter » commence
           par son grand textarea — son anneau se faisait couper NET sur le
           haut : trois cotes dores, le quatrieme absent.
           ⛔ La marge negative ANNULE le decalage : le contenu ne bouge pas
           d'un pixel dans les sept feuilles, seule la boite de rognage monte. */
        .f-corps {
            flex: 1; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
            padding: 8px 24px 24px; margin-top: -4px;
            scrollbar-width: none; -webkit-overflow-scrolling: touch;
            touch-action: pan-y;
        }
        .f-corps::-webkit-scrollbar { display: none; }
        .f-desc { font-size: 14px; color: var(--fumee); line-height: 1.55; margin: 14px 0 0; }
        .f-ligne { display: flex; align-items: flex-start; gap: 9px; font-size: 13px; color: var(--craie2); margin: 10px 0 0; font-weight: 600; line-height: 1.45; }
        .f-ligne > .i { width: 17px; height: 17px; flex: 0 0 17px; margin-top: 1px; color: var(--fumee2); }
        .f-ligne .bout .i { width: 16px; height: 16px; margin: -2px 4px 0 0; color: var(--fumee2); }
        /* ⭐ F37 — LE CONTACT : le dessin d'une `.f-ligne` (icone 17 px, meme gris), mais la LIGNE ENTIERE
           est la cible (44 px) et le geste s'ecrit a droite dans l'or de TEXTE (`--or`), jamais le jaune de
           remplissage. ⛔ La valeur ne se coupe pas : un numero tronque se compose faux. */
        .contact { margin: 12px 0 0; border-top: 1px solid var(--trait); }
        .f-lien { display: flex; align-items: center; gap: 9px; min-height: 44px; border-bottom: 1px solid var(--trait);
                  color: var(--craie2); text-decoration: none; font-size: 13px; font-weight: 600; -webkit-tap-highlight-color: transparent; }
        .f-lien > .i { width: 17px; height: 17px; flex: 0 0 17px; color: var(--fumee2); }
        .f-lien .val { flex: 1; min-width: 0; white-space: nowrap; overflow-wrap: normal; font-variant-numeric: tabular-nums; }
        .f-lien .geste { flex: none; color: var(--or); font-weight: 800; white-space: nowrap; display: inline-flex; align-items: center; gap: 4px; }
        .f-lien .geste .i { width: 15px; height: 15px; }
        .f-lien:active { background: var(--n2); }
        #m-trajets { display: block; }
        #m-trajets .bout { white-space: nowrap; display: inline-block; }
        .f-actions { display: flex; gap: 8px; margin-top: 16px; }
        /* Les actions d'une fiche sont des `.bouton` (23/09) : la rangée ne leur
           donne que leur part de la largeur. */
        .f-actions > .bouton { flex: 1; min-width: 0; padding: 0 10px; }
        /* ⚠️ Le coeur allume est ROUGE, l'etoile OR — la meme convention que le
           rail des vignettes portait. Deux collections, deux couleurs, et jamais
           l'une prise pour l'autre. */
        .bouton.coeur[aria-pressed="true"] { color: var(--rouge); border-color: rgba(248,113,113,.45); background: rgba(248,113,113,.12); }
        /* Les deux rangees se suivent sans se coller. */
        .f-actions + .f-actions { margin-top: 8px; }
        /* ⚠️ MESURE, pas supposition : a 320 px — le plancher du projet — trois
           actions ne tiennent plus a 15 px. « Partager » debordait sa boite de
           4 px, en silence (nowrap + overflow), donc rogne des deux cotes. */
        @media (max-width: 360px) {
            .f-actions { gap: 6px; }
            .f-actions > .bouton { font-size: 14px; gap: 6px; }
            .f-actions > .bouton .i { width: 17px; height: 17px; }
        }
        /* ⚠️ L'histoire ne peut PAS mettre ses trois actions sur une ligne : son
           bouton de nature porte un libelle variable, et « La ville d'avant »
           fait a lui seul la largeur de deux boutons. Il prend donc la premiere
           ligne entiere — c'est le geste principal, il merite la place — et les
           deux actions secondaires se partagent la seconde. */
        #histoire-feuille .f-actions { flex-wrap: wrap; }
        #histoire-feuille #h-action { flex: 1 0 100%; }
        /* ⛔ « Lire la suite » n'existe plus une fois qu'on lit. Les deux autres
           libellés restent : ils ouvrent le comparateur ou lancent la voix. */
        #histoire-feuille.pleine #h-action.mene-au-recit { display: none; }
        .f-vignette { width: 64px; height: 64px; border-radius: 14px; object-fit: cover; flex: 0 0 64px; margin-top: 2px; display: block; }
        /* « D'où ça vient » : quatre sources verifiees sur une histoire, une
           ligne sur une fiche. Meme bloc, meme place — c'est la que la
           provenance se lit, et c'est pour ca que Signaler est juste dessous. */
        .f-source { font-size: 14px; color: var(--fumee); line-height: 1.5; margin: 0; }
        /* La provenance : un sceau, qui a publie, et ce que ca garantit. C'est
           la seule chose qui distingue une histoire de l'office du souvenir d'un
           habitant — ⛔ aucun badge ailleurs, ni sur la map, ni dans les listes. */
        .provenance { display: flex; gap: 11px; align-items: flex-start; background: var(--n2);
            border: 1px solid var(--trait); border-radius: 14px; padding: 13px 14px; }
        .provenance .sceau { flex: 0 0 34px; width: 34px; height: 34px; border-radius: 11px;
            display: flex; align-items: center; justify-content: center;
            background: rgba(255,215,0,.14); color: var(--accent); }
        .provenance .sceau svg { width: 18px; height: 18px; }
        .provenance.habitant .sceau { background: rgba(255,255,255,.07); color: #C7C7D4; }
        .provenance .qui { font-size: 14px; font-weight: 800; line-height: 1.35; margin: 0; }
        .provenance .quoi { font-size: 13px; line-height: 1.45; margin: 3px 0 0; color: var(--fumee2); }
        /* Sur un drame, l'interface se tait : le sceau perd son or comme le
           reste de la feuille. */
        #histoire-feuille.grave .provenance .sceau { background: rgba(214,214,224,.12); color: var(--craie2); }
        /* Signaler est EN BAS, discret : on veut qu'on PUISSE, pas qu'on y soit
           invite. Sans bordure, mais 44 px de haut quand meme — le doigt ne sait
           pas que c'est un controle secondaire. */
        /* C'est un `.bouton.nu` (23/09) : il ne garde ici que sa place, calé sur la gouttière. */
        .f-signaler { margin: 10px 0 0 -8px; }
        /* La feuille de signalement : cinq motifs, un envoi, un mot de fin. */
        .sig-question { font-size: 14px; color: var(--fumee); margin: 2px 0 0; line-height: 1.5; }
        .sig-motif {
            display: block; width: 100%; text-align: left; margin-bottom: 10px; padding: 15px 16px;
            border-radius: 14px; border: 1px solid var(--trait); background: var(--n2); color: var(--craie);
            font-family: 'Inter', sans-serif; font-weight: 800; font-size: 15px; line-height: 1.3;
            cursor: pointer; -webkit-tap-highlight-color: transparent;
        }
        .sig-motif[aria-checked="true"] { border-color: var(--ambre); background: rgba(251,191,36,.12); color: var(--or); }
        .sig-motif .note { display: block; font-weight: 700; font-size: 12px; color: var(--fumee2); margin-top: 4px; }
        .sig-motif[aria-checked="true"] .note { color: rgba(251,191,36,.8); }
        .sig-envoyer { width: 100%; margin-top: 8px; }
        .sig-merci-txt { font-size: 14px; color: var(--fumee); line-height: 1.6; margin: 0 0 10px; }

        /* ══ RACONTER — la porte, et le formulaire ═══════════════════════
           Contrat : docs/mockups/histoire-proposee.html. L'echelle de texte du
           07/09 s'applique : rien sous 11 px, le corps a 14. */
        .porte-raconter {
            background: rgba(255, 215, 0, .07); border: 1px solid rgba(255, 215, 0, .22);
            border-radius: 16px; padding: 14px 15px; margin: 18px 0 4px;
        }
        .porte-raconter p { margin: 0; font-size: 14px; line-height: 1.5; color: var(--craie); }
        .pr-lien { margin-top: 6px; }
        /* ⚠️ Sur une histoire GRAVE, l'interface se tait — l'or devient gris.
           Meme regle que la bulle de la carte et le bandeau (08/09). */
        #histoire-feuille.grave .porte-raconter { background: rgba(255,255,255,.05); border-color: var(--trait); }
        #histoire-feuille.grave .pr-lien { color: var(--craie2); }

        .rc-sous { font-size: 13px; color: var(--fumee2); margin: 0; line-height: 1.5; }
        /* Les étiquettes et les champs sont `.etiquette` et `.saisie` (23/09) : un dessin
           pour toute l'app. ⚠️ 16 px de texte, toujours — voir `.saisie`. */
        .rc-label { margin-top: 16px; }
        .rc-haut { min-height: 132px; }
        .rc-aide { font-size: 12px; color: var(--fumee2); margin: 6px 0 0; line-height: 1.5; }
        .rc-aide b { color: var(--fumee); }
        /* Dans le pied, au-dessus des boutons (19/09) : la marge passe SOUS le message. */
        .rc-mal { font-size: 13px; color: var(--rouge); margin: 0 0 10px; line-height: 1.45; }
        /* La porte d'une étape de Raconter : le dessin du choix de l'onboarding —
           une tuile, deux lignes (23/09). */
        .rc-ou {
            display: flex; align-items: center; gap: 14px; width: 100%; text-align: left;
            background: var(--n2); border: 1px solid var(--trait); border-radius: var(--r-ctl);
            padding: 12px 14px; margin-bottom: 8px; color: var(--fumee); cursor: pointer; min-height: 64px;
        }
        .rc-ou .tuile { background: var(--n3); }
        .rc-ou svg { width: 20px; height: 20px; flex: 0 0 auto; }
        .rc-ou b { display: block; font-size: 15px; font-weight: 700; color: var(--craie); line-height: 1.25; }
        .rc-ou span span { display: block; font-size: 12px; color: var(--fumee2); margin-top: 2px; line-height: 1.4; }
        .rc-ou[aria-pressed="true"] { border-color: var(--ambre); background: rgba(251,191,36,.12); color: var(--or); }
        .rc-ou[aria-pressed="true"] .tuile { background: var(--ambre); color: #191100; }
        .rc-resultats { margin-top: 8px; max-height: 210px; overflow-y: auto; }
        .rc-resultats button {
            display: block; width: 100%; text-align: left; background: none; border: 0;
            border-bottom: 1px solid var(--trait); padding: 12px 2px; min-height: 44px;
            font-size: 14px; color: var(--craie); cursor: pointer;
        }
        .rc-resultats button span { display: block; font-size: 12px; color: var(--fumee2); margin-top: 2px; }
        .rc-deux { display: flex; gap: 8px; }
        .rc-deux button {
            flex: 1; background: var(--n2); border: 1px solid var(--trait); border-radius: var(--r-ctl);
            padding: 13px 6px; min-height: 44px; font-size: 14px; color: var(--fumee); cursor: pointer;
        }
        .rc-deux button[aria-pressed="true"] {
            background: var(--n3); border-color: var(--ambre); color: var(--craie); font-weight: 800;
        }
        /* La zone pointillée « Choisir une image » est partie le 01/10 : l'étape 3 prend
           la porte de l'étape 2 (`.rc-ou` + `.rc-lien`) — docs/mockups/photo-sur-place.html. */
        .rc-vignette {
            display: flex; gap: 12px; align-items: center; background: var(--n2);
            border: 1px solid var(--trait); border-radius: 14px; padding: 10px;
        }
        .rc-vignette img { width: 66px; height: 66px; object-fit: cover; border-radius: 10px; flex: 0 0 auto; background: var(--n3); }
        .rc-vignette .v-n { flex: 1; min-width: 0; font-size: 13px; color: var(--fumee); }
        .rc-vignette .v-n b { display: block; color: var(--craie); font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        /* « Retirer » est un `.btn-rond` (23/09) — la croix du système, zone de 44. */
        .rc-vignette .v-x { margin-right: 4px; }
        .rc-case {
            display: flex; gap: 10px; align-items: flex-start; width: 100%; text-align: left;
            background: var(--n2); border: 1px solid var(--trait); border-radius: var(--r-ctl);
            padding: 12px 13px; margin-top: 10px; color: var(--fumee);
            font-size: 13px; line-height: 1.45; cursor: pointer;
        }
        .rc-case .coche {
            width: 20px; height: 20px; border-radius: 6px; border: 1.5px solid var(--fumee2);
            flex: 0 0 auto; margin-top: 1px; display: grid; place-items: center;
        }
        .rc-case .coche svg { width: 12px; height: 12px; opacity: 0; color: #141018; stroke-width: 3.4; }
        .rc-case[aria-pressed="true"] { border-color: var(--ambre); color: var(--craie); }
        .rc-case[aria-pressed="true"] .coche { background: var(--ambre); border-color: var(--ambre); }
        .rc-case[aria-pressed="true"] .coche svg { opacity: 1; }
        .rc-cta { width: 100%; margin-top: 16px; }
        /* ── LA VOIX D'UN HABITANT (21/09) — contrat : docs/mockups/voix-habitant.html ──
           Le poste d'enregistrement est celui de l'Atelier, dans la DA de l'app ; le
           lecteur est `.lecteur`, celui de la feuille d'une histoire — un seul dessin.
           ⚠️ 44 px partout : la porte (`.rc-ou`), « Arrêter » (`.f-btn`, 48), les
           liens (`.rc-lien`), le bouton de lecture (48), les deux réponses de « Qui
           parle » (`.rc-deux`), la case de l'accord (`.rc-case`).
           ⚠️ « Arrêter » est une TEINTE rouge sous un texte `--rouge`, comme le cœur
           pressé : il n'existe aucun jeton de REMPLISSAGE rouge, et un fond `--rouge`
           serait illisible sur le clair (où il vaut un rouge sombre). */
        /* `.rc-lien` est un `.bouton.nu` depuis le 23/09 : plus de soulignement — un
           lien souligné dans une app se lit comme une page web. */
        .rc-lien { padding: 0 2px; }
        .rc-enr {
            background: var(--n2); border: 1px solid var(--trait); border-radius: 14px;
            padding: 16px 14px; text-align: center; margin-bottom: 12px;
        }
        .rc-enr.actif { border-color: rgba(248,113,113,.55); background: rgba(248,113,113,.06); }
        .rc-enr-attente { font-size: 13px; color: var(--fumee2); margin: 0 0 8px; line-height: 1.5; }
        .rc-pastille {
            display: inline-flex; align-items: center; gap: 8px; margin: 0;
            font-size: 11px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--rouge);
        }
        .rc-point { width: 9px; height: 9px; border-radius: 50%; background: var(--rouge); animation: rc-bat 1.1s infinite; }
        @keyframes rc-bat { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
        .rc-chrono {
            font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: 36px; line-height: 1;
            letter-spacing: .02em; margin: 8px 0 4px; font-variant-numeric: tabular-nums; color: var(--craie);
        }
        .rc-enr-meta { margin: 0; }
        .rc-jauge { height: 4px; background: rgba(255,255,255,.12); border-radius: 999px; overflow: hidden; margin: 12px 0; }
        html[data-theme="clair"] .rc-jauge { background: rgba(11,11,15,.12); }
        .rc-jauge i { display: block; height: 100%; width: 0; background: var(--vert-fond); border-radius: 999px; }
        .rc-jauge i.chaud { background: var(--ambre); }
        .rc-jauge i.plein { background: rgba(248,113,113,.85); }   /* un filet de 4 px, pas un texte */
        .rc-enr-rangee { display: flex; gap: 8px; align-items: center; }
        .rc-enr-rangee .rc-lien { flex: 0 0 auto; padding: 0 12px; }
        .bouton.rc-arreter { color: var(--rouge); border-color: rgba(248,113,113,.45); background: rgba(248,113,113,.12); }
        .rc-apres-voix { display: flex; gap: 4px; margin: 2px 0 0; }
        .rc-court { min-height: 64px; }
        #raconter-feuille .lecteur { margin-top: 0; }
        /* Pendant que ça tourne, le pied ne mène nulle part : on arrête d'abord. */
        #rc-pied .bouton[disabled] { pointer-events: none; }   /* le dessin éteint : `.bouton.plein:disabled` */
        /* L'ancrage : le pied reste sous le pouce pendant qu'on écrit. Le fond
           est OPAQUE — sans lui, le texte défile en transparence sous le bouton
           et les deux deviennent illisibles. Le dégradé au-dessus dit qu'il
           reste du contenu, au lieu de le couper net. */
        /* ⚠️⚠️ `var(--n3)`, ET SURTOUT PAS `var(--n1)` : une FEUILLE redefinit
           `--n1` et `--n2` un plan PLUS CLAIR (« plus c'est pres de toi, plus
           c'est clair »), mais son propre fond reste `--n3`. Un pied ancre en
           `--n1` peignait donc #2E2E3A sur une feuille #262631 — un PAVE GRIS
           visible sous « Revenir », de la hauteur du coussin, et un halo clair
           au-dessus de « Continuer » puisque le degrade portait la meme
           mauvaise couleur. Mesure : fond du pied rgb(46,46,58), fond de la
           feuille rgb(38,38,49).
           ⚠️ L'onboarding, lui, est juste : `.btn-next-step.ancre` peint
           `--n0` et `.screen` EST `--n0`. C'est en recopiant ce motif dans une
           feuille que la couleur a cesse d'etre la bonne. */
        #rc-pied.ancre {
            position: sticky; bottom: 0; z-index: 2; background: var(--n3);
            /* Le double dégradé, comme `.btn-next-step.ancre` de l'onboarding :
               il évite que le contenu semble passer AU TRAVERS du bouton. */
            box-shadow: 0 -16px 20px -6px var(--n3), 0 -32px 20px -18px var(--n3);
            /* ⚠️⚠️ LE COUSSIN PASSE DU CORPS AU PIED, et ce n'est pas cosmétique :
               `bottom: 0` colle au bord du PADDING du conteneur, pas au bord de
               la feuille. Les 24 px de coussin laissaient donc une bande sous le
               pied où la fin de l'étape s'affichait — lisible, et intouchable.
               Vu à l'écran ; aucune mesure de largeur ne l'aurait dit. */
            padding-bottom: 24px;
        }
        #raconter-feuille .f-corps.a-pied-ancre { padding-bottom: 0; }
        /* ⭐ LE PIED EST AU BAS DE LA FEUILLE À CHAQUE ÉTAPE (23/09), ancré ou non :
           « Continuer » tombait sous le contenu aux étapes 1 et 3 et au bord de la
           feuille aux étapes 2 et 4 — il changeait de place d'un écran à l'autre du
           même geste. Le corps devient une colonne, le pied prend la marge qui
           reste. ⚠️ L'ancrage (collé pendant qu'on défile) n'est pas touché : il
           reste DÉRIVÉ des champs de l'étape, dans `raconterEtape`. */
        #raconter-feuille .f-corps { display: flex; flex-direction: column; }
        /* ⚠️ Ce qu'on fait défiler vers soi (le focus d'un champ, `scrollIntoView`)
           s'arrête AU-DESSUS du pied ancré, jamais dessous : 48 + 16 + 24 + marge. */
        #raconter-feuille .f-corps.a-pied-ancre { scroll-padding-bottom: 104px; }
        #raconter-feuille .f-corps > * { flex-shrink: 0; }
        #rc-pied { margin-top: auto; }
        .rc-retour { width: 100%; margin-top: 8px; }
        .rc-fini { text-align: center; padding: 20px 0 6px; }
        .rc-fini .rond {
            width: 60px; height: 60px; border-radius: 50%; background: var(--vert-fond);
            display: grid; place-items: center; margin: 0 auto 16px;
        }
        .rc-fini .rond svg { width: 28px; height: 28px; }
        /* ⭐ La ligne qui porte le surnom — « Il paraitra sous ‹…› — s'il tient
           debout ». Elle a pris la place du second « C'est note », et c'est elle
           qu'on lit : la tete de la feuille dit deja le titre. */
        .rc-fini-mot { font-size: 15px; color: var(--craie); margin-top: 2px; }
        /* Le viseur du choix d'un point sur la carte. ⭐ On emploie LA carte,
           pas un timbre-poste dans une feuille : on pane la vraie ville. */
        .rc-viseur {
            position: absolute; inset: 0; z-index: 10001; pointer-events: none;
            display: grid; place-items: center;
        }
        .rc-viseur .croix { width: 46px; height: 46px; color: var(--accent); filter: drop-shadow(0 2px 6px rgba(0,0,0,.8)); }
        .rc-barre {
            position: absolute; left: 12px; right: 12px; z-index: 10002;
            /* ⚠️ La zone sure manquait (vu le 30/09) : la barre d'onglets la compte, pas le viseur — sur un
               iPhone a barre d'accueil (34 px), il entrait de 24 px dans les onglets. */
            bottom: calc(var(--tab-h) + 10px + var(--suivi-h, 0px) + env(safe-area-inset-bottom));
            background: var(--n1); border: 1px solid var(--trait); border-radius: 18px; padding: 13px 15px;
        }
        .rc-barre p { margin: 0 0 10px; font-size: 13px; color: var(--fumee); line-height: 1.45; }
        .rc-barre .rangee { display: flex; gap: 8px; }
        .rc-barre .rangee button { flex: 1; min-height: 44px; }
        /* ⭐ LE TITRE DE SECTION — un seul dessin sur tous les écrans (23/09).
           L'accueil écrivait les siens en Inter espacé (`.n-kick`), MES CARTES en
           Montserrat gris sans italique, BALADES en Inter 15 gris : quatre styles pour
           un même rôle. C'est celui de la DA qui reste — Montserrat 900 italique. */
        .f-sec, .n-kick {
            font-family: 'Montserrat', sans-serif; font-weight: 900; font-style: italic; text-transform: uppercase;
            font-size: 13px; letter-spacing: .04em; line-height: 1.25; color: var(--or); margin: 24px 0 12px;
        }
        /* La semaine, en pleine hauteur : sept lignes, aujourd'hui en or. */
        .semaine { margin: 0; padding: 0; list-style: none; font-size: 13px; font-variant-numeric: tabular-nums; }
        .semaine li { display: flex; justify-content: space-between; gap: 14px; padding: 7px 0; border-bottom: 1px solid var(--trait); color: var(--fumee); }
        .semaine li:last-child { border-bottom: 0; }
        .semaine li.auj { color: var(--craie); font-weight: 700; }
        .semaine li.auj span:first-child { color: var(--or); }
        .semaine li .ferme { color: var(--fumee2); }
        .semaine li span:last-child { text-align: right; }
        /* Le bandeau photo ne se montre qu'en pleine hauteur ; en apercu, la
           vignette tient sa place a cote du nom. */
        /* ⚠️ `f-pleine-seule` n'est PAS decoratif : le calcul de l'apercu
           soustrait ces blocs, qui ne seront pas la quand la feuille y sera. */
        .feuille #m-photos { display: none; margin-top: 8px; }
        .feuille.pleine #m-photos { display: block; }
        .feuille.pleine #m-vignette { display: none; }
        .feuille .m-piste { touch-action: pan-x; }
        .feuille .m-legende { padding: 9px 24px 0; }

        /* La liste d'un groupe, la liste des favoris du profil. */
        .grp { display: flex; align-items: center; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--trait); width: 100%; text-align: left; background: none; border-left: 0; border-right: 0; border-top: 0; color: inherit; cursor: pointer; }
        .grp:last-child { border-bottom: 0; }
        .grp .e { font-size: 20px; width: 34px; text-align: center; flex: 0 0 34px; }
        .grp .txt { flex: 1; min-width: 0; }
        .grp .n { font-size: 16px; line-height: 1.1; margin: 0; }
        .grp .h { font-size: 13px; color: var(--fumee); margin: 3px 0 0; }
        .grp .etat { font-size: 13px; color: var(--vert); flex: 0 0 auto; }
        .grp .etat.off { color: var(--fumee2); }
        .grp .chev { width: 16px; height: 16px; color: var(--fumee2); flex: 0 0 16px; }

        /* ══ LE PROFIL, EN DEUX ONGLETS (25/09/2026) ═════════════════════════════
           Contrat : docs/mockups/profil-onglets.html. Hakim : « que ça ne fasse pas plic
           ploc ». Mesuré avant : neuf lignes au même dessin pour quatre natures (le compte,
           les goûts, les réglages, le téléphone), 1 367 px à faire défiler pour 686 de
           feuille, « Me déconnecter » collé au filet de la dernière ligne — et deux réponses
           CENTRÉES, en gris, dans 36 px de vide (138 et 173 px au lieu de 66) : la règle de
           l'état vide (`.vide`, 23/09, plus bas) attrapait la réponse marquée `.r.vide`.
           ⛔⛔ UNE RÉPONSE NE PORTE PLUS JAMAIS `vide` : « Pas encore répondu » est `.r.sans`.
           TROIS RÈGLES :
           · UN GROUPE = UNE CARTE (le plan des encarts de feuille, --n1, arrondi 16) ; un
             filet ENTRE deux lignes, décalé après la tuile, jamais sous la dernière ;
           · LA QUESTION EN PETIT GRIS, LA RÉPONSE EN BLANC — l'or ne reste qu'aux titres
             de groupe, aux icônes et à l'onglet actif ;
           · LE BORD DROIT DIT LE GESTE : un chevron ouvre une feuille de choix, un
             interrupteur bascule sur place, un segment choisit sur place. */
        .segment.profil-onglets { display: flex; margin: 14px 24px 0; }
        .profil-onglets .pilule { flex: 1 1 0; min-width: 0; justify-content: center; padding: 0 10px; }
        [data-profil-panneau] > .f-sec:first-child { margin-top: 12px; }
        .f-sous { font-size: 13px; color: var(--fumee); line-height: 1.4; margin: -4px 0 10px; }
        .groupe { margin: 0; padding: 0 14px; list-style: none; background: var(--n1); border: 1px solid var(--trait); border-radius: 16px; }
        .groupe > li, .groupe > .grp { position: relative; }
        .groupe > li + li::before, .groupe > .grp + .grp::before {
            content: ''; position: absolute; top: 0; left: 46px; right: 0; height: 1px; background: var(--trait); }
        /* ⭐ Une ligne qu'on touche est un vrai `<button>` : le `<li role="button">` d'avant ne
           répondait ni à Entrée ni à Espace. */
        .groupe .ligne { display: flex; align-items: center; gap: 12px; width: 100%; min-height: 44px; padding: 12px 0; margin: 0;
            background: none; border: 0; color: inherit; font: inherit; text-align: left; cursor: pointer;
            -webkit-tap-highlight-color: transparent; }
        .groupe > li.haut { display: flex; align-items: flex-start; gap: 12px; padding: 12px 0; }
        .groupe .txt { flex: 1; min-width: 0; }
        .groupe .q { display: block; font-size: 13px; font-weight: 600; color: var(--fumee); line-height: 1.3; margin: 0; }
        /* ⚠️ `overflow-wrap: anywhere` n'est pas decoratif (13/09) : une adresse est un mot sans
           espace — « marie-christine.vandenbroucke@student.uclouvain.be » sortait de 61 px. */
        .groupe .r { display: block; font-size: 16px; font-weight: 800; line-height: 1.25; margin: 2px 0 0; overflow-wrap: anywhere; color: var(--craie); }
        .groupe .r.sans { color: var(--fumee2); font-weight: 600; }
        .groupe .d { font-size: 13px; font-weight: 600; color: var(--fumee); line-height: 1.35; margin: 2px 0 0; }
        .groupe .d.or { color: var(--or); font-weight: 800; }
        .groupe .chev { width: 18px; height: 18px; flex: 0 0 18px; color: var(--fumee2); }
        .groupe .bouton.compact { margin-top: 10px; }
        /* ⭐ LA LIGNE FILLE (15/09) — « Me faire signe dehors, même l'app fermée ». Elle pend sous
           la ligne des histoires : ni tuile, ni filet au-dessus, le texte aligné sur celui de sa
           mère. ⛔ Pas de chevron : elle bascule, elle ne mène nulle part. */
        .groupe > li.fille::before { display: none; }
        .groupe > li.fille .ligne { padding: 0 0 12px 46px; margin-top: -4px; }
        /* ⭐ LES NOUVELLES DE LA VILLE (30/09, contrat nouvelles-de-la-ville) : une ligne HAUTE — sa phrase est l'accord.
           L'interrupteur s'aligne sur la question, pas au milieu de la phrase ; refusée par le téléphone, la phrase
           cède sa place à ce qu'il faut faire, en orange. */
        .groupe .ligne.haute { align-items: flex-start; }
        .groupe .ligne.haute > .inter { margin-top: 6px; }
        .groupe .ligne .d { display: block; margin-top: 4px; line-height: 1.4; }
        .groupe .d.refus { display: flex; gap: 6px; align-items: flex-start; color: var(--orange-texte); font-weight: 700; }
        .groupe .d.refus .i { width: 15px; height: 15px; flex: 0 0 15px; stroke-width: 2.2; margin-top: 2px; }
        .groupe .r2 { display: block; font-size: 14px; font-weight: 700; color: var(--craie2); line-height: 1.35; }
        /* Le thème, dans sa ligne : le segment de l'app, les deux choix sous les yeux. */
        .groupe .txt .segment { display: flex; margin-top: 10px; background: var(--n2); }
        .groupe .txt .segment .pilule { flex: 1 1 0; min-width: 0; justify-content: center; padding: 0 8px; }
        /* L'interrupteur. 42x25 : la cible fait toute la ligne, lui n'est que le signe. */
        .inter { flex: 0 0 42px; height: 25px; border-radius: 999px; background: var(--ambre);
                 position: relative; transition: background .18s; }
        .inter::after { content: ""; position: absolute; top: 3px; right: 3px; width: 19px; height: 19px;
                        border-radius: 50%; background: #191100; transition: right .18s, left .18s; }
        .inter.off { background: var(--inter-off); }
        .inter.off::after { right: auto; left: 3px; background: var(--inter-bouton-off); box-shadow: 0 1px 2px rgba(0,0,0,.18); }
        /* TON COMPTE, en tête de Profil : l'initiale de l'avatar qu'on vient de toucher, l'adresse,
           la porte, et où en est la sauvegarde — son tracé suit l'état (coche, attente, refus). */
        .compte-carte { display: flex; align-items: center; gap: 14px; margin: 10px 0 0; padding: 14px;
                        background: var(--n1); border: 1px solid var(--trait); border-radius: 16px; }
        .compte-carte .disque { width: 48px; height: 48px; flex: 0 0 48px; border-radius: 50%; background: var(--ambre); color: #191100;
                                display: inline-flex; align-items: center; justify-content: center;
                                font-family: 'Montserrat', sans-serif; font-weight: 900; font-size: 20px; line-height: 1; text-transform: uppercase; }
        .compte-carte .disque .i { width: 22px; height: 22px; stroke-width: 2; }
        .compte-carte .txt { flex: 1; min-width: 0; }
        .compte-carte .mail { font-size: 15px; font-weight: 800; line-height: 1.25; margin: 0; overflow-wrap: anywhere; }
        .compte-carte .par { font-size: 13px; color: var(--fumee); line-height: 1.35; margin: 3px 0 0; }
        .compte-carte .sauve { display: flex; align-items: flex-start; gap: 5px; font-size: 13px; color: var(--fumee); line-height: 1.35; margin: 2px 0 0; }
        .compte-carte .sauve[hidden] { display: none; }
        .compte-carte .sauve .i { width: 15px; height: 15px; flex: none; stroke-width: 2.3; margin-top: 1px; }
        .compte-carte .sauve.ok .i { color: var(--vert); }
        .compte-carte .sauve.attente .i { color: var(--orange-texte); }
        .compte-carte .sauve.refus .i { color: var(--rouge); }
        #profil-entree { margin-top: 10px; }
        /* Les autorisations, une fois vérifiées : un point, le nom, l'état. Trois états, trois
           couleurs, jamais un booléen nu — accordé, refusé, et « à voir » quand ça se règle ailleurs. */
        .autos { list-style: none; margin: 10px 0 0; padding: 0; }
        .autos li { display: grid; grid-template-columns: 10px 1fr; column-gap: 10px; align-items: start; padding: 6px 0; }
        .autos .pt { width: 10px; height: 10px; border-radius: 50%; margin-top: 5px; }
        .autos .pt.ok { background: var(--vert-fond); }
        .autos .pt.non { background: var(--rouge-fond); }
        .autos .pt.att { background: var(--fumee2); }
        .autos .nom { display: block; font-size: 14px; font-weight: 700; color: var(--craie2); line-height: 1.35; }
        .autos .et { display: block; font-size: 13px; color: var(--fumee); line-height: 1.35; }
        /* Tes lieux à visiter : les lignes d'un favori, dans leur carte — et le vide, sans ses 36 px. */
        .groupe > .grp { border-bottom: 0; }
        .groupe > .vide { padding: 22px 12px; }
        /* Les trois puces de l'annonce : un tracé monochrome, jamais un emoji —
           un emoji dans un CONTRÔLE n'obéit pas à `color` (règle du 07/09). */
        /* ⚠️ Les marges et l'écart sont MESURÉS à 320x568 : à 16/20/13, le
           second bouton finissait 31 px sous la ligne de flottaison — l'écran
           ne montrait qu'une des deux réponses, et une annonce Play doit
           présenter un vrai choix. */
        .da-puces { list-style: none; margin: 14px 0 16px; padding: 0; display: flex;
                    flex-direction: column; gap: 11px; }
        .da-puces li { display: flex; gap: 11px; align-items: flex-start; }
        .da-puces svg { flex: 0 0 18px; width: 18px; height: 18px; margin-top: 2px; color: var(--or); }
        .da-puces span { font-size: 13.5px; color: var(--fumee); line-height: 1.5; }
        .da-puces b { color: var(--craie); font-weight: 600; }
        .choix { display: flex; align-items: center; gap: 12px; padding: 13px 0; border-bottom: 1px solid var(--trait); font-size: 15px; font-weight: 600; width: 100%; background: none; border-left: 0; border-right: 0; border-top: 0; color: var(--fumee); text-align: left; cursor: pointer; font-family: inherit; }
        .choix:last-child { border-bottom: 0; }
        .choix.on .tuile { background: var(--ambre); color: #191100; }
        .choix .lib { flex: 1; min-width: 0; }
        .choix .coche { margin-left: auto; width: 22px; height: 22px; border-radius: 50%; border: 1.5px solid rgba(255,255,255,.22); flex: 0 0 22px; display: flex; align-items: center; justify-content: center; }
        .choix .coche svg { width: 12px; height: 12px; color: #191100; display: none; }
        .choix.on { color: var(--craie); }
        .choix.on .coche { background: var(--ambre); border-color: var(--ambre); }
        .choix.on .coche svg { display: block; }

        /* La creation : une recherche, des lignes cochables, un bouton ancre. */
        /* ⭐ LE CHAMP DE RECHERCHE — un seul (23/09) : LIEUX et MES CARTES avaient un
           rectangle de 8 px d'arrondi avec « 🔍 » dans le texte d'invite, à 14 px ; la
           création celui-ci, avec la loupe en tracé. ⚠️ 16 px de texte, toujours. */
        .champ {
            display: flex; align-items: center; gap: 10px; height: var(--h-btn); padding: 0 14px; box-sizing: border-box;
            border-radius: var(--r-ctl); background: var(--n2); border: 1px solid var(--trait-fort);
            color: var(--fumee2); transition: border-color .15s;
        }
        /* ⚠️ Dans un `.champ`, le contrôle VISIBLE est le cadre, pas l'`<input>` nu qu'il
           contient : l'anneau du focus entourait le texte seul, loupe exclue. Il passe sur
           le cadre — le même anneau, au même décalage. */
        .champ input:focus-visible { outline: none !important; }
        .champ:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
        /* La croix d'effacement native (un « x » BLEU dans Chrome) n'est pas un contrôle du produit. */
        .champ input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
        .champ .i { width: 18px; height: 18px; }
        .champ input { flex: 1; min-width: 0; height: 100%; padding: 0; background: none; border: 0; outline: 0; color: var(--craie); font-size: 16px; font-family: inherit; }
        .champ input::placeholder { color: var(--fumee2); opacity: 1; }
        .sel { display: flex; align-items: center; gap: 12px; padding: 11px 0; border-bottom: 1px solid var(--trait); width: 100%; background: none; border-left: 0; border-right: 0; border-top: 0; color: inherit; text-align: left; cursor: pointer; font-family: inherit; }
        .sel:last-child { border-bottom: 0; }
        .sel .e { font-size: 18px; width: 28px; text-align: center; flex: 0 0 28px; }
        .sel .txt { flex: 1; min-width: 0; }
        .sel .n { font-size: 15px; line-height: 1.1; margin: 0; }
        .sel .h { font-size: 12px; color: var(--fumee); margin: 3px 0 0; }
        .sel .case { margin-left: auto; width: 24px; height: 24px; border-radius: 8px; border: 1.5px solid rgba(255,255,255,.22); flex: 0 0 24px; display: flex; align-items: center; justify-content: center; }
        .sel .case svg { width: 14px; height: 14px; color: #191100; display: none; }
        .sel.on .case { background: var(--ambre); border-color: var(--ambre); }
        .sel.on .case svg { display: block; }
        .f-ancre {
            flex: 0 0 auto; padding: 12px 24px 22px; margin-top: -26px; position: relative; z-index: 2;
            background: linear-gradient(transparent, var(--n3) 30%); display: flex; align-items: center; gap: 12px;
        }
        .f-ancre .cpt { font-size: 13px; color: var(--fumee); font-weight: 600; flex: 1; min-width: 0; margin: 0; }
        .f-ancre .cpt b { color: var(--craie); }
        /* Son action est un `.bouton` (23/09) ; le Retour est monté dans la tête. */
        .f-ancre .bouton { flex: 0 0 auto; }
        /* ⭐ LA PROGRESSION D'UN PARCOURS EN ÉTAPES — l'onboarding, Raconter, la
           création : trois parcours, une barre. */
        .pas { display: flex; gap: 6px; margin: 12px 24px 0; }
        .pas span { flex: 1; height: 4px; border-radius: 99px; background: var(--rond-fond-presse); }
        .pas span.on { background: var(--ambre); }

        /* ══ LE FOND RECULE D'AUTANT QUE LA FEUILLE MONTE ═══════════════════
           `--recul` vaut 1 des qu'une feuille est ouverte : flou 8 px,
           brightness .62, saturate .75, voile 34 %. Pose par majRecul(), lu ici.
           ⛔ PAS de scale, PAS de coins arrondis (retires le 08/09 au soir,
           demande d'Hakim : « enleve le retrecissement, garde juste le flou »).
           L'ecran garde sa taille ; seul le flou dit qu'il est derriere.
           ⚠️⚠️ Regle CHANGEE le 08/09. Elle disait « en apercu, 0 : la carte
           reste nette et vivante ». Elle laissait le fond VIVANT : un doigt
           pose sur une carte derriere la fiche changeait le lieu qu'elle
           montre, sans que rien ne bouge a l'ecran. Le fond est desormais
           INERTE des qu'une feuille est ouverte, a n'importe quelle hauteur,
           et le flou est ce qui rend cette inertie lisible. Les deux vont
           ENSEMBLE : un fond net et fige se lit comme une panne.
           ⚠️ `filter`, jamais `backdrop-filter` (cf. la regle plus haut). */
        .iphone { --recul: 0; }
        .iphone.recule > .screen {
            filter: blur(calc(var(--recul) * 8px)) brightness(calc(1 - var(--recul) * .38)) saturate(calc(1 - var(--recul) * .25));
        }
        .iphone.inerte > .screen, .iphone.inerte > .tab-bar { pointer-events: none; }
        .iphone.fenetre-ouverte::after {
            content: ''; position: absolute; inset: 0; z-index: 140;
            background: rgba(6,6,10,calc(var(--recul) * .34)); pointer-events: none;
        }
        .iphone.anim-recul > .screen { transition: filter .42s cubic-bezier(.2,.9,.3,1); }
        .iphone.anim-recul::after { transition: background .42s; }
        .fenetre-ouverte ~ #suivi-balade { pointer-events: none; }
        @media (prefers-reduced-motion: reduce) {
            .iphone.recule > .screen { transition: none; }
            .feuille.anim { transition: none; }
            .iphone.anim-recul > .screen, .iphone.anim-recul::after { transition: none; }
        }

        /* ══ LA PAGE POUSSEE — le detail d'une balade ══════════════════════
           Pas une feuille : on la parcourt, on y revient. Elle glisse depuis
           la droite, l'ecran qui reste derriere recule de 30 %. */
        .page {
            position: absolute; inset: 0; z-index: 150; background: var(--n3);
            display: flex; flex-direction: column; padding-top: env(safe-area-inset-top);
            transform: translateX(100%); visibility: hidden; will-change: transform;
            border-left: 1px solid rgba(255,255,255,.06);
        }
        .page.visible { visibility: visible; }
        .page.ouverte { transform: translateX(0); }
        .page.anim { transition: transform .32s cubic-bezier(.2,.8,.2,1); }
        .iphone.page-ouverte > .screen.active { transform: translateX(-30%); }
        .iphone.page-anim > .screen.active { transition: transform .32s cubic-bezier(.2,.8,.2,1); }
        /* ⭐ Le chevron, Éditer et Partager sont des `.btn-rond` (23/09) : le chevron
           était OR et sans fond, Partager GRIS et sans fond, Éditer un MOT en
           capitales or — trois dessins dans la même barre. Les bords des ronds
           tombent sur la gouttière de 24 px du corps. */
        .p-tete {
            height: 56px; flex: 0 0 auto; display: flex; align-items: center; gap: 10px; padding: 0 24px;
            background: var(--n3); border-bottom: 1px solid var(--trait); position: relative; z-index: 2;
        }
        .p-tete .cond {
            flex: 1; min-width: 0; text-align: center; font-family: 'Montserrat', sans-serif; font-weight: 900;
            text-transform: uppercase; font-size: 14px; letter-spacing: -.3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
            opacity: 0; transition: opacity .2s;
        }
        .p-tete .cond.visible { opacity: 1; }
        .p-corps { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 0 24px; scrollbar-width: none; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
        .p-corps::-webkit-scrollbar { display: none; }
        #b-theme { background: transparent; padding: 0; letter-spacing: .08em; }
        .p-pied {
            flex: 0 0 auto; padding: 12px 24px calc(22px + env(safe-area-inset-bottom));
            background: linear-gradient(transparent, var(--n3) 24%); display: flex; gap: 8px; position: relative; z-index: 2; margin-top: -30px;
        }
        /* Ses actions sont des `.bouton` (23/09) — 52 px de Montserrat avant. */
        /* Le geste principal (plein ou vert) prend la place qui reste ; « Annuler » et
           le rond de la carte gardent la leur — plus de `style="flex:0 0 auto"` écrit
           à la main sur un seul bouton. */
        .p-pied .bouton.plein, .p-pied .bouton.vert { flex: 1; }

        /* ══ LE SUIVI, DOCKE SUR LA BARRE ══════════════════════════════════
           72 px au lieu de 270 : l'anneau dit l'avancement, la ligne dit ou
           aller et si c'est ouvert, le bouton dit quoi faire. Un toucher sur
           le texte ouvre le panneau complet, en feuille. */
        /* ⭐ 44 PX SOUS LE DOIGT, DEUXIEME PASSE (19/09, contrat retouches-mecaniques
           n° 01, valide) : « J'y suis » faisait 40 px et la zone du texte 37 — sur
           l'ecran qu'on regarde le moins. Le bouton passe a 44, le rembourrage rend
           les 4 px (10 → 8) : la hauteur du dock NE BOUGE PAS, et c'est ce qui compte
           — `--suivi-h` et tout ce qui se pose dessus (Raconter, Retour, la bulle)
           en dependent. Un test tient 8 + 44 + 8 = 60. */
        .suivi-dock {
            display: flex; align-items: center; gap: 12px;
            background: var(--n3); border: 1px solid rgba(255,255,255,.14); border-radius: 18px;
            padding: 8px 12px 8px 14px; box-shadow: 0 10px 30px rgba(0,0,0,.55);
        }
        .suivi-dock .anneau {
            width: 40px; height: 40px; flex: 0 0 40px; border-radius: 50%; position: relative;
            display: flex; align-items: center; justify-content: center;
            font-family: 'Montserrat', sans-serif; font-weight: 900; font-size: 12px;
            background: conic-gradient(var(--ambre) 0 var(--part, 0%), rgba(255,255,255,.12) var(--part, 0%) 100%);
        }
        .suivi-dock .anneau::before { content: ''; position: absolute; inset: 4px; border-radius: 50%; background: var(--n3); }
        .suivi-dock .anneau span { position: relative; }
        /* `align-self: stretch` : la zone qui ouvre le parcours prend toute la
           hauteur de la ligne (44), pas celle de ses deux lignes de texte (37). */
        .suivi-dock .txt { flex: 1; min-width: 0; text-align: left; background: none; border: 0; padding: 0; color: inherit; cursor: pointer; font-family: inherit;
                           align-self: stretch; display: flex; flex-direction: column; justify-content: center; }
        .suivi-dock .nom { font-size: 14px; font-weight: 800; line-height: 1.2; margin: 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
        .suivi-dock .sous { font-size: 12px; color: var(--fumee); margin: 2px 0 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; letter-spacing: -.01em; }
        .suivi-dock .sous b { font-weight: 700; }
        .suivi-dock .sous b.ferme { color: var(--rouge); }
        .suivi-dock .sous b.ouvert { color: var(--vert); }
        .suivi-dock .sous b.incertain { color: var(--or); }
        /* « J'y suis » est un `.bouton.compact` (23/09) — 44 px, comme avant : la
           hauteur du dock ne bouge pas (8 + 44 + 8 = 60). */
        .suivi-dock .go { flex: 0 0 auto; height: 44px; padding: 0 14px; }
        .suivi-dock .go.la { background: var(--vert-fond); border-color: var(--vert-fond); color: #04220F; }
        /* La ligne de position de la feuille de suivi : le tracé, puis la phrase. */
        .suivi-ligne { display: flex; align-items: flex-start; gap: 6px; }
        .suivi-ligne .i { width: 16px; height: 16px; flex: 0 0 16px; margin-top: 1px; stroke-width: 2; }

        /* ══ LA BARRE D'ONGLETS — des traces, pas des emojis ═══════════════
           Meme regle que le rail d'actions des cartes : un emoji dans le
           TEXTE, un trace dans un CONTROLE. L'actif se remplit d'or. */
        .tab-item svg { width: 22px; height: 22px; display: block; }
        .tab-item .ico-on { display: none; }
        .tab-item.active .ico-off { display: none; }
        .tab-item.active .ico-on { display: block; }

        /* La page recule aussi quand une feuille passe au-dessus d'elle. */
        .iphone.recule > .page.ouverte {
            filter: blur(calc(var(--recul) * 8px)) brightness(calc(1 - var(--recul) * .38));
        }
        .iphone.anim-recul > .page.ouverte { transition: filter .42s cubic-bezier(.2,.9,.3,1); }
        .iphone.inerte > .page { pointer-events: none; }

        /* La legende, a la demande. */
        .leg-ligne { display: flex; align-items: center; gap: 12px; padding: 11px 0; border-bottom: 1px solid var(--trait); font-size: 14px; color: var(--craie2); }
        .leg-ligne:last-child { border-bottom: 0; }
        .puce { width: 14px; height: 14px; border-radius: 50%; border: 2px solid #6b7280; background: #1a1a1a; flex: 0 0 14px; }
        .puce.v { border-color: #22c55e; }
        .puce.g { border: 0; background: #fbbf24; }
        .puce.moi { border: 3px solid #fff; background: #3b82f6; }

        /* ══ LES PORTES DE L'ACCUEIL — « et douze bars ouverts » ═══════════
           Trois tuiles qui donnaient un nom coupe (« CAFÉ VILLE B… ») : le nom
           tient sur deux lignes, l'heure en dessous. */
        .n-portes { display: flex; gap: 6px; }
        .n-porte {
            flex: 1; min-width: 0; background: var(--n1); border: 1px solid var(--trait); border-radius: 14px;
            padding: 10px 8px; text-align: center; color: inherit; font-family: inherit; cursor: pointer;
            display: flex; flex-direction: column; justify-content: space-between; gap: 6px;
        }
        .n-porte .l {
            display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
            font-size: 13px; font-weight: 700; line-height: 1.25; color: var(--craie);
        }
        .n-porte .v { display: block; font-family: 'Montserrat', sans-serif; font-weight: 900; font-size: 17px; color: var(--or); line-height: 1; font-variant-numeric: tabular-nums; }

        #qr-modal > div { background: var(--n3); }
        /* ⚠️⚠️ SUR UN ECRAN COURT, LA BOITE N'AVAIT AUCUNE SORTIE (18/09, mesure a
           320 x 568) : `items-center` centre une boite de 732 px dans 568, donc
           elle deborde des DEUX cotes — la croix a -62 px, « Fermer » a 563 —,
           et rien ne defile. Le code s'affichait, et on restait dedans. Meme
           defaut sur un iPhone 8/SE2 (667 px). Le remede : la fenetre defile,
           et la boite se centre par `margin: auto` — une marge automatique
           n'absorbe que l'espace LIBRE, donc elle centre quand ca tient et
           s'aligne en haut quand ca depasse, sans jamais couper. */
        #qr-modal { overflow-y: auto; }
        #qr-modal > div { margin: auto; }
        /* ⚠️ Et la croix fait 44 px, le plancher : elle mesurait 18 x 32. Depuis le
           23/09, c'est le `.btn-rond` du système (36 dessinés, 44 de zone), posé dans
           le coin comme celui d'une feuille. */
        .qr-x { position: absolute; top: 16px; right: 16px; }
        /* ---- LE PASSAGE (14/09 soir) — la fenetre du code, la carte, « Mes cartes » ----
             Contrat : docs/mockups/clients-comptoir.html. Les points d'une carte sont
             une rangee de <i> : or = pose, vert = celui qui va se poser quand c'est
             le Nᵉ, gris = a venir. Rien sous 11 px (l'echelle du 07/09). */
        .qr-chiffres { font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: 26px; letter-spacing: .18em; color: var(--accent); font-weight: 700; margin: 0 0 4px; }
        .qr-tourne { font-size: 11px; color: var(--fumee2); margin: 0 0 14px; }
        .qr-montre { font-size: 12px; color: var(--fumee); margin: 14px 0 12px; }
        /* ⭐ Les deux legendes SUIVENT le code (19/09, contrat retouches-mecaniques
           n° 05) : cachees tant qu'il n'y en a pas — hors ligne, elles decrivaient
           un code absent sous « Pas de reseau ». `legendesDuCode` dans fiche.js. */
        .qr-tourne[hidden], .qr-montre[hidden] { display: none; }
        .qr-carte { background: rgba(255, 215, 0, .08); border: 1px solid rgba(255, 215, 0, .25); border-radius: 14px; padding: 12px 14px; text-align: left; }
        .qr-carte p { margin: 0; font-size: 13px; color: var(--craie); }
        .qr-carte .qc-l { font-size: 11px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--or); margin: 0 0 6px; }
        .qr-carte .qc-d { color: var(--fumee); margin-top: 4px; font-size: 12px; }
        .qr-carte .qc-d b { color: var(--craie); }
        .qr-carte .qc-d.vert { color: #86efac; }
        .qr-carte .qc-d.vert b { color: inherit; }
        .qr-carte .qc-quoi { font-size: 15px; font-weight: 800; line-height: 1.3; }
        /* ⭐ 25/09 — FREEBIE / FIDÉLITÉ dans la fenêtre du code (contrat docs/mockups/code-freebie-fidelite.html) :
           le segment de l'app, sur toute la largeur, en deux moitiés égales — mesuré DANS L'APP à 320 (Inter) : 106 px par
           pilule pour 93 de contenu, point compris, centré. Le point vert dit « à prendre ici ». ⚠️ `hidden` ne cache rien sans sa règle : `.segment`
           et `flex` (la boîte du code) posent un `display`. */
        .qr-onglets { display: flex; width: 100%; margin: 0 0 16px; }
        .qr-onglets .pilule { flex: 1 1 0; min-width: 0; justify-content: center; padding: 0 8px; }
        .qr-onglets .pt { width: 7px; height: 7px; border-radius: 50%; background: var(--vert-fond); margin-left: 1px; flex: none; }
        .qr-onglets .pilule.active .pt { background: #0d5a2a; }
        .qr-onglets[hidden], #qr-boite[hidden], #qr-code-id[hidden] { display: none; }
        /* Rien à prendre sur l'onglet : pas de code — l'état, dit en grand, et la suite. */
        .qr-etat { background: var(--n2); border: 1px solid var(--trait); border-radius: 16px; padding: 22px 18px; text-align: left; margin: 0 0 16px; }
        .qr-etat .gros { font-family: 'Montserrat', sans-serif; font-weight: 900; font-size: 19px; line-height: 1.15; text-transform: uppercase;
            letter-spacing: -.5px; color: var(--craie); margin: 0; }
        .qr-etat .gros .i { width: 22px; height: 22px; margin: -4px 8px 0 0; color: var(--vert); }
        .qr-etat .gros.ambre .i { color: var(--or); }
        .qr-etat p { font-size: 13px; color: var(--fumee); margin: 8px 0 0; }
        .qr-etat p b { color: var(--craie); }
        .qr-etat .lien { display: inline-flex; align-items: center; gap: 6px; margin-top: 12px; min-height: 44px; padding: 0; border: 0;
            background: none; font-family: 'Inter', sans-serif; font-size: 13px; font-weight: 800; color: var(--or); cursor: pointer; }
        .qr-etat .lien .i { width: 16px; height: 16px; }
        .tp { display: inline-flex; gap: 4px; vertical-align: middle; }
        .tp i { width: 10px; height: 10px; border-radius: 50%; background: var(--tp-vide); display: block; }
        .tp i.on { background: var(--jaune); }
        .tp i.due { background: var(--vert-fond); }
        #qr-prenom .qp-sur { font-size: 11px; font-weight: 900; color: var(--accent); text-transform: uppercase; letter-spacing: .08em; margin: 0 0 8px; }
        #qr-prenom .qp-titre { font-family: 'Montserrat', sans-serif; font-weight: 900; font-style: italic; text-transform: uppercase; font-size: 22px; line-height: 1.1; margin: 0 0 8px; }
        #qr-prenom .qp-txt { font-size: 14px; color: var(--fumee); margin: 0 0 18px; line-height: 1.55; }
        #qr-prenom .qp-pied { font-size: 12px; color: var(--fumee2); text-align: center; margin: 10px 0 0; }
        #qr-prenom .mur-champ { margin-bottom: 9px; }
        /* la fiche : ce que la carte promet, et ou en est ce compte */
        /* Dans le CORPS de la fiche depuis le 19/09 (variante A) : il defile avec le reste. */
        .m-cartes { margin: 12px 0 16px; padding-top: 12px; border-top: 1px solid var(--trait); }
        .mc-ligne { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
        .mc-l { font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: var(--or); }
        .mc-d { font-size: 12px; color: var(--fumee); margin: 2px 0 0; }
        .mc-d b { color: var(--craie); }
        .mc-v { font-family: 'Montserrat', sans-serif; font-weight: 900; font-size: 15px; color: var(--or); white-space: nowrap; flex: none; }
        .mc-v.vert { color: #86efac; }
        .mc-free { font-size: 13px; color: var(--fumee); margin: 8px 0 0; }
        /* Le cadeau et la carte en tracés (23/09) — c'étaient 🎁 et 💳. */
        .mc-l .i, .mc-free .i, .qc-d .i, .qp-sur .i { width: 15px; height: 15px; margin: -2px 5px 0 0; stroke-width: 2; }
        /* « Montrer mon code » : un `.bouton.plein.large` (23/09) — il faisait 46 px
           en capitales. `.mc-btn` ne garde que sa place, et sert de repère. */
        .mc-btn { margin-top: 12px; }
        /* ══ MES CARTES — deux onglets, le style du profil (26/09/2026) ═══════════════
           Contrat : docs/mockups/freebies-onglets.html. Le sélecteur de Profil · Réglages,
           les groupes en carte (`.groupe`), la ligne d'un lieu comme un favori du profil,
           « Montrer mon code » en pilule d'action SOUS sa ligne, alignée sur le texte. */
        .segment.freebies-onglets { display: flex; margin: 0; }
        .freebies-onglets .pilule { flex: 1 1 0; min-width: 0; justify-content: center; padding: 0 10px; }
        #mes-cartes:not(:empty) { margin: 0 0 20px; }
        .freebies-recherche { margin-top: 14px; }
        .groupe > li.offre { padding-bottom: 12px; }
        .groupe > li.offre > .ligne { align-items: flex-start; padding: 12px 0 0; }
        .offre .n { display: block; font-size: 16px; line-height: 1.1; }
        .offre .o { display: block; font-size: 14px; font-weight: 700; color: var(--craie); line-height: 1.35; margin-top: 4px; overflow-wrap: anywhere; }
        .offre .o b { font-weight: 800; }
        .offre .h { display: block; font-size: 13px; color: var(--fumee); line-height: 1.35; margin-top: 3px; }
        .offre .pris { display: flex; align-items: center; gap: 5px; font-size: 13px; font-weight: 700; color: var(--vert); margin-top: 4px; }
        .offre .pris .i { width: 15px; height: 15px; stroke-width: 2.3; }
        .offre .carte-l { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px; margin-top: 6px; }
        .offre .tp { flex-wrap: wrap; }
        .offre .tampons { font-family: 'Montserrat', sans-serif; font-weight: 900; font-size: 15px; color: var(--or); white-space: nowrap; }
        .offre .tampons.vert { color: var(--vert); }
        .offre .chev { margin-top: 8px; }
        .groupe > li.offre > .mc-btn { margin: 10px 0 0 46px; }
        .groupe.freebies-entree { margin-top: 26px; }
        .freebies-note { font-size: 14px; color: var(--fumee); line-height: 1.5; margin: 22px 0 0; }
        /* ══ BALADES — deux onglets, le style du profil (26/09/2026) ═════════════════
           Contrat : docs/mockups/balades-onglets.html. Le sélecteur de Profil · Réglages sous
           le titre ; les cinq filtres SOUS lui (arbitré : « on les garde ») ; une balade = une
           ligne dans un groupe — le dessin d'un lieu de Mes cartes ; les trois ronds d'une
           balade à toi SOUS sa ligne, alignés sur le texte, et la corbeille qui se confirme là. */
        .segment.balades-onglets { display: flex; margin: 0; }
        /* ⚠️ 6 px et non 10 : « MES BALADES » demande 114 px, une moitié en offre 126 à 320 (mesuré). */
        .balades-onglets .pilule { flex: 1 1 0; min-width: 0; justify-content: center; padding: 0 6px; }
        .rangee.balades-filtres { margin-top: 12px; }
        .groupe > li.balade { padding-bottom: 12px; }
        .groupe > li.balade > .ligne { align-items: flex-start; padding: 12px 0 0; }
        .balade .n { display: block; font-size: 16px; line-height: 1.1; overflow-wrap: anywhere; }
        /* Sa phrase : deux lignes au plus — la page la donne entière. */
        .balade .b-desc { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
                          font-size: 14px; color: var(--craie2); line-height: 1.4; margin-top: 4px; }
        .balade .b-meta { display: block; font-size: 13px; font-weight: 600; color: var(--fumee); line-height: 1.35; margin-top: 4px; }
        /* Ce qu'il faut savoir avant de partir : un tracé, une phrase. */
        .balade .b-signe { display: flex; align-items: flex-start; gap: 5px; font-size: 13px; font-weight: 700; line-height: 1.35; margin-top: 4px; }
        .balade .b-signe .i { width: 15px; height: 15px; flex: none; stroke-width: 2.2; margin-top: 1px; }
        .balade .b-signe.cours { color: var(--or); }
        .balade .b-signe.jour { color: var(--craie2); }
        .balade .b-signe.bus { color: var(--orange-texte); }
        .balade .chev { margin-top: 8px; }
        .groupe > li.balade > .b-actions { display: flex; gap: 8px; margin: 10px 0 0 46px; }
        .groupe > li.balade > .b-confirmer { margin: 10px 0 0 46px; }
        .b-confirmer p { font-size: 14px; font-weight: 700; color: var(--craie); line-height: 1.35; margin: 0 0 8px; }
        .b-confirmer .boutons { display: flex; flex-wrap: wrap; gap: 8px; }
        /* 14 px et non 16 : « Supprimer » et « Garder » côte à côte demandent 201 px, il y en a 196 sous
           le texte à 320 (mesuré). ⚠️ `.groupe .bouton.compact` pose 10 px au-dessus : pas ici. */
        .groupe .b-confirmer .bouton.compact { margin-top: 0; padding: 0 14px; }
        .bouton.balades-creer { margin-top: 20px; }
        .balades-note { font-size: 14px; color: var(--fumee); line-height: 1.5; margin: 22px 0 0; }
        .mc-vide { background: var(--n1); border: 1px solid var(--trait); border-radius: 16px; padding: 18px; }
        .mc-vide p { margin: 0; font-size: 14px; color: var(--fumee); line-height: 1.5; }
        .mc-vide .bouton { margin-top: 14px; }
        /* ⭐ LE VIDE (23/09) : un tracé, une phrase — « Aucun lieu ne correspond à ta
           recherche » était un paragraphe gris nu, « Rien à afficher » un texte de 12 px. */
        .vide { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 36px 16px; text-align: center; color: var(--fumee2); }
        .vide .i { width: 28px; height: 28px; stroke-width: 1.6; }
        .vide p { margin: 0; font-size: 14px; color: var(--fumee); line-height: 1.5; max-width: 260px; }
        /* ---- L'ACCUEIL « BISOUS M'CHOU » ----
             Plein or, le zebre de la marque leve les bras. Il ne se voit qu'une
             fois, a la sortie de l'onboarding. */
        #scr-bisous {
            position: fixed; inset: 0; z-index: 900;
            background: var(--jaune);
            display: flex; flex-direction: column; align-items: center; justify-content: center;
            overflow: hidden; cursor: pointer;
            padding-bottom: env(safe-area-inset-bottom);
        }
        #scr-bisous.hidden { display: none; }

        /* ---- LE MUR — l'auth wall, arbitre le 10/09/2026, leve le 14/09 ----
             ⛔ MUR DUR : on n'atteint pas les 54 fiches sans compte. Il tombe
             DES L'OUVERTURE depuis le 14/09 — variante A du contrat : c'est le
             PREMIER ecran, les cinq questions sont derriere lui, et « Bisous
             m'chou » leur a repris sa place a la fin.
             ⚠️⚠️ Il ne se leve QUE si MUR_DUR est vrai. A false, l'app se
             comporte comme avant la levee, ecran de fete compris — c'est le
             geste d'urgence si les portes cassent : sans identifiants,
             `/auth/portes` rend une liste vide et l'ecran n'a aucun bouton.
             ⛔ Aucune sortie : ni « Plus tard », ni fermeture par le fond.
             C'est ce qui le separe du mur mou dessine la veille. */
        #scr-mur {
            position: fixed; inset: 0; z-index: 900;
            background: var(--n0);
            display: flex; flex-direction: column; justify-content: center;
            /* ⚠️ MESURÉ le 27/09 : à 320 × 568 le mur dépassait déjà de 20 px, et centré « tout court »
               son haut débordait SANS RETOUR — on ne défile pas au-dessus du premier mot. `safe` le fait
               défiler depuis son premier mot ; un navigateur qui ne le connaît pas garde la ligne d'avant. */
            justify-content: safe center;
            overflow-y: auto;
            padding: 30px 24px calc(30px + env(safe-area-inset-bottom));
        }
        #scr-mur.hidden { display: none; }
        .mur-sur {
            color: var(--accent); font-weight: 900; font-size: 11px;
            text-transform: uppercase; letter-spacing: .08em; margin: 0 0 8px;
        }
        .mur-titre {
            font-family: 'Montserrat', sans-serif; font-weight: 900; font-style: italic;
            text-transform: uppercase; font-size: 22px; line-height: 1.1; margin: 0 0 8px;
        }
        .mur-txt { font-size: 14px; color: var(--fumee); margin: 0 0 10px; line-height: 1.55; }
        /* ⭐ La phrase des conditions (27/09, contrat docs/mockups/cgu-au-mur.html, A) : collée au texte, avant
           les portes, qu'elle ne distrait pas — le tertiaire, et l'or du texte pour ses deux liens. */
        .mur-cgu { font-size: 13px; line-height: 1.5; color: var(--fumee2); margin: 0 0 20px; }
        .mur-cgu a { color: var(--accent); font-weight: 700; text-decoration: none; }
        /* ⚠️ Un bouton de fournisseur porte SA marque : c'est ce que les trois
             consoles exigent, et c'est ce qu'un utilisateur reconnait. Les tracés
             sont donc en couleur ici — la regle « tracé monochrome dans un
             controle » vise les icones de l'app, pas un logo tiers. */
        /* Des `.bouton.large` (23/09) aux couleurs du fournisseur : 51, 53 et 51 px avant —
           le filet de la porte Apple la grandissait de deux pixels. */
        .mur-porte { margin-bottom: 10px; background: #fff; border-color: #fff; color: #111; gap: 10px; }
        .mur-porte.noir { background: #000; border-color: #3A3A42; color: #fff; }
        .mur-porte.bleu { background: #1877F2; border-color: #1877F2; color: #fff; }
        .mur-porte svg { width: 19px; height: 19px; flex: 0 0 19px; }
        .mur-sortie { margin-top: 10px; }
        .mur-ou {
            display: flex; align-items: center; gap: 11px; margin: 16px 0;
            color: var(--fumee); font-size: 12px;
        }
        .mur-ou::before, .mur-ou::after { content: ""; height: 1px; background: var(--trait); flex: 1; }
        /* Le champ du mur est une `.saisie`, ses boutons des `.bouton` (23/09) : il en
           avait trois hauteurs (51, 53, 53) et un jaune à lui. */
        .mur-champ { margin-bottom: 10px; }
        /* ⭐ LE CODE DU MAIL (27/09, contrat docs/mockups/code-dans-le-mail.html) — sur « Regarde ta boîte
           mail ». L'adresse tapée en clair, pour qu'une faute se voie : elle casse après l'arobase (un
           <wbr> posé par mur.js), et n'importe où seulement en dernier recours. Le champ est la même
           `.saisie` de 48 px, ses chiffres plus grands, espacés, à chasse fixe — centrés : l'espacement
           pousse à droite, la marge de gauche le rend. */
        .mur-txt b { color: var(--craie); }
        .mur-adresse { overflow-wrap: anywhere; }
        .mur-etiquette {
            display: block; font-size: 12px; font-weight: 800; color: var(--fumee);
            text-transform: uppercase; letter-spacing: .06em; margin: 0 0 7px;
        }
        .saisie.code {
            text-align: center; font-size: 22px; font-weight: 800; letter-spacing: .32em;
            font-variant-numeric: tabular-nums; padding-left: calc(14px + .32em); margin-bottom: 10px;
        }
        .saisie.code:placeholder-shown { padding-left: 14px; }
        .saisie.code::placeholder { font-size: 16px; font-weight: 400; letter-spacing: 0; color: var(--fumee2); }
        .saisie.code.refuse { border-color: var(--rouge); }
        .mur-erreur { font-size: 14px; line-height: 1.45; color: var(--rouge); margin: -2px 0 10px; }
        .mur-erreur[hidden] { display: none; }
        .mur-aide { font-size: 13px; line-height: 1.5; color: var(--fumee2); margin: 14px 0 0; }
        /* ⛔ Le ROUGE PLEIN (`.bouton.rouge`) n'existe que pour UN geste : supprimer
           son compte (13/09, arbitre sur le mockup). Un troisieme bouton plein apres
           le vert et l'or, reserve a ce qui ne se defait pas. Noir sur #FF3B5C : 6,0:1. */
        /* Le lien discret sous « Me deconnecter » : 14 px, gris, souligne — mais une
           cible de 44 px, la regle du produit. */
        /* « Supprimer mon compte » est un `.bouton.nu.alerte` (23/09) : un lien souligné
           se lisait comme une page web. */
        .profil-lien { margin-top: 6px; }
        /* L'encart d'une feuille : un fait a part, dans son cadre — « ce telephone
           garde ses affaires ». Le fond est un plan en dessous de la feuille. */
        .f-encart { background: var(--n1); border: 1px solid var(--trait); border-radius: 16px; padding: 13px 14px; margin: 16px 0; }
        .f-encart .t { font-size: 11px; font-weight: 900; color: var(--or); text-transform: uppercase; letter-spacing: .08em; margin: 0 0 5px; }
        .f-encart p { font-size: 13px; margin: 0; color: var(--fumee); line-height: 1.5; }
        .f-encart p b { color: var(--craie); }
        /* ⚠️⚠️ L'etat « aucune porte ouverte » se VOIT, il ne se devine pas.
             Sans lui, l'ecran serait un titre suivi de rien — ce qui se lit
             comme une panne, et c'en serait une. */
        /* ⚠️ Le rouge de TEXTE est `--rouge` (23/09) : #FF3B5C faisait 4,49:1 sur
           l'anthracite et 3,09 sur la creme — sous le seuil, dans les deux themes.
           La page de repli de la coque reprend ce cadre et cette phrase. */
        .mur-vide {
            border: 1px dashed rgba(255,59,92,.5); border-radius: 14px;
            padding: 26px 16px; text-align: center; color: var(--rouge);
            font-size: 13px; line-height: 1.6;
        }
        .mur-vide span { color: var(--fumee); }
        /* ⭐ La porte commerçant du mur (14/09) : une ligne discrète sous les portes,
           cible de 44 px, le geste souligné. Elle sort du produit, elle ne l'ouvre pas. */
        .mur-pro {
            display: block; width: 100%; min-height: 44px; margin: 18px 0 0; padding: 12px 0 0;
            color: var(--fumee); font-size: 14px; line-height: 1.5; text-align: center;
            text-decoration: none;
        }
        /* Le lien de l'app (23/09) — l'or du texte et un chevron, comme « Voir les autres »
           ou « Raconter la mienne » : un soulignement se lit comme une page web. */
        .mur-pro b { color: var(--accent); font-weight: 800; }
        .mur-pro .i { width: 16px; height: 16px; stroke-width: 2.2; color: var(--accent); vertical-align: -3px; margin-left: 2px; }
        .bisous-zebre { width: min(62vw, 260px); height: min(46vh, 340px); flex: 0 0 auto; }
        #scr-bisous { padding: 24px 20px calc(64px + env(safe-area-inset-bottom)); }
        .bisous-coeur {
            position: absolute; width: 34px; height: 34px; opacity: 0;
            left: 58%; top: 42%; pointer-events: none;
        }
        .bisous-mot {
            font-family: 'Montserrat', sans-serif; font-weight: 900; font-style: italic;
            text-transform: uppercase; letter-spacing: -2px; line-height: .92;
            font-size: clamp(38px, 13vw, 54px); color: #080808; text-align: center; margin: 10px 0 0;
        }
        .bisous-sous { font-size: 16px; font-weight: 700; color: rgba(8,8,8,.55); margin: 16px 0 0; text-align: center; }
        .bisous-passer { position: absolute; bottom: calc(30px + env(safe-area-inset-bottom)); left: 0; right: 0; text-align: center; font-size: 13px; font-weight: 700; color: rgba(8,8,8,.4); margin: 0; }

        @keyframes bisousSalut { 0% { opacity: 0; transform: translateY(64px) scale(.9); } 60% { opacity: 1; } 100% { opacity: 1; transform: translateY(0) scale(1); } }
        @keyframes bisousCoeur { 0% { opacity: 0; transform: translate(0,0) scale(.3); } 18% { opacity: 1; } 100% { opacity: 0; transform: translate(70px,-150px) scale(1.9) rotate(16deg); } }
        @keyframes bisousMonte { from { opacity: 0; transform: translateY(26px); } to { opacity: 1; transform: translateY(0); } }
        #scr-bisous:not(.hidden) .bisous-zebre { animation: bisousSalut .6s cubic-bezier(.2,.9,.3,1.25) both; }
        #scr-bisous:not(.hidden) .bisous-coeur { animation: bisousCoeur .95s cubic-bezier(.3,0,.4,1) .85s both; }
        #scr-bisous:not(.hidden) .bisous-mot   { animation: bisousMonte .45s cubic-bezier(.2,.9,.3,1.2) .62s both; }
        #scr-bisous:not(.hidden) .bisous-sous  { animation: bisousMonte .45s ease .86s both; }
        @media (prefers-reduced-motion: reduce) {
            #scr-bisous * { animation: none !important; opacity: 1 !important; transform: none !important; }
            .bisous-coeur { display: none; }
        }

        /* ---- L'ARRIVEE D'UNE BALADE ----
             Le seul moment ou le produit dit bravo. Le fanion or reprend
             l'ecran de bienvenue ; dessous, le carnet dit ce qu'on vient de
             faire et ouvre sur la suite. Meme z-index que « Bisous m'chou » :
             au-dessus de la barre d'onglets, du panneau de suivi et des toasts. */
        #scr-arrivee {
            position: fixed; inset: 0; z-index: 900;
            background: var(--n0); color: var(--craie);
            overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch;
            padding-bottom: calc(36px + env(safe-area-inset-bottom));
        }
        #scr-arrivee.hidden { display: none; }

        .arr-fanion {
            position: relative;
            background: var(--jaune); text-align: center;
            padding: calc(34px + env(safe-area-inset-top)) 22px 26px;
            border-bottom-left-radius: 26px; border-bottom-right-radius: 26px;
        }
        /* La croix du premier ecran (23/09) : le gabarit `.f-x`, a l'encre du fanion
           (#080808, celle de `.arr-mot`) — le jaune est le meme dans les deux themes. */
        .arr-x { position: absolute; top: calc(14px + env(safe-area-inset-top)); right: 24px; }
        .arr-zebre { width: 104px; height: 138px; margin: 0 auto 2px; }
        .arr-zebre svg { width: 100%; height: 100%; }
        .arr-mot {
            font-family: 'Montserrat', sans-serif; font-weight: 900; font-style: italic;
            text-transform: uppercase; letter-spacing: -1.6px; line-height: .92;
            font-size: 38px; color: #080808; margin: 6px 0 0;
        }
        .arr-sous { font-size: 15px; font-weight: 700; color: rgba(8,8,8,.6); margin: 10px 0 0; line-height: 1.5; }

        .arr-corps { padding: 0 18px; max-width: 520px; margin: 0 auto; }
        .arr-carte { background: var(--n1); border-radius: 24px; padding: 20px; margin-top: 16px; }
        .arr-titre {
            font-family: 'Montserrat', sans-serif; font-weight: 900; font-style: italic;
            text-transform: uppercase; font-size: 12px; letter-spacing: 1px;
            color: var(--or); margin-bottom: 14px;
        }
        .arr-nom {
            font-family: 'Montserrat', sans-serif; font-weight: 900; text-transform: uppercase;
            font-size: 20px; letter-spacing: -.7px; line-height: 1.08; margin-top: -2px;
        }
        .arr-chiffres {
            display: flex; margin-top: 18px; background: var(--n2);
            border-radius: 16px; padding: 14px 6px;
        }
        .arr-chiffres > div { flex: 1; text-align: center; position: relative; min-width: 0; }
        .arr-chiffres > div + div::before {
            content: ''; position: absolute; left: 0; top: 2px; bottom: 2px;
            width: 1px; background: var(--trait);
        }
        .arr-ch-v {
            font-family: 'Montserrat', sans-serif; font-weight: 900; font-size: 20px;
            letter-spacing: -1px; color: var(--craie); white-space: nowrap;
        }
        .arr-ch-v small { font-size: 14px; letter-spacing: 0; }
        .arr-ch-l {
            font-size: 11px; font-weight: 800; text-transform: uppercase;
            letter-spacing: 1.4px; color: var(--fumee2); margin-top: 5px;
        }

        .arr-etapes { margin-top: 6px; }
        .arr-etape { display: flex; gap: 13px; padding: 13px 0; }
        .arr-coche {
            width: 26px; height: 26px; border-radius: 9px; background: rgba(34,197,94,.14);
            display: flex; align-items: center; justify-content: center; flex: 0 0 auto; margin-top: 1px;
        }
        .arr-coche svg { width: 14px; height: 14px; }
        .arr-etape-txt { min-width: 0; flex: 1; }
        .arr-etape-nom {
            font-family: 'Montserrat', sans-serif; font-weight: 900; text-transform: uppercase;
            font-size: 16px; letter-spacing: -.3px; line-height: 1.2;
        }
        .arr-punch { font-size: 14px; font-style: italic; color: #8f8f9e; margin-top: 4px; line-height: 1.45; }
        .arr-segment {
            display: flex; align-items: center; gap: 7px; padding: 2px 0 2px 39px;
            font-size: 13px; color: #6a6a78; font-weight: 600;
        }
        .arr-segment svg { width: 11px; height: 11px; flex: 0 0 auto; opacity: .65; }

        .arr-relance {
            background: var(--n1); border-radius: 24px; padding: 20px; margin-top: 16px;
            border: 1px solid rgba(251,191,36,.22);
        }
        .arr-relance .arr-titre { margin-bottom: 2px; }
        .arr-prochain {
            display: flex; gap: 13px; align-items: center; margin-top: 12px; width: 100%;
            text-align: left; background: none; border: 0; padding: 0; cursor: pointer; color: inherit;
        }
        .arr-vignette {
            width: 46px; height: 46px; border-radius: 14px; background: var(--n2); color: var(--accent);
            display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
        }
        .arr-vignette .i { width: 22px; height: 22px; }
        .arr-prochain-nom {
            font-family: 'Montserrat', sans-serif; font-weight: 900; text-transform: uppercase;
            font-size: 16px; letter-spacing: -.3px; line-height: 1.15;
        }
        .arr-prochain-meta { font-size: 13px; color: #8f8f9e; margin-top: 4px; }

        /* Deux `.bouton.large` (23/09) : 55 et 53 px de Montserrat avant. */
        .arr-actions { margin-top: 20px; display: flex; flex-direction: column; gap: 10px; }

        /* ---- CARTE ----
             L'application est noire et or ; la carte s'ouvrait en blanc. Plutot
             que de changer de fournisseur de tuiles (clé d'API, attribution,
             parfois filigrane) ou de moteur, on assombrit les tuiles OSM
             elles-memes. Meme source, meme attribution, aucune dependance. */
        .leaflet-tile-pane {
            /* Assombri pour s'accorder au reste (06/09). Les tuiles ressortaient
               nettement plus claires que l'interface une fois celle-ci reprise :
               rien n'avait change sur elles, c'est le reste qui s'etait accorde.

               brightness .62 et non .48 : au-dela, les noms de rues et de
               quartiers deviennent illisibles — mesure a l'ecran sur Dampremy,
               Marcinelle-Centre et Charleroi-Central. Une carte doit rester une
               carte : c'est le seul ecran ou l'on cherche a se reperer. */
            filter: invert(1) hue-rotate(180deg) brightness(.62) contrast(.9) saturate(.3) grayscale(.35);
        }
        /* ⭐ Sur le CLAIR, les tuiles OpenStreetMap sont claires d'origine : le
           filtre tombe, et l'ecran CARTE ressemble enfin au reste de l'app. */
        html[data-theme="clair"] .leaflet-tile-pane { filter: none; }

        /* Leaflet peint son conteneur en #DDD par defaut. On le voyait avant
           l'arrivee des tuiles et entre elles pendant un deplacement rapide :
           un flash gris clair au milieu d'une application sombre. Ce fond n'est
           PAS dans le tile-pane, donc le filtre ne l'atteint jamais. */
        /* Prefixe par `.iphone` : la feuille de Leaflet est chargee APRES
           celle-ci et impose `.leaflet-container{background:#ddd}`. A
           specificite egale, c'est le dernier charge qui gagne — meme piege
           que le CDN Tailwind. Deux classes battent une classe. */
        .iphone .leaflet-container { background: var(--n0); }

        /* L'attribution OpenStreetMap est une mention obligatoire, et elle
           ressortait a 3,14 de contraste : sous le seuil de lecture. Elle reste
           discrete, mais elle se lit. */
        .iphone .leaflet-control-attribution {
            background: var(--voile) !important;
            color: var(--fumee) !important;
        }
        .iphone .leaflet-control-attribution a { color: var(--craie2) !important; }
        /* Le filtre s'applique aux tuiles seules : marqueurs, traces et
             panneaux gardent leurs vraies couleurs. */

        .pin {
            width: 30px; height: 30px; border-radius: 50%;
            display: flex; align-items: center; justify-content: center;
            font-size: 14px; line-height: 1; color: #fff;
            background: #1a1a1a; border: 2px solid #6b7280;
            box-shadow: 0 2px 8px rgba(0,0,0,.6);
        }
        /* L'univers d'une pastille est un TRACÉ (23/09) — un emoji de 14 px avant. */
        .pin .i { width: 15px; height: 15px; stroke-width: 2; }
        .pin.cible .i { color: var(--jaune); }
        /* L'anneau vert dit « ouvert maintenant » — la seule information que les
             52 pastilles identiques d'avant portaient sans jamais la nommer. */
        .pin.ouvert { border-color: #22c55e; box-shadow: 0 0 0 1px rgba(34,197,94,.35), 0 2px 8px rgba(0,0,0,.6); }
        .pin.actif  { border-color: #fbbf24; transform: scale(1.15); }
        /* Loin du profil : en retrait, jamais retire. Il reste franchement
           visible et cliquable — on attenue, on ne cache pas. */
        .pin.hors-profil { opacity: .45; }

        /* ═══ LE LIEU QU'ON VIENT DE TOUCHER ═══════════════════════════════
           Contrat : `docs/mockups/carte-lieu-cible.html`, valide le 10/09/2026.

           Toucher l'icone carte d'une vignette volait jusqu'au lieu, posait un
           toast EN HAUT DE L'ECRAN — a 400 px du point qu'il decrivait — et
           dorait la pastille 2,5 s. Apres quoi il ne restait rien. Et une fois
           sur deux il ne se passait RIEN DU TOUT : mesure sur les 53 fiches
           geolocalisees, 23 voient leur pastille prise dans un compteur de
           regroupement au zoom 16, et le code sortait alors sans rien allumer. */
        .pin.cible {
            border-color: var(--jaune);
            transform: scale(1.18);
            box-shadow: 0 0 0 4px rgba(255,215,0,.22), 0 2px 12px rgba(0,0,0,.75);
        }
        /* On ATTENUE, on ne masque jamais : les autres pastilles restent
           visibles et cliquables — la regle du catalogue, appliquee a la carte.
           En toucher une la designe a son tour. */
        .carte-focus .pin:not(.cible),
        .carte-focus .pin-groupe { opacity: .4; }

        /* L'ancre fait 0x0 et se pose sur la pastille ; la boite vit au-dessus. */
        .etiq-ancre { width: 0; height: 0; }
        .etiq {
            position: absolute; bottom: 24px; left: 0;
            width: var(--larg, 268px);
            transform: translateX(calc(-50% + var(--dx, 0px)));
            filter: drop-shadow(0 8px 22px rgba(0,0,0,.75));
        }
        /* ⚠️ LARGEUR FIXE, BORD CALE. Laissee en largeur automatique, la boite
           se fait ECRASER par le bord de la carte : mesuree a 186 px au lieu de
           268 pres du bord droit, avec un nom de deux mots coupe en deux. Sa
           largeur aurait donc dependu de l'endroit ou le lieu se trouve a
           l'ecran. `--dx` la recale, `--queue` ramene la queue sur la pastille. */
        .etiq-c {
            position: relative; background: var(--n1); border: 1px solid var(--jaune);
            border-radius: 13px; padding: 10px 40px 11px 13px; text-align: left;
            cursor: pointer;
        }
        .etiq-c b {
            display: block; font-family: 'Montserrat', sans-serif; font-weight: 900;
            text-transform: uppercase; font-size: 14px; line-height: 1.15;
            letter-spacing: -.3px; color: var(--craie);
        }
        .etiq-etat { display: block; font-size: 12px; margin-top: 5px; }
        .etiq-d { display: block; font-size: 12px; color: var(--fumee2); margin-top: 2px; }
        .etiq-d .i { width: 14px; height: 14px; margin: -2px 3px 0 0; stroke-width: 2; }
        /* ⚠️ 44 px, LE PLANCHER DU PRODUIT — mesure du 18/09 (passe « perfection ») :
           la croix faisait 26 x 26, le glyphe et sa cible confondus. Sur une carte,
           rater la croix c'est toucher la carte : rien ne se ferme, et on retouche.
           Le glyphe garde sa taille (17 px) ; c'est la ZONE qui grandit, centree
           8 px du bord au lieu de 16 — decalee pour ne pas mordre sur la fin d'un
           nom long —, et deborde de la boite sans etre coupee (aucun overflow
           sur `.etiq`). ⛔ Ne pas revenir a 26. */
        /* ⭐ Depuis le 23/09, le `.btn-rond.petit` du système (28 px dessinés, une zone
           de 28 + 2 × 8 = 44) — et plus un « × » en caractère. */
        .etiq-x { position: absolute; top: 7px; right: 7px; }
        .etiq-queue {
            position: absolute; bottom: -7px; left: var(--queue, 50%);
            transform: translateX(-50%) rotate(45deg);
            width: 12px; height: 12px; background: var(--n1);
            border-right: 1px solid var(--jaune); border-bottom: 1px solid var(--jaune);
        }
        .pin-groupe {
            width: 34px; height: 34px; border-radius: 50%;
            display: flex; align-items: center; justify-content: center;
            background: #fbbf24; border: 2px solid #d97706; color: #000;
            font-weight: 900; font-size: 14px; font-family: 'Inter', sans-serif;
            box-shadow: 0 2px 8px rgba(0,0,0,.6);
        }
        .pin-moi {
            width: 16px; height: 16px; border-radius: 50%;
            background: #3b82f6; border: 3px solid #fff;
            box-shadow: 0 0 0 4px rgba(59,130,246,.3);
        }
        .leaflet-div-icon { background: transparent; border: 0; }

        /* ══ LES HISTOIRES — la couche de memoire de la ville (08/09) ═══════
           Une histoire n'est pas un lieu : sa pastille est une BULLE, pas un
           rond — quelqu'un raconte quelque chose ici. La queue pointe l'endroit
           exact, ce qui la tient AU-DESSUS de la pastille d'un lieu quand les
           deux partagent les memes coordonnees (une photo prise devant le
           Carolo). Dedans, un trace monochrome, jamais un emoji : c'est un
           controle. En or quand on sourit, en gris quand on se tait : la carte
           dit deja le registre, avant qu'on ouvre. */
        .pin-histoire {
            position: relative; width: 28px; height: 28px; border-radius: 9px;
            background: #1a1a1a; border: 2px solid var(--jaune); color: var(--accent);
            display: flex; align-items: center; justify-content: center;
            box-shadow: 0 2px 8px rgba(0,0,0,.6);
        }
        .pin-histoire::after {
            content: ''; position: absolute; left: 50%; bottom: -7px; width: 9px; height: 9px;
            background: #1a1a1a; border-right: 2px solid var(--jaune); border-bottom: 2px solid var(--jaune);
            transform: translateX(-50%) rotate(45deg); border-radius: 0 0 2px 0;
        }
        .pin-histoire svg { width: 15px; height: 15px; display: block; position: relative; z-index: 1; }
        .pin-histoire.actif { transform: scale(1.15); box-shadow: 0 0 0 4px rgba(255,215,0,.25), 0 2px 10px rgba(0,0,0,.7); }
        .pin-histoire.grave { border-color: var(--craie2); color: var(--craie2); }
        .pin-histoire.grave::after { border-color: var(--craie2); }
        .pin-histoire.grave.actif { box-shadow: 0 0 0 4px rgba(214,214,224,.22), 0 2px 10px rgba(0,0,0,.7); }
        .puce.histoire { border-radius: 4px; border-color: var(--jaune); }

        /* La feuille d'une histoire : la meme feuille, un autre contenu. En
           apercu : ICI, EN 1914, le titre, l'accroche, la nature et deux gestes.
           La photo d'epoque attend la pleine hauteur, comme le bandeau d'un lieu. */
        .feuille #h-photos { display: none; margin-top: 8px; }
        .feuille.pleine #h-photos { display: block; }
        .h-accroche { font-size: 15px; line-height: 1.45; margin: 6px 0 0; font-style: italic; color: var(--or); }
        /* ⚠️⚠️ LE LIEU NE SE TRONQUE PLUS, ET C'EST TOUT L'INTERET DE LA LIGNE.
           Il portait `.tronque` (nowrap + ellipsis), et le commentaire de
           `teteDuLieu` justifiait la troncature de la carte d'accueil par
           « le lieu ENTIER est dans la feuille, a un toucher ». Mesure du
           12/09 : 16 des 17 lieux etaient coupes ICI AUSSI — a 375 comme a
           320 — jusqu'a 20 % du texte conserve (« Ancienne caserne De… »
           pour « Ancienne caserne Defeld, boulevard Pierre Mayence 67 —
           aujourd'hui Musee des Beaux-Arts »). Le lieu est la SEULE ligne
           qui dise ou se planter : d'une couche qui promet « on passe
           devant et bim », c'est la phrase a ne pas couper.
           ⭐ `flex: 1 1 auto` + `.n-pied { flex-wrap: wrap }` : un lieu court
           reste au rang de la nature, un lieu long prend sa propre ligne et
           s'y plie. Mesure : 0 tronque, 3 lignes au pire a 320, et les trois
           boutons ne bougent pas d'un pixel — la feuille grandit vers le HAUT.
           ⛔ Pas de clamp a 2 lignes : le pire lieu en demande 3, et couper
           « aujourd'hui Musee des Beaux-Arts » redonnerait le defaut. */
        #histoire-feuille .n-pied #h-nature { white-space: nowrap; flex: 0 0 auto; }
        #histoire-feuille .n-pied #h-lieu { flex: 1 1 auto; min-width: 0; }
        /* Registre grave : l'interface se tait. Ni or, ni italique, ni emoji. */
        #histoire-feuille.grave .h-accroche { color: var(--craie); font-style: normal; }
        #histoire-feuille.grave .n-cat { color: var(--craie2); }
        #histoire-feuille.grave .bouton.plein { background: var(--craie2); border-color: var(--craie2); }
        .h-recit p { font-size: 14px; color: var(--fumee); line-height: 1.6; margin: 12px 0 0; }
        .h-recit p:first-child { margin-top: 14px; }
        .h-source { display: flex; gap: 9px; align-items: flex-start; font-size: 13px; color: var(--fumee); margin: 8px 0 0; line-height: 1.45; }
        .h-source .n { flex: 0 0 auto; color: var(--fumee2); font-variant-numeric: tabular-nums; }
        .h-source a { color: var(--craie2); text-decoration: underline; text-underline-offset: 2px; word-break: break-word; }
        /* « Sur le chemin de » : une pilule de la famille (19/09), qui se COUPE au lieu
           de casser — le nom le plus long du catalogue fait 35 signes, la feuille 272 px
           de large a 320. */
        .h-balade { max-width: 100%; margin: 0 6px 6px 0; }
        .h-balade .tronque { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }

        /* La voix : un lecteur a nous — le lecteur natif est blanc sur un fond
           que nous ne controlons pas. Un bouton de 48 px, une barre, le temps. */
        .lecteur { display: flex; align-items: center; gap: 12px; margin-top: 14px; padding: 12px; border-radius: 16px; background: var(--n2); border: 1px solid rgba(255,255,255,.08); }
        .lecteur .lire { width: 48px; height: 48px; flex: 0 0 48px; border-radius: 50%; border: 0; background: var(--ambre); color: #191100; display: flex; align-items: center; justify-content: center; cursor: pointer; }
        .lecteur .lire svg { width: 20px; height: 20px; }
        .lecteur .piste { flex: 1; min-width: 0; }
        .lecteur .qui { font-size: 13px; color: var(--craie2); font-weight: 600; margin: 0 0 7px; line-height: 1.3; }
        .lecteur .barre { height: 4px; border-radius: 99px; background: rgba(255,255,255,.14); overflow: hidden; }
        .lecteur .barre i { display: block; height: 100%; width: 0; background: var(--ambre); }
        .lecteur .temps { display: flex; justify-content: space-between; font-size: 12px; color: var(--fumee); margin-top: 6px; font-variant-numeric: tabular-nums; }
        #histoire-feuille.grave .lecteur .lire { background: var(--craie2); }
        #histoire-feuille.grave .lecteur .barre i { background: var(--craie2); }

        /* ── ÉCOUTER UNE HISTOIRE (21/09) — contrat : docs/mockups/ecouter-une-histoire.html,
           option A (« ok pour A, oui le surlignage, pas de durée sur un drame »).
           ⭐ La porte vit sous le lecteur d'une vraie voix et au-dessus du récit.
           Touchée, elle est REMPLACÉE par `.lecteur` — celui d'une voix d'habitant,
           au pixel près : aucun contrôle neuf, lecture et pause suffisent.
           ⚠️ Tout le bloc est la cible : 61 px à 375 comme à 320, donc au-dessus
           du plancher de 44 sans avoir à le déclarer. */
        .ecouter { display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
                   background: var(--n2); border: 1px solid var(--trait); border-radius: 14px;
                   padding: 11px 14px; margin-top: 14px; min-height: 44px; color: var(--craie); cursor: pointer; }
        .ecouter .rond { width: 32px; height: 32px; flex: 0 0 32px; border-radius: 50%; background: var(--ambre);
                         color: #191100; display: flex; align-items: center; justify-content: center; }
        .ecouter .rond svg { width: 15px; height: 15px; }
        .ecouter b { font-size: 14px; font-weight: 700; display: block; line-height: 1.25; }
        .ecouter i { font-style: normal; font-size: 12px; color: var(--fumee2); display: block; margin-top: 1px; }
        /* Le lien discret, quand une vraie voix est déjà là. `.rc-lien` porte
           déjà les 44 px du produit — on n'en redessine pas un second. */
        .lecture-lien { margin-top: 4px; }
        /* ⭐⭐ LE SURLIGNAGE : le paragraphe qu'on entend. ⚠️ Le texte passe en
           `--craie` et ce n'est PAS cosmétique — la teinte éclaircit le plan, et
           `--fumee` y tomberait sous 4,5 (mesuré : 4,44 sur --n3). */
        .h-recit p.lu { color: var(--craie); background: var(--lu-fond); border-radius: 6px;
                        box-shadow: -8px 0 0 var(--lu-fond), 8px 0 0 var(--lu-fond); }
        #histoire-feuille.grave .ecouter .rond { background: var(--craie2); }
        #histoire-feuille.grave .h-recit p.lu { background: rgba(214,214,224,.13);
                        box-shadow: -8px 0 0 rgba(214,214,224,.13), 8px 0 0 rgba(214,214,224,.13); }

        /* Une histoire EN CHEMIN, sur la page d'une balade : pas une etape (pas
           de numero), une bulle sur le rail, et un texte qui se lit comme une
           parenthese entre deux etapes. */
        .en-chemin { display: flex; gap: 12px; }
        .en-chemin .rail { display: flex; flex-direction: column; align-items: center; flex: 0 0 32px; }
        .en-chemin .bulle { width: 24px; height: 24px; border-radius: 7px; border: 2px solid var(--jaune); color: var(--accent); background: #1a1a1a; display: flex; align-items: center; justify-content: center; flex: 0 0 24px; margin-top: 3px; }
        .en-chemin .bulle svg { width: 13px; height: 13px; }
        .en-chemin.grave .bulle { border-color: var(--craie2); color: var(--craie2); }
        .en-chemin .txt { flex: 1; min-width: 0; text-align: left; background: none; border: 0; padding: 0 0 12px; color: inherit; font-family: inherit; cursor: pointer; }
        .en-chemin .sur { font-size: 11px; font-weight: 900; color: var(--accent); text-transform: uppercase; letter-spacing: .08em; margin: 5px 0 0; }
        .en-chemin.grave .sur { color: var(--craie2); }
        .en-chemin .t { font-size: 15px; line-height: 1.2; margin: 3px 0 0; }
        .en-chemin .a { font-size: 13px; color: var(--fumee); margin: 4px 0 0; line-height: 1.45; }
        .en-chemin .a.leger { color: var(--or); font-style: italic; }

        /* ══ « UNE HISTOIRE ICI » — la proposition, dockee ══════════════════
           Le GPS propose, l'utilisateur touche. Une ligne au-dessus de la barre
           d'onglets — ou de la barre de suivi, si un parcours est en cours —
           jamais une feuille qui s'ouvre toute seule sous le pouce du marcheur.
           Elle vit au niveau de l'application, comme la barre de suivi : rien
           n'est ancre sur la carte. */
        #histoire-bulle {
            position: absolute; left: 10px; right: 10px; z-index: 125;
            bottom: calc(var(--tab-h) + 8px + var(--suivi-h, 0px) + env(safe-area-inset-bottom));
            display: flex; align-items: center; gap: 12px;
            background: var(--n3); border: 1px solid rgba(255,215,0,.45); border-radius: 18px;
            padding: 10px 6px 10px 14px; box-shadow: 0 10px 30px rgba(0,0,0,.55);
        }
        #histoire-bulle[hidden] { display: none; }
        #histoire-bulle .bulle { width: 40px; height: 40px; flex: 0 0 40px; border-radius: 12px; border: 2px solid var(--jaune); color: var(--accent); background: #1a1a1a; display: flex; align-items: center; justify-content: center; }
        #histoire-bulle .bulle svg { width: 20px; height: 20px; }
        #histoire-bulle.grave { border-color: rgba(214,214,224,.35); }
        #histoire-bulle.grave .bulle { border-color: var(--craie2); color: var(--craie2); }
        #histoire-bulle .txt { flex: 1; min-width: 0; text-align: left; background: none; border: 0; padding: 0; color: inherit; font-family: inherit; cursor: pointer; }
        #histoire-bulle .sur { font-size: 11px; font-weight: 900; color: var(--accent); text-transform: uppercase; letter-spacing: .08em; margin: 0; }
        #histoire-bulle.grave .sur { color: var(--craie2); }
        #histoire-bulle .nom { font-size: 14px; font-weight: 800; line-height: 1.2; margin: 2px 0 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
        /* « Plus tard » : le `.btn-rond` du système (23/09), comme la croix des feuilles. */
        #histoire-bulle .x { margin-right: 6px; }
        .iphone.inerte > #histoire-bulle { pointer-events: none; }

        /* ══ LA PORTE « RACONTER » — sur la carte ═══════════════════════════
           Contrat : `docs/mockups/raconter-porte-carte.html`, option B validee
           le 12/09. Le formulaire existait deja ; il n'avait qu'UNE porte, au
           bas de la feuille d'une AUTRE histoire — mesure : six gestes depuis
           la carte, et de 664 a 1042 px de defilement selon l'histoire. Il
           fallait lire le souvenir de quelqu'un d'autre jusqu'au bout pour
           apprendre qu'on pouvait deposer le sien.

           ⛔ ELLE PORTE SON MOT, ce n'est pas un rond muet. Les quatre ronds de
           droite sont des gestes de CARTE — recentrer, zoomer, lire la legende ;
           un cinquieme rond se lirait comme un cinquieme outil de carte, et
           « raconter » n'a pas d'icone que tout le monde lit.
           ⛔ Ni dans le rail : a 375 px une sixieme pastille tomberait a
           x 566→688, hors champ — et elle ressemblerait aux quatre FILTRES
           qu'elle cotoierait. Un bouton qui CREE au milieu de boutons qui
           filtrent est un piege.

           ⚠️⚠️ ELLE MONTE, ELLE NE SE CACHE PAS. La bande du bas est l'espace
           le plus dispute de l'app : barre d'onglets 56, dock de suivi 72,
           bulle « une histoire ici » 66 a 75 selon la longueur du nom. Deux
           portes valent mieux qu'une — celle de la bulle mene au souvenir d'un
           AUTRE. D'ou les deux variables : sans elles, la pastille se poserait
           SUR la bulle, et le GPS recouvrirait la porte au moment meme ou
           quelqu'un est devant l'endroit qu'il veut raconter.

           ⚠️ Inter s'arrete a 800 dans cette page : ecrire 900 ici ferait
           synthetiser une graisse. Le mockup rendait deja 800. */
        .carte-raconter {
            position: absolute; right: 16px; z-index: 124;
            bottom: calc(var(--tab-h) + 16px + var(--suivi-h, 0px) + var(--bulle-h, 0px) + env(safe-area-inset-bottom));
            /* ⚠️ 44 px, pas moins : c'est la cible minimale de tout ce produit
               (les ronds de la carte, la croix d'une feuille). Demande d'Hakim
               le 12/09 — 56 px prenait trop de ville. Le mockup montrait 56 :
               ecart assume, et reporte dans le contrat. */
            height: 44px; display: flex; align-items: center; gap: 7px;
            padding: 0 16px 0 13px; border: 0; border-radius: 99px;
            /* ⚠️ L'or de remplissage des ACTIONS est `--ambre` (23/09) : Raconter était le
               seul bouton plein en #FFD700, avec l'onboarding et le mur. */
            background: var(--ambre); color: #191100;
            font-family: 'Inter', sans-serif; font-weight: 800; font-size: 14px; letter-spacing: -.2px;
            box-shadow: 0 10px 26px rgba(0,0,0,.6), 0 0 0 3px rgba(11,11,15,.55);
            cursor: pointer; transition: bottom .22s ease;
        }
        .carte-raconter svg { width: 17px; height: 17px; display: block; }
        .carte-raconter[hidden] { display: none; }

        /* ══ LE RETOUR DE LA CARTE — le pendant de Raconter ═════════════════
           Contrat : `docs/mockups/retour-carte.html`, variante B validee le
           19/09 (« ok pour B, garde Retour »). Il n'apparait qu'en mode « une
           balade sur la carte » et ramene a la page de la balade.
           Mesure avant : une boite `.glass` (coins 12, filet or 20 %, 13 px)
           dont le bas tombait a 96 px quand Raconter finit a 90 — et 49,5 px
           de haut contre 44. Ni le haut, ni le bas, ni le milieu ne tombaient
           sur la meme ligne, et sa position ignorait la zone sure du telephone.
           ⚠️⚠️ MEME FORMULE DE POSITION QUE `.carte-raconter`, A LA LETTRE :
           dock de suivi, bulle « une histoire ici », zone sure. C'est ce qui
           les fait monter ENSEMBLE — deux formules, c'est deux lignes le jour
           ou la bulle sort. Un test compare les deux. Noir a 92 % et filet
           blanc : le vocabulaire des ronds et des pilules de la carte, qui ne
           suit pas le theme (des controles opaques sur n'importe quelle tuile).
           44 px : le plancher, EN VRAI — plus de pseudo-element qui rallongeait
           une cible de 32. */
        .carte-retour {
            position: absolute; left: 16px; z-index: 124;
            bottom: calc(var(--tab-h) + 16px + var(--suivi-h, 0px) + var(--bulle-h, 0px) + env(safe-area-inset-bottom));
            height: 44px; display: flex; align-items: center; gap: 5px;
            padding: 0 15px 0 11px; border: 1px solid rgba(255,255,255,.14); border-radius: 99px;
            background: rgba(10,10,10,.92); color: #fff;
            font-family: 'Inter', sans-serif; font-weight: 800; font-size: 14px; letter-spacing: -.2px;
            white-space: nowrap; box-shadow: 0 10px 26px rgba(0,0,0,.6), 0 0 0 3px rgba(11,11,15,.55);
            cursor: pointer; transition: bottom .22s ease;
        }
        .carte-retour svg { width: 20px; height: 20px; display: block; }
        /* `.hidden` est ce que `visualizeBaladeRoute` et `quitterVisualisationBalade`
           posent et retirent : elle doit battre le `display:flex` ci-dessus quel
           que soit l'ordre des feuilles de style. */
        .carte-retour.hidden { display: none; }

        /* ══ LA VILLE D'AVANT — deux photos, un curseur ════════════════════
           Plein ecran, au-dessus de tout. Le curseur va d'hier a aujourd'hui :
           la photo d'epoque se fond sur celle d'aujourd'hui, prise du meme
           endroit. ⛔ Plus de camera — decision du 08/09 au soir. */
        #ville-avant { position: absolute; inset: 0; z-index: 300; background: #000; display: flex; flex-direction: column; overflow: hidden; }
        #ville-avant[hidden] { display: none; }
        #ville-avant .avant, #ville-avant .apres { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
        /* Celle d'aujourd'hui est DESSOUS et toujours opaque : c'est l'epoque
           qui s'efface au-dessus d'elle. Un fondu croise sur deux opacites
           laisserait le noir traverser au milieu du geste. */
        #ville-avant .avant { opacity: var(--avant, .5); pointer-events: none; }
        #ville-avant .apres { pointer-events: none; }
        #ville-avant .haut { position: relative; z-index: 2; display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: calc(14px + env(safe-area-inset-top)) 14px 0 20px; }
        #ville-avant .haut .sur { font-size: 11px; font-weight: 900; color: var(--jaune); text-transform: uppercase; letter-spacing: .08em; margin: 0; text-shadow: 0 1px 4px rgba(0,0,0,.85); }
        #ville-avant .haut .t { font-family: 'Montserrat', sans-serif; font-weight: 900; text-transform: uppercase; letter-spacing: -.5px; font-size: 17px; line-height: 1.1; margin: 3px 0 0; text-shadow: 0 1px 4px rgba(0,0,0,.85); }
        #ville-avant .bas { position: relative; z-index: 2; margin-top: auto; padding: 22px 20px calc(24px + env(safe-area-inset-bottom)); background: linear-gradient(transparent, rgba(0,0,0,.86) 45%); }
        #ville-avant .curseur { display: flex; align-items: center; gap: 12px; font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--fumee); }
        /* ⭐ 44 et non 32 (20/09, contrat docs/mockups/photos-histoire-telephone.html § 02) :
           mesure a l'ecran, c'est la BOITE de l'input qui recoit le toucher — un clic
           reel a 2 px de son bord haut deplace le curseur. Une boite de 44 est donc
           une zone de 44, et WebKit dessine le rail au milieu : rien ne change a l'oeil. */
        #ville-avant input[type=range] { flex: 1; accent-color: var(--or); height: 44px; margin: 0; }
        /* Quand une photo ne charge pas (pas de reseau, image retiree du sas), son texte
           de remplacement s'ecrivait en haut a gauche, PAR-DESSUS le surtitre et le titre
           — vu pendant la premiere marche de la ville d'avant, sur deux cadres noirs.
           Il reste pour un lecteur d'ecran ; a l'oeil, transparent (contrat § 03). */
        #ville-avant img { color: transparent; }
        #ville-avant .cred { font-size: 11px; color: rgba(255,255,255,.72); margin: 10px 0 0; line-height: 1.4; }

        /* ⭐ Les ronds FLOTTANTS de la carte : 44 px en vrai (ils se posent sur une
           ville qui bouge), et depuis le 23/09 cinq TRACÉS de 20 px — le « ＋ » et
           le « − » étaient des caractères de 18 px à côté de tracés de 21 et 42 px. */
        .carte-rond {
            width: 44px; height: 44px; border-radius: 50%; padding: 0;
            background: rgba(10,10,10,.92); border: 1px solid rgba(255,255,255,.14);
            color: #fff; display: flex; align-items: center; justify-content: center;
        }
        .carte-rond .i { width: 20px; height: 20px; }
        .carte-rond:active { border-color: var(--ambre); }
        /* ⭐ CARTE NUE armée : le rond s'allume comme une pastille de filtre
             active — c'est le même vocabulaire, et c'est le seul rond qui porte
             un ÉTAT. Les quatre autres sont des gestes qui ne durent pas. */
        .carte-rond[aria-pressed="true"] { background: var(--ambre); border-color: var(--ambre); color: #191100; }

        /* La ligne qui nomme le vide. Elle se pose SOUS la barre de filtres —
             mesuré : la barre finit à y 100, elle commence à 105. */
        /* La fiche, le groupe et la legende sont des feuilles depuis le 08/09 :
           plus rien n'est ancre au-dessus de la barre de suivi, la barre dockee
           passe SOUS les feuilles. */

        /* ══ LA FAMILLE DES PILULES — un dessin, deux corps ══════════════════
           Contrat : `docs/mockups/pilules.html`, variante C validee le 19/09
           (« ok pour C, garde les mots »). L'app parlait CINQ langues de pilules :
           les categories de LIEUX (39 px, 14 px, #FFD700 en dur), ses filtres
           (38 px, minuscules, contour or pose en styles inline), les filtres de
           BALADES (33,5 px), le segment « Populaires / Mes balades » (31,5 px dans
           une boite de 41,5) et les pastilles de la carte (34 px, zone 48 — la
           seule rangee validee telle quelle, le 18/09). C'est elle qui donne le
           dessin : capitales, 800, rayon plein, l'emoji dans son `span`, l'ACTIF
           en ambre plein — l'or de REMPLISSAGE, jamais l'or de texte —, filtres et
           categories dans la meme pilule (un filtre est une demande de montrer).
           ⭐ DEUX CORPS, comme les tailles de l'echelle de texte :
           · STANDARD, 44 px, 13 px — dans les listes, ou la pilule est une
             commande de premier plan, a cote d'un champ de 46 et de boutons de 44 ;
           · COMPACTE, 34 px, 12 px, zone tactile de 48 par pseudo-element — sur
             la carte, ou elle flotte au-dessus de la ville (44 px y mangeraient
             20 px de ville par rangee : ecarte, mockup n° 03).
           ⚠️ Sur fond d'app elle prend les JETONS du theme (--n2, --trait,
           --craie2 : creme et trait noir en clair) ; sur la carte, `.carte` la
           garde noire et opaque dans les deux themes — des controles lisibles sur
           n'importe quelle tuile (regle du 14/09).
           ⛔ Aucune couleur en dur pour l'actif : `--ambre`, et #191100 dessus.
           ⛔ Un test (`test/pilules.test.js`) refuse toute pilule du document hors
           de cette classe, et tout retour des anciennes. */
        .pilule {
            display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; position: relative;
            height: 44px; padding: 0 14px; border-radius: 999px;
            font-family: 'Inter', sans-serif; font-size: 13px; font-weight: 800; text-transform: uppercase; white-space: nowrap;
            background: var(--n2); border: 1px solid var(--trait); color: var(--craie2);
            cursor: pointer; -webkit-tap-highlight-color: transparent;
            transition: background .15s, border-color .15s, color .15s;
        }
        .pilule.active { background: var(--ambre); border-color: var(--ambre); color: #191100; }
        /* ⭐ L'icône d'une pilule est un TRACÉ de 16 px (23/09) : c'étaient des emojis de
           13 px — « 🎯 TOUT », « 🎯 À 10 MIN DU CENTRE », le même dessin pour deux sens. */
        .pilule .i { width: 16px; height: 16px; stroke-width: 2; margin-left: -2px; }
        .pilule.compacte { height: 34px; padding: 0 11px; font-size: 12px; }
        .pilule.compacte .i { width: 14px; height: 14px; }
        .pilule.carte { background: rgba(10,10,10,.92); border-color: rgba(255,255,255,.12); color: #d1d5db; }
        .pilule.carte.active { background: var(--ambre); border-color: var(--ambre); color: #191100; }
        /* ⚠️ 44 px SOUS LE DOIGT, SANS TOUCHER AU DESSIN (18/09, passe « perfection »).
           La compacte fait 34 px : sous le plancher, sur l'ecran ou rater c'est
           toucher la carte. On ne grossit pas la pilule — la rangee est validee
           telle quelle — on ETEND la zone qui recoit le doigt, en pseudo-element,
           invisible et hors du flux : 48 px. ⛔ Ne pas retirer pour « nettoyer ».
           (Le « ← Retour » de la carte recevait la meme rallonge ; depuis le 19/09
           il fait 44 px LUI-MEME, `.carte-retour`.) */
        .pilule.compacte::before { content: ''; position: absolute; inset: -7px -2px; }
        /* Le SEGMENT (« Populaires / Mes balades ») : il choisit une liste, il ne
           filtre pas — une boite --n1 et deux pilules sans bord dedans, l'active
           en ambre. Le doigt recoit la boite entiere : la zone de chaque moitie
           s'etend a son rembourrage (38 + 2 x 3 = 44). */
        .segment { display: inline-flex; background: var(--n1); border: 1px solid var(--trait); border-radius: 999px; padding: 3px; }
        .segment .pilule { height: 38px; border: 0; background: transparent; color: var(--fumee); }
        .segment .pilule.active { background: var(--ambre); color: #191100; }
        .segment .pilule::before { content: ''; position: absolute; inset: -3px; }
        .segment[hidden] { display: none; }
        /* ⚠️ ET LA RANGEE DOIT LAISSER LA PLACE : elle defile en X, donc son
           `overflow-y` vaut `hidden` (la regle CSS : un seul axe non visible
           force l'autre), et la zone etendue du chip etait COUPEE — mesure au
           `elementFromPoint` : 3 px au-dessus du chip, la carte recevait le
           doigt. Un rembourrage compense par une marge negative garde la mise
           en page au pixel et met les 7 px dans la boite qui defile. */
        #scr-carte .rangee { padding: 7px 0; margin: -7px 0; }

        /* ---- BALADES : le parcours et ses trajets ----
             Le fil pointille relie les etapes ; il passe a l'orange sur un
             segment long, pour que le marcheur voie l'effort avant de le subir. */
        .clamp2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
        /* ══ LE MODULE NOW — L'ECRAN D'ATTERRISSAGE ═══════════════════════
           L'accueil disait « À LA UNE » et listait les trois premiers lieux
           ouverts DU CATALOGUE. Il dit desormais le MOMENT, et ce qui en
           decoule. Contrat : mockup-NOW-charleroi-irl.html, valide le 07/09. */

        /* « MERCREDI APRÈS-MIDI » est le pire cas : a text-3xl (30 px) il
           passait sur deux lignes et decalait tout l'ecran d'un jour a l'autre.
           `nowrap` empeche l'enroulement ET rend la mesure possible — sans lui,
           scrollWidth vaut clientWidth et le debordement est indetectable.
           C'est initHomeRecs qui reduit la taille si le titre ne tient pas. */
        #home-titre { font-size: 26px; line-height: 1.05; white-space: nowrap; }

        /* La phrase du moment. Un filet, pas une carte : ce n'est pas un
           element de liste, c'est l'app qui parle. */
        .n-dit { border-left: var(--dit-largeur) solid var(--dit-trait); padding: 2px 0 2px 14px; margin-bottom: 20px; }
        .n-dit p { margin: 0; font-size: 17px; line-height: 1.45; color: var(--craie); }
        .n-dit p b { font-weight: 800; color: var(--dit-fort-texte); background: var(--dit-fort-fond); padding: 0 3px; margin: 0 -3px; border-radius: 3px;
                     box-decoration-break: clone; -webkit-box-decoration-break: clone; }

        /* `.n-kick` est un titre de section de l'accueil : le dessin est celui de `.f-sec`. */
        .n-kick { margin: 26px 0 12px; }
        /* Rouge SEULEMENT pour ce qui ferme : la couleur doit rester rare pour
           vouloir dire quelque chose. */
        .n-kick.urg { color: var(--rouge); }
        .n-urgent { border-color: rgba(248,113,113,.34) !important;
                    background: linear-gradient(180deg, rgba(248,113,113,.07), var(--n1) 62%) !important; }

        /* ══ LA UNE DE L'ACCUEIL (26/09) ═════════════════════════════════════
           Contrat : `docs/mockups/a-la-une.html` (« ok vas y »). Le dessin de
           « Ça ferme dans… » — un liseré, un fond dégradé —, teinté d'OR au lieu
           du rouge : aucun dessin neuf à apprendre, et au clair elle prend d'elle-
           même le surligneur, la pastille jaune et la carte blanche.
           ⭐ La photo : un cadre 2:1 (163 px à 375, 135 à 320), la photo ENTIÈRE
           sur sa copie floutée — la règle des fiches (`.m-vue`) —, le crédit en
           bas à droite. ⭐ « Partager » (le cœur jusqu'au 27/09) : 44 px de toucher,
           À CÔTÉ de la carte (un bouton dans un lien ne se clique pas). */
        .une-cadre { position: relative; }
        .glass.une { display: block; color: inherit; text-decoration: none;
                     border-color: rgba(255,215,0,.34) !important;
                     background: linear-gradient(180deg, rgba(255,215,0,.07), var(--n1) 62%) !important; }
        .glass.une.photo { padding: 0; overflow: hidden; }
        .glass.une.photo .une-corps { padding: 16px 20px 20px; }
        .une-cadre.sans-photo .n-cat { max-width: calc(100% - 44px); }
        .une-photo { position: relative; aspect-ratio: 2 / 1; overflow: hidden; background: var(--n2); }
        .une-photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; display: block; }
        .une-photo img.une-fond { object-fit: cover; filter: blur(16px) brightness(.5) saturate(1.15); transform: scale(1.15); }
        .une-credit { position: absolute; right: 8px; bottom: 7px; font-size: 11px; letter-spacing: .02em; line-height: 1.35;
                      color: rgba(255,255,255,.92); background: rgba(6,6,10,.62); padding: 3px 7px; border-radius: 6px;
                      max-width: calc(100% - 16px); overflow: hidden;
                      display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
        /* Une phrase, pas une punchline : ni italique, ni guillemets. */
        .une-texte { font-size: 14px; line-height: 20px; color: var(--craie); margin: 6px 0 0; }
        .n-pied.une-lien { flex-wrap: nowrap; align-items: center; }
        .une-lien .ou { color: var(--or); font-weight: 800; white-space: nowrap; }
        .une-lien .tronque { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        /* ⚠️ `.n-pied .i` (15 px, posé PLUS BAS dans la feuille) battrait un `.une-lien .i` : le chevron
           prend la spécificité qu'il lui faut, et le trait du contrat (2,4 ; 2 pour la flèche qui sort). */
        .n-pied.une-lien .i.chev { width: 18px; height: 18px; flex: 0 0 18px; margin: 0; color: var(--fumee); stroke-width: 2.4; }
        .n-pied.une-lien .i.chev.ext { stroke-width: 2; }
        /* ⭐ « Partager » (27/09) à la place du cœur, au pixel près : 44 px de toucher, 36 dessinés. Jamais allumé —
           partager est un geste, pas un état. Sur la photo, le rond reste sombre dans les deux thèmes. */
        .une-partager { position: absolute; top: 4px; right: 4px; width: 44px; height: 44px; border: 0; padding: 0;
                        background: transparent; display: flex; align-items: center; justify-content: center;
                        color: #fff; cursor: pointer; }
        .une-partager::before { content: ""; position: absolute; inset: 4px; border-radius: 50%; background: rgba(11,11,15,.55); }
        .une-partager .i { position: relative; width: 20px; height: 20px; margin: 0; stroke-width: 2; }
        .une-cadre.sans-photo .une-partager { top: 10px; right: 10px; color: var(--fumee); }
        .une-cadre.sans-photo .une-partager::before { background: var(--rond-fond); }
        /* ⭐ Les nouveaux Charlembassadeurs (27/09, contrat une-partager-charlembassadeurs.html, B) : une ligne sous
           l'aveu du bloc du bas, un filet au-dessus ; les noms en clair, jamais un nombre. */
        .amb-nouv { font-size: 13px; line-height: 19px; color: var(--fumee); margin: 10px 0 0; padding-top: 10px;
                    border-top: 1px solid var(--trait); }
        .amb-nouv b { color: var(--craie); font-weight: 800; }

        /* ══ L'HISTOIRE DU JOUR ═══════════════════════════════════════════
           Contrat : `docs/mockups/histoire-du-jour.html`, arbitre B le 12/09.
           La meme economie que `.n-urgent` — un lisere et un fond degrade —
           pour dire « ce n'est pas un lieu » sans ajouter une ligne de texte.
           ⛔ Aucun style de registre GRAVE ici : l'accueil n'en montre aucune
           (regle 0). Le gris tient la bulle et la feuille depuis le 8/09 ; un
           style qui n'a aucun cas ne s'ecrit pas. */
        .n-histoire { border-color: var(--hist-trait) !important;
                      background: linear-gradient(180deg, var(--hist-voile), var(--n1) 64%) !important; }
        .n-h-top { display: flex; align-items: flex-start; gap: 11px; }
        .n-h-top .txt { min-width: 0; }
        .n-h-ic { width: 34px; height: 34px; flex: 0 0 34px; border-radius: 11px;
                  display: flex; align-items: center; justify-content: center;
                  color: var(--ic-texte); background: var(--ic-fond);
                  border: 1px solid var(--ic-trait); }
        .n-h-ic svg { width: 17px; height: 17px; display: block; }
        .n-h-acc { font-size: 14px; color: var(--fumee); line-height: 1.5; margin: 9px 0 0; }
        /* Le lieu se tronque, « Lire → » ne bouge pas : c'est lui qu'on vise. */
        .n-histoire .n-pied .tronque { flex: 1; }
        .n-h-go { color: var(--accent); font-weight: 800; display: inline-flex; align-items: center; gap: 2px; }
        .n-h-go .i { width: 16px; height: 16px; margin: 0; stroke-width: 2.2; }
        /* ⭐ LA PORTE VERS LES AUTRES. Sous l'arbitrage B, c'est le SEUL chemin
           de l'accueil vers les sept histoires graves : elle se touche, elle
           n'est pas un commentaire. */
        /* « Les 37 histoires sont sur la carte » : un `.bouton.nu` depuis le 23/09 —
           une phrase grise au mot souligné, le seul lien de l'accueil. */
        .n-h-porte { margin: 0 0 6px; }

        .n-cat { font-size: 11px; font-weight: 900; color: var(--pastille-texte); text-transform: uppercase;
                 letter-spacing: .08em; margin: 0 0 5px; }
        .n-pun { font-size: 14px; font-style: italic; color: var(--ambre-texte); margin: 6px 0 0; line-height: 1.4; }
        /* ⚠️ `flex-wrap` n'est pas decoratif : depuis que la distance dit son
           ORIGINE (« 8 min du centre » au lieu de « 8 min »), les trois
           informations ne tiennent plus sur une ligne a 320 px — mesure : le
           pied debordait de 4 a 13 px, et seulement a cette largeur. Sans le
           wrap il faudrait sacrifier le quartier ou l'origine ; avec lui, la
           distance passe a la ligne et rien n'est perdu. ⛔ A 375 px, rien ne
           change : le pied tient toujours sur une seule ligne. */
        .n-pied { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 9px;
                  margin-top: 11px; padding-top: 10px;
                  border-top: 1px solid var(--trait); font-size: 13px; color: var(--fumee); }
        /* La distance va a DROITE, comme dans toute l'app. */
        .n-sp { margin-left: auto; white-space: nowrap; }
        /* ⭐ LA DISTANCE A SA LIGNE (23/09) : elle passait à la ligne SELON la longueur de
           l'horaire et du quartier — calée à droite quand elle ne tenait pas, sur la même
           ligne sinon : deux cartes voisines, deux mises en page. Elle est toujours la
           seconde ligne du pied, alignée à gauche comme ce qui la précède. */
        .n-pied .dist { flex-basis: 100%; margin-left: 0; }
        /* La distance porte son tracé (marche, voiture, épingle, cible) — plus un emoji. */
        .n-pied .i, .n-sp .i { width: 15px; height: 15px; margin: -2px 4px 0 0; stroke-width: 2; }

        .b-theme { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .4px; color: var(--chip-texte); background: var(--chip-fond); padding: 4px 9px; border-radius: 999px; }
        /* Le thème porte son tracé (23/09) : « 🚶 À PIED » était un emoji de 12 px. */
        .b-theme .i { width: 14px; height: 14px; stroke-width: 2.1; }
        /* ⭐ LA CARTE DE BALADE de la liste est partie le 26/09 : une balade y est une
           LIGNE (§ BALADES — deux onglets). Ce qui suit est la PAGE d'une balade.
           Duree, distance et effort etaient trois mots gris colles en fin de
           ligne. Chacun a sa case : la valeur en clair, le libelle dessous. */
        .b-reperes { display: flex; gap: 6px; margin-top: 12px; }
        .b-repere {
            flex: 1; min-width: 0; background: var(--n2); border-radius: 12px;
            padding: 8px 6px; text-align: center;
        }
        .b-repere .v {
            display: block; font-family: 'Montserrat', sans-serif; font-weight: 900;
            font-size: 17px; color: var(--craie); line-height: 1;
            font-variant-numeric: tabular-nums;
        }
        /* Ecart assume au mockup : il donnait --fumee2 (#7C7C8C) pour ce
           libelle, mesure a 3,98:1 sur son fond — sous le seuil de 4,5 pour du
           texte de 8,5 px. Remonte a --fumee, qui passe a 6,4:1. */
        .b-repere .l {
            display: block; font-size: 11px; color: var(--fumee); text-transform: uppercase;
            letter-spacing: .07em; margin-top: 4px;
            white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
        }
        /* ⚠️ AU PLANCHER, les valeurs ne tiennent plus dans leur repere.
           Mesure a 320 px : la boite offre 60 px, et en Montserrat 900 a 17 px
           « Soutenu » en reclame 77, « Modere » 70, « 6,4 km » 64. Le libelle
           (`.l`) avait deja son ellipsis ; la valeur (`.v`) n'avait RIEN, donc
           un mot unique trop long sortait de son cadre — visible a l'oeil, et
           sur les trois balades qui annoncent un effort.
           ⭐ Deux gestes ensemble, parce qu'aucun ne suffit seul : le padding
           passe de 6 a 3 px (60 → 66 px de place) et la valeur descend au
           palier « corps fort » (15 px) puis « corps » (14 px). Mesure apres
           coup : « Soutenu » 63, « Modere » 57, « 6,4 km » 52 — tout tient.
           ⛔ Rien ne change au-dessus de 360 px : la regle ne s'y applique pas.
           ⚠️ « À pied + TEC » (95 px meme a 14) reste sur DEUX lignes : c'est
           voulu, la boite grandit et rien ne deborde. */
        @media (max-width: 359px) {
            .b-repere { padding: 8px 3px; }
            .b-repere .v { font-size: 14px; }
        }
        /* ══ LES GRIS DE TEXTE ═════════════════════════════════════════════
           74 usages de `text-gray-400/500/600`. Sur #0B0B0F, gray-600 (#4B5563)
           donne un contraste de 3:1 et gray-500 (#6B7280) a peine 4,4:1 : sous
           le seuil de lecture confortable, et c'est ce qui portait la moitie des
           informations secondaires — horaires, distances, departs.

           Le selecteur est prefixe par `.iphone` a dessein : le CDN Tailwind
           est charge APRES cette feuille et gagnerait a specificite egale.
           Deux classes battent une classe. */
        /* Les surfaces internes suivent les memes plans. Meme raison de
           specificite que pour les gris : deux classes battent une classe. */
        .iphone .bg-white\/5,
        .iphone .bg-white\/\[0\.06\] { background-color: var(--n1) !important; }
        .iphone .bg-white\/10 { background-color: var(--n2) !important; }
        .iphone .bg-white\/15,
        .iphone .bg-white\/20 { background-color: var(--n3) !important; }

        .iphone .text-gray-300 { color: var(--craie2) !important; }
        .iphone .text-gray-400 { color: var(--fumee) !important; }
        .iphone .text-gray-500 { color: var(--fumee) !important; }
        .iphone .text-gray-600 { color: var(--fumee2) !important; }
        /* ══ LES DEUX THEMES ET LES CLASSES TAILWIND ECRITES EN DUR (19/09) ═══
           Vingt-quatre `text-yellow-400`, sept `text-white`, des verts et des
           rouges : sur le clair, l'or ne se lit pas et le blanc disparait. Le
           meme motif que les gris remappes juste au-dessus — une regle par
           classe, jamais une retouche par usage.
           ⛔ `text-black` et `bg-yellow-400` : du noir sur un remplissage jaune,
           juste dans les deux themes — le remplissage prend `--jaune`. */
        .iphone .text-yellow-400, .iphone .text-yellow-300 { color: var(--accent) !important; }
        .iphone .bg-yellow-400 { background-color: var(--jaune) !important; }
        html[data-theme="clair"] .iphone .text-white { color: var(--craie) !important; }
        html[data-theme="clair"] .iphone .hover\:text-white:hover { color: var(--craie) !important; }
        html[data-theme="clair"] .iphone .text-green-400,
        html[data-theme="clair"] .iphone .text-green-300 { color: var(--vert) !important; }
        html[data-theme="clair"] .iphone .text-red-400 { color: var(--rouge) !important; }
        html[data-theme="clair"] .iphone .bg-black\/90 { background-color: rgba(244,241,234,.94) !important; }
        /* la duree d'une balade et la pastille Fidelite : du noir sur du jaune plein, comme le mockup */
        html[data-theme="clair"] .iphone .bg-yellow-400\/10.text-yellow-400,
        html[data-theme="clair"] .iphone .bg-yellow-400\/20.text-yellow-400 { background-color: var(--jaune) !important; color: #0B0B0F !important; }

        /* ══ LE CLAIR DONNE UNE FORME A L'OR (contrat : mockup B) ═════════════
           Sur le fonce, l'or est une couleur de texte. Sur le clair il ne se lit
           pas : il devient une MATIERE — pastille sous la categorie, surligneur
           sous la punchline, marqueur sous le titre de section. ⚠️ Scope au
           clair : sur le fonce, ces `display` changeraient la mise en page. */
        html[data-theme="clair"] :is(.f-sec, .n-kick):not(.urg) { display: inline-block; background: linear-gradient(transparent 66%, var(--marqueur) 66%); padding: 0 2px; margin-left: -2px; }
        html[data-theme="clair"] .n-cat { display: inline-block; background: var(--pastille-fond); padding: 3px 8px; border-radius: 99px; }
        html[data-theme="clair"] .n-pun { display: inline; background: var(--surligneur); padding: 1px 4px; margin: 0 -4px; border-radius: 3px;
                                          box-decoration-break: clone; -webkit-box-decoration-break: clone; }
        /* ⚠️ Un inline ne porte pas de marge haute, et un `::before` en bloc heriterait
           du surligneur (vu a l'ecran : un petit carre jaune sous le titre). C'est le
           TITRE qui precede qui porte l'espace — `:has()` vise exactement lui. */
        html[data-theme="clair"] .heading:has(+ .n-pun) { margin-bottom: 6px; }

        /* ══ LE BANDEAU DE L'ACCUEIL — le jaune de l'icone, sur le clair seulement ══
           Sur le fonce, `.bandeau` n'a ni fond ni marge : il est transparent a la
           mise en page. Sur le clair, il sort du gabarit jusqu'aux bords et sous la
           barre d'etat, comme le mockup B. */
        html[data-theme="clair"] #scr-home .bandeau {
            margin: calc(-20px - env(safe-area-inset-top)) -24px 22px;
            padding: calc(20px + env(safe-area-inset-top)) 24px 8px;
            background: var(--bandeau-fond); color: var(--bandeau-texte);
        }
        html[data-theme="clair"] #scr-home .bandeau .text-gray-400 { color: var(--bandeau-sous) !important; }
        html[data-theme="clair"] #scr-home .bandeau .n-dit { margin-bottom: 8px; }
        html[data-theme="clair"] #scr-home .bandeau .glass { background: #0B0B0F; border-color: transparent; }
        html[data-theme="clair"] #scr-home .bandeau .avatar { background: #0B0B0F; color: var(--jaune); }
        html[data-theme="clair"] .iphone #scr-home .bandeau .text-yellow-400 { color: var(--jaune) !important; }

        /* ══ CE QUE L'AUDIT DU DOM A TROUVE EN CLAIR (19/09) — le contraste de chaque
           texte visible, calcule sur son fond effectif, ecran par ecran ═══════
           · la feuille redefinit ses plans en dur (l'echelle est relative au plan
             porteur) : sur une feuille blanche, ses cartes restaient sombres ;
           · les chips de theme d'une balade prennent un pastel INLINE (le liseré de la carte de la liste — partie le 26/09 — et la page),
             lisible sur le noir, pas sur le blanc : noir sur jaune, comme le mockup ;
           · trois oranges Tailwind d'avertissement. */
        html[data-theme="clair"] .feuille { --n1: #F4F1EA; --n2: #EFEBE1; }   /* #E9E4D8 faisait tomber --fumee2 a 4,32 : mesure */
        html[data-theme="clair"] #histoire-feuille.grave .bouton.plein { color: #FFFFFF; }   /* le neutre du grave est l'encre sur le clair : le texte s'inverse */
        html[data-theme="clair"] #b-theme { color: #0B0B0F !important; background: var(--jaune); padding: 4px 9px; letter-spacing: .4px; }
        /* ⚠️ Le titre du segment long (« 1,8 km — 25 min a pied ») prend le JETON, plus
           la classe `text-orange-400` : une classe Tailwind fige #FB923C sur le fonce,
           et c'est la que le contraste manquait (4,03 sur la teinte). */
        html[data-theme="clair"] .iphone .text-orange-200\/80 { color: var(--orange-texte) !important; }

        .etape-rail { display: flex; flex-direction: column; align-items: center; flex: 0 0 32px; }
        .etape-num { width: 32px; height: 32px; border-radius: 50%; background: #fbbf24; color: #000; font-weight: 900; font-size: 14px; display: flex; align-items: center; justify-content: center; flex: 0 0 32px; }
        .etape-fil { width: 2px; flex: 1; margin: 4px 0; background: repeating-linear-gradient(to bottom, rgba(251,191,36,.35) 0 4px, transparent 4px 9px); }
        .etape-fil.long { background: repeating-linear-gradient(to bottom, rgba(251,146,60,.5) 0 4px, transparent 4px 9px); }

        /* ══ UNE ETAPE PEUT ETRE UNE HISTOIRE (26/09) — contrat docs/mockups/balade-grave.html ══
           La ligne d'une histoire en chemin (`.en-chemin`) — surtitre, titre, accroche — et ce qu'une
           balade doit dire en plus : OU REGARDER. Grave, elle se tait ; legere, l'or de l'en-chemin. */
        .e-txt { flex: 1; min-width: 0; padding: 0 0 6px; text-align: left; background: none; border: 0; color: inherit; font: inherit; cursor: pointer; }
        .e-sur { font-size: 11px; font-weight: 900; color: var(--craie2); text-transform: uppercase; letter-spacing: .08em; margin: 7px 0 0; line-height: 1.3; }
        .e-sur.leger { color: var(--accent); }
        .e-t { font-size: 16px; line-height: 1.15; margin: 4px 0 0; }
        .e-a { font-size: 14px; color: var(--fumee); line-height: 1.5; margin: 6px 0 0; }
        .e-a.leger { color: var(--or); font-style: italic; }
        .e-ou { display: flex; gap: 6px; font-size: 13px; color: var(--fumee2); line-height: 1.4; margin: 8px 0 0; }
        .e-ou .i { width: 15px; height: 15px; flex: 0 0 15px; margin-top: 1px; stroke-width: 2; }

        /* ══ UNE BALADE GRAVE SE TAIT (26/09) — le neutre de la feuille d'une histoire grave
           (`#histoire-feuille.grave .bouton.plein`), étendu à tout ce qu'une balade grave dessine :
           la page, la liste, le suivi, la fin. Ni or, ni italique. ⚠️ Le theme porte un pastel INLINE
           (`couleurTheme`) : `viewBalade` ne le pose pas sur une balade grave. ══ */
        .page.grave #b-theme { color: var(--craie2); }
        .page.grave .f-sec, .arr-grave .f-sec { color: var(--craie2); font-style: normal; }
        .page.grave .etape-num { background: var(--craie2); color: #191100; }
        .page.grave .etape-fil { background: repeating-linear-gradient(to bottom, rgba(214,214,224,.30) 0 4px, transparent 4px 9px); }
        .page.grave .bouton.plein, .suivi-dock.grave .bouton.plein, #suivi-feuille-corps.grave .bouton.plein {
            background: var(--craie2); border-color: var(--craie2); color: #191100; }
        .suivi-dock.grave .anneau { background: conic-gradient(var(--craie2) 0 var(--part, 0%), rgba(255,255,255,.12) var(--part, 0%) 100%); }
        .suivi-dock.grave .sous b.ouvert { color: var(--craie); }
        .pas.grave span.on { background: var(--craie2); }
        .tuile.grave { color: var(--craie2); }
        .balade.grave .b-signe.cours { color: var(--craie2); }
        /* La fin d'une balade grave (variante A) : montree seule quand `#scr-arrivee` porte `.grave`. */
        #scr-arrivee .arr-grave { display: none; }
        #scr-arrivee.grave .arr-fanion, #scr-arrivee.grave .arr-corps { display: none; }
        #scr-arrivee.grave .arr-grave { display: block; max-width: 520px; margin: 0 auto; padding: calc(16px + env(safe-area-inset-top)) 24px 0; }
        .arr-g-tete { display: flex; justify-content: flex-end; }
        .arr-g-sur { font-size: 11px; font-weight: 900; text-transform: uppercase; letter-spacing: .08em; color: var(--craie2); margin: 2px 0 0; }
        .arr-g-titre { font-size: 26px; line-height: 1.05; margin: 6px 0 0; }
        .arr-g-sous { font-size: 14px; line-height: 1.6; color: var(--fumee); margin: 8px 0 0; }
        .arr-grave .tuile.num { color: var(--craie2); font-family: 'Montserrat', sans-serif; font-weight: 900; font-size: 14px; }
        .arr-grave .groupe .n { display: block; font-size: 15px; line-height: 1.15; }
        .arr-grave .groupe .e-sur { display: block; margin: 0 0 3px; }
        .arr-g-actions { display: flex; flex-direction: column; gap: 10px; margin-top: 22px; }
        /* Sur le clair, le neutre du grave est l'ENCRE : le texte s'inverse — la regle de la feuille
           d'une histoire grave. Ni la pastille jaune du theme, ni le surligneur d'un titre de section. */
        html[data-theme="clair"] .page.grave .etape-num,
        html[data-theme="clair"] :is(.page.grave, .suivi-dock.grave, #suivi-feuille-corps.grave) .bouton.plein { color: #FFFFFF; }
        html[data-theme="clair"] .page.grave .etape-fil { background: repeating-linear-gradient(to bottom, rgba(42,42,53,.28) 0 4px, transparent 4px 9px); }
        html[data-theme="clair"] .page.grave #b-theme { color: var(--craie2) !important; background: transparent; padding: 0; }
        html[data-theme="clair"] :is(.page.grave, .arr-grave) .f-sec { background: none; }
        .trajet-long { background: rgba(251,146,60,.1); border: 1px solid rgba(251,146,60,.32); border-radius: 12px; padding: 9px 11px; }
        /* letter-spacing negatif des titres : il colle l'apostrophe a la lettre
             suivante et « L'ITINERAIRE » se lit « LTTINERAIRE ». */
        .heading .apo { letter-spacing: 0; }

        /* ⭐ LE CHOIX D'UNE QUESTION (23/09) — l'onboarding : une tuile d'icône, le mot
           en gras, la phrase dessous, la coche à droite. ⚠️ Plus de `scale(1.02)` :
           le choix sélectionné débordait de 3 px de chaque côté (331,5 px de large
           contre 325), et plus de carte entière en jaune plein — trois pavés jaunes
           à l'écran quand on en choisit deux. Le choix se dit par le filet ambre, la
           teinte et la tuile qui se remplit. */
        .btn-choice {
            display: flex; align-items: center; gap: 14px; width: 100%; min-height: 64px; box-sizing: border-box;
            padding: 12px 14px; margin-bottom: 8px; text-align: left; cursor: pointer;
            background: var(--n1); border: 1px solid var(--trait); border-radius: var(--r-ctl);
            color: var(--craie2); font-family: inherit; -webkit-tap-highlight-color: transparent;
            transition: background-color .15s, border-color .15s;
        }
        .btn-choice .txt { flex: 1; min-width: 0; }
        .btn-choice .txt b { display: block; font-size: 15px; font-weight: 800; color: var(--craie); line-height: 1.25; }
        .btn-choice .txt span { display: block; font-size: 13px; color: var(--fumee); line-height: 1.4; margin-top: 2px; }
        .btn-choice .coche {
            width: 24px; height: 24px; flex: 0 0 24px; border-radius: 50%; box-sizing: border-box;
            border: 1.5px solid var(--trait-fort); display: flex; align-items: center; justify-content: center;
        }
        .btn-choice .coche .i { width: 14px; height: 14px; stroke-width: 2.6; color: #191100; opacity: 0; }
        .btn-choice.selected { background: rgba(251,191,36,.12); border-color: var(--ambre); }
        .btn-choice.selected .tuile { background: var(--ambre); color: #191100; }
        .btn-choice.selected .coche { background: var(--ambre); border-color: var(--ambre); }
        .btn-choice.selected .coche .i { opacity: 1; }
        
        /* Le rang d'un choix multiple ordonne (etape 1). Le chiffre EST
           l'information : +5 au premier, +2 aux suivants. Sur fond or il passe
           en noir, sinon il serait illisible. */
        .choix[data-rang] { position: relative; }
        /* Le rang PREND LA PLACE de la tuile (23/09) : même carré, rempli d'ambre — le
           geste de l'onboarding, où la tuile d'un choix pris se remplit aussi. */
        .choix[data-rang]::before {
            content: attr(data-rang); position: absolute; left: 0; top: 50%;
            transform: translateY(-50%); width: 34px; height: 34px; border-radius: 10px;
            background: var(--ambre); color: #191100; font-size: 14px; font-weight: 800;
            line-height: 34px; text-align: center;
        }
        .choix[data-rang] .tuile { visibility: hidden; }
        /* Le rang d'un choix ORDONNÉ (étape 1) prend la place de la coche : même rond,
           même ambre — le chiffre EST l'information (+5 au premier, +2 aux suivants).
           ⚠️ Sur le BOUTON, pas sur la coche : `attr()` lit l'élément qui porte le
           pseudo-élément, et c'est le bouton qui porte `data-rang`. */
        .btn-choice[data-rang] { position: relative; }
        .btn-choice[data-rang]::after {
            content: attr(data-rang); position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
            width: 24px; height: 24px; border-radius: 50%; background: var(--ambre); color: #191100;
            font-family: 'Inter', sans-serif; font-size: 12px; font-weight: 800; line-height: 24px; text-align: center;
        }
        /* ⭐⭐ LE BOUTON D'UNE ÉTAPE EST TOUJOURS AU MÊME ENDROIT (23/09) : en bas de
           l'écran, aux cinq étapes. Il était ANCRÉ à l'étape 1 seule (mesure du
           10/09 : à 320×568 son contenu défile de 145 px, et « Suivant » passait
           sous la ligne de flottaison) et suivait le contenu ailleurs — il sautait
           de 355 à 273, 268 puis 746 px d'une question à l'autre, mesuré à 375×812.
           Et l'ancre de l'étape 1 laissait défiler les cartes SOUS lui, dans les
           60 px de réserve de `.scroll` : lisibles, et intouchables.
           · L'onboarding n'a pas de barre d'onglets : l'écran rend ses 74 px, et
             `.scroll` sa réserve.
           · L'étape visible est une colonne qui remplit l'écran ; un espaceur
             (`::after`, rangé AVANT le bouton par `order`) prend le reste — au
             moins 18 px, pour que le dégradé ne voile pas la dernière carte.
           · `::after` du bouton : la bande sous lui, peinte du fond de l'écran —
             sans elle, ce qui défile s'y lirait.
           Le dégradé évite que le contenu semble passer AU TRAVERS du bouton. */
        #scr-ob { padding-bottom: 0; --ob-bas: calc(16px + env(safe-area-inset-bottom)); }
        #scr-ob .scroll { display: flex; flex-direction: column; padding-bottom: 0; scroll-padding-bottom: calc(96px + env(safe-area-inset-bottom)); }
        #scr-ob .ob-step:not(.hidden) { display: flex; flex-direction: column; flex: 1 0 auto; }
        #scr-ob .ob-step:not(.hidden)::after { content: ""; flex: 1 0 18px; order: 1; }
        .btn-next-step.ancre {
            position: sticky; bottom: var(--ob-bas, 0px); order: 2; flex: none;
            margin-top: 0; margin-bottom: var(--ob-bas, 0px);
            box-shadow: 0 -16px 20px -6px var(--n0), 0 -32px 20px -18px var(--n0);
        }
        .btn-next-step.ancre::after {
            content: ""; position: absolute; left: -24px; right: -24px; top: calc(100% + 1px);
            height: var(--ob-bas, 0px); background: var(--n0); pointer-events: none;
        }
        /* « Suivant » est un `.bouton.plein.large` (23/09) : 63 px, un arrondi de 20 et
           le jaune #FFD700 avant — le seul bouton de l'app dans ce dessin. Il ne garde
           ici que son apparition : invisible tant qu'on n'a rien choisi. */
        .btn-next-step { margin-top: 12px; opacity: 0; pointer-events: none; transition: opacity .3s; }
        .btn-next-step.visible { opacity: 1; pointer-events: auto; }

        /* ⚠️ `overflow-x: hidden` est INDISPENSABLE, pas decoratif : des qu'un
           axe n'est plus `visible`, l'autre passe a `auto` tout seul. Les
           rangees de pastilles debordent volontairement de 25 px de chaque
           cote (cf. `.rangee`), et tout l'ecran devenait donc defilable
           lateralement de 66 px — mesure a 320. Le debordement est voulu ;
           le defilement de l'ecran, non. */
        /* ⭐ 24 px de gouttière (23/09) : les écrans étaient à 25, les feuilles, la page
           d'une balade et le mur à 24 — un pixel de décalage entre un écran et la
           feuille qui s'ouvre dessus, sur toute la hauteur. */
        .scroll { overflow-y: auto; overflow-x: hidden; flex: 1; scrollbar-width: none; padding: 24px 24px calc(60px + var(--suivi-h, 0px)) 24px; padding-top: calc(20px + env(safe-area-inset-top)); }
        
        /* ⭐ L'ONBOARDING (23/09) : la rangée du haut (Retour à gauche dès l'étape 2, la
           progression), le surtitre « Étape 2 sur 5 · Le portefeuille » — le format de
           Raconter et de la création —, la question à 26 px comme un titre d'écran.
           Il n'y avait NI Retour à l'écran NI progression : le seul chemin vers
           l'étape précédente était le Retour d'Android. */
        #scr-ob .scroll { padding-top: calc(20px + env(safe-area-inset-top)); }
        .ob-step { margin-top: 0; }
        /* `align-self` : l'étape est une COLONNE flex depuis le 23/09 (le bouton en bas),
           et un enfant de colonne s'étire — la pastille du clair couvrait la largeur. */
        .ob-sur { margin: 0 0 8px; align-self: flex-start; }
        .ob-titre { font-size: 26px; line-height: 1.05; margin: 0; }
        .ob-aide { font-size: 14px; color: var(--fumee); line-height: 1.45; margin: 8px 0 20px; }
        .scroll::-webkit-scrollbar { display: none; }
        
        /* ⭐ 56 PX, PLUS 74 (30/09, demande d'Hakim : « un peu haute », la barre de Snacky en modele) : le contenu
           — trace de 22, libelle — fait 42 px ; les 74 laissaient 15 px de vide dessus et dessous, 7 desormais.
           Chaque onglet reste une cible de 56 px de haut, au-dessus du plancher de 44. */
        .tab-bar { position: absolute; bottom: 0; width: 100%; height: calc(var(--tab-h) + env(safe-area-inset-bottom)); background: var(--tab-fond); border-top: 1px solid rgba(255,255,255,0.05); display: flex; justify-content: space-around; align-items: center; z-index: 100; padding-bottom: env(safe-area-inset-bottom); }
        /* ⚠️ Les onglets faisaient 24 a 52 px de large : « LIST » offrait une
           cible de 24 px sur une barre de 375. Le doigt vise l'ETIQUETTE, pas
           le mot. Chaque onglet prend desormais sa cinquieme de barre et toute
           sa hauteur — meme dessin, cible quatre fois plus grande. */
        .tab-item {
            flex: 1; min-width: 0; height: 100%;
            display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
            color: var(--tab-texte); font-size: 11px; font-weight: 800; letter-spacing: .02em;
            text-align: center; cursor: pointer; transition: color .2s;
            -webkit-tap-highlight-color: transparent;
        }
        .tab-item .e { font-size: 19px; line-height: 1; }
        .tab-item.active { color: var(--jaune); }   /* la barre est noire dans les deux themes : l'or de REMPLISSAGE */

        /* ══ LE BAS DE LA LISTE ════════════════════════════════════════════
           La barre d'onglets est opaque et se pose sur le contenu : la
           derniere carte se terminait par une COUPE NETTE, comme si l'ecran
           s'arretait la. Un degrade au-dessus dit que ca continue en dessous.
           `pointer-events: none` : il ne doit rien intercepter. 20 px depuis le
           30/09 : a 34, il faisait monter la barre d'autant sur le contenu. */
        .tab-bar::before {
            content: ''; position: absolute; left: 0; right: 0; bottom: 100%;
            height: 20px; pointer-events: none;
            background: linear-gradient(transparent, var(--tab-fondu));
        }
        /* Le gris des onglets inactifs etait #444 : 2,6 de contraste sur le noir
           de la barre, sous le seuil. Il se lit maintenant sans crier. */

        /* ══ LA CIBLE MINIMALE ═════════════════════════════════════════════
           Coeur, etoile, carte : trois glyphes de 16 a 24 px poses cote a cote,
           donc trois cibles trop petites ET trop proches l'une de l'autre. Le
           carre de 44 px est le minimum tenable au pouce ; il reste invisible,
           c'est le glyphe qu'on voit. */
        .cible {
            width: 40px; height: 40px; padding: 0; border: 0; background: none;
            display: inline-flex; align-items: center; justify-content: center;
            font-size: 22px; line-height: 1; cursor: pointer; border-radius: 13px;
            -webkit-tap-highlight-color: transparent; transition: background .15s, opacity .15s;
            position: relative;
        }
        .cible:active { background: rgba(255,255,255,.08); }
        /* ⭐ 44 PX SOUS LE DOIGT, sans toucher au dessin (19/09, contrat
           retouches-mecaniques n° 03) : la meme rallonge que `.pilule.compacte::before`
           — 2 px tout autour, invisible et hors du flux. Le rail et sa marge
           negative sont un alignement VOULU (09/09), on ne grossit pas la boite.
           ⚠️ `position: relative` ci-dessus est ce qui ancre la zone au bouton :
           sans lui, elle se poserait par rapport a la carte entiere. */
        .cible::before { content: ''; position: absolute; inset: -2px; }
        /* ⚠️ Le coeur etait un emoji PLEIN et l'etoile un glyphe de TEXTE, fin :
           deux actions soeurs, deux poids visuels sans rapport, et l'etat
           « non choisi » de l'une ressemblait a l'etat « choisi » de l'autre.
           Meme glyphe pour les deux etats, l'opacite dit lequel. */

        /* ══ LE RAIL D'ACTIONS D'UNE CARTE ═════════════════════════════════
           ⚠️ C'etaient trois EMOJIS : ❤️/🤍, ⭐/☆, 🗺️. Trois problemes en un.
           · Ils n'ont pas le meme poids : le coeur est un aplat plein, l'etoile
             creuse un glyphe de texte fin, la carte une vignette multicolore —
             l'action la moins utile etait donc la plus voyante.
           · Sur un fond noir, un emoji « eteint » a l'opacite devient une tache
             grise sale : illisible sans avoir l'air desactive pour autant.
           · Leur couleur n'obeit a rien : `color` ne les atteint pas.
           Trois traces monochromes reglent les trois : meme graisse, meme
           taille, et l'etat se dit par la COULEUR, qui est enfin la notre. */
        .cible svg { width: 21px; height: 21px; display: block; }
        .rail-actions .cible { color: var(--fumee2); }
        .rail-actions .cible[aria-pressed="true"] { color: var(--or); }
        .rail-actions .cible.coeur[aria-pressed="true"] { color: var(--rouge); }
        .rail-actions .cible:hover { color: var(--craie); }
        .tronque { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

        /* ══ UN RANG QUI DEFILE SUR LE COTE ════════════════════════════════
           Les rangees de pastilles vivent dans `.scroll`, qui a 25 px de
           marge : la derniere pastille visible etait donc TRANCHEE net a
           25 px du bord, avec du vide derriere elle. Rien ne disait qu'il y
           avait une suite — on lisait un defaut d'affichage, pas un rang.

           Deux corrections, et elles vont ensemble :
           · le rang DEBORDE jusqu'aux bords de l'ecran (marge negative
             compensee par un padding egal), donc les pastilles sortent du
             cadre au lieu d'y buter ;
           · un degrade sur le bord droit dit qu'il en reste. */
        .rangee {
            display: flex; flex-wrap: nowrap; gap: 8px;
            overflow-x: auto; overflow-y: hidden;
            scrollbar-width: none; -ms-overflow-style: none;
            -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain;
            -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 38px), transparent 100%);
                    mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 38px), transparent 100%);
        }
        .rangee::-webkit-scrollbar { display: none; }
        /* ⚠️ `hidden` ne cache rien sans sa règle (23/09) : les filtres de BALADES partent d'un onglet sans balade. */
        .rangee[hidden] { display: none; }
        .rangee > * { flex: 0 0 auto; }
        /* Le debordement ne vaut que dans `.scroll`, dont on connait la marge.
           Dans une fenetre, le rang reste dans sa boite. */
        .scroll > .rangee, .scroll > * > .rangee {
            margin-left: -24px; margin-right: -24px;
            padding-left: 24px; padding-right: 24px;
        }
        /* ⚠️ Arrive au bout, le degrade ferait passer la DERNIERE pastille pour
           une pastille coupee — exactement ce qu'on vient de corriger. Il
           s'efface, et `majFondus()` s'en charge. */
        .rangee.fin { -webkit-mask-image: none; mask-image: none; }

        /* CARTE */
        .map-bg { 
            background: #1a1a1a; 
            flex: 1; 
            position: absolute;
            inset: 0;
            width: 100%; 
            height: 100%; 
            top: 0;
            left: 0;
        }
        
        /* LEAFLET OVERRIDES */
        #map-root { 
            width: 100%; 
            height: 100%; 
            position: absolute; 
            inset: 0;
            z-index: 1;
        }
        
        /* Leaflet controls */
        .leaflet-control { z-index: 100 !important; }
        .leaflet-top, .leaflet-bottom { z-index: 100 !important; }
        .poi { position: absolute; width: 18px; height: 18px; background: var(--jaune); border: 3px solid #000; border-radius: 50%; transform: translate(-50%, -50%); cursor: pointer; transition: all 0.2s; z-index: 10; }
        .poi:hover { transform: translate(-50%, -50%) scale(1.3); }
        .poi-label { position: absolute; font-size: 11px; font-weight: 900; background: black; color: white; padding: 2px 6px; border-radius: 4px; top: 20px; left: 50%; transform: translateX(-50%); white-space: nowrap; pointer-events: none; }
        
        /* MA POSITION */
        .my-position { position: absolute; width: 24px; height: 24px; background: #3b82f6; border: 4px solid white; border-radius: 50%; transform: translate(-50%, -50%); z-index: 15; box-shadow: 0 0 15px rgba(59, 130, 246, 0.6); animation: pulse 2s infinite; }
        @keyframes pulse { 0%, 100% { box-shadow: 0 0 15px rgba(59, 130, 246, 0.6); } 50% { box-shadow: 0 0 25px rgba(59, 130, 246, 0.8); } }
        
        .my-position::after { content: ''; position: absolute; inset: 6px; background: white; border-radius: 50%; }
        
        .my-pos-label { position: absolute; font-size: 11px; font-weight: 900; background: #3b82f6; color: white; padding: 2px 6px; border-radius: 4px; top: -15px; left: 50%; transform: translateX(-50%); white-space: nowrap; pointer-events: none; }

        /* ⭐ LE TOAST (23/09) : le noir de la barre d'onglets, pleine largeur, une icône
           qui dit le genre du message. Il était un pavé JAUNE centré, étroit — « Lien
           copié — colle-le où tu veux » tenait sur TROIS lignes — et sur le clair, posé
           sur le bandeau jaune de l'accueil, il DISPARAISSAIT. `showToast` lit l'emoji
           de tête du message et le remplace par son tracé (`TOAST_ICONES`, lib/aides.js). */
        .toast {
            position: fixed; z-index: 500; top: calc(10px + env(safe-area-inset-top)); left: 12px; right: 12px;
            max-width: 480px; margin: 0 auto; box-sizing: border-box;
            display: flex; align-items: center; gap: 11px; min-height: 50px; padding: 12px 16px;
            background: var(--toast-fond); color: #fff; border: 1px solid rgba(255,255,255,.1); border-radius: 16px;
            box-shadow: 0 14px 36px rgba(0,0,0,.45);
            font-size: 14px; font-weight: 700; line-height: 1.35; text-align: left; pointer-events: none;
            animation: toastEntre .3s cubic-bezier(.2,.9,.3,1);
        }
        .toast .i { width: 20px; height: 20px; flex: 0 0 20px; color: var(--jaune); }
        .toast.ok .i { color: #4ADE80; }
        .toast.alerte .i { color: #FDA85A; }
        @keyframes toastEntre { from { opacity: 0; transform: translateY(-12px); } to { opacity: 1; transform: none; } }
        @media (prefers-reduced-motion: reduce) { .toast { animation: none; } }

        .modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 110; }
        
        
        /* CATEGORY BUTTONS */
        /* `.category-btn` vivait ici (39 px, #FFD700 en dur) : c'est `.pilule`
           depuis le 19/09 — voir « LA FAMILLE DES PILULES ». */
        
        /* SCROLLBAR INVISIBLE */
        .scrollbar-hide {
            -ms-overflow-style: none;  /* IE and Edge */
            scrollbar-width: none;  /* Firefox */
        }
        .scrollbar-hide::-webkit-scrollbar {
            display: none;  /* Chrome, Safari and Opera */
        }
        
        /* Les pastilles de la carte ne portent que `.pilule compacte carte` (19/09) :
           avant, la cascade `.map-category-btn` (fond translucide, backdrop-filter)
           battait `.carte-chip` sur la premiere rangee, d'ou deux rendus « actif ». */

        /* NAVIGATION ACTIVE */
        .nav-panel { position: absolute; bottom: 20px; left: 20px; right: 20px; background: rgba(255,215,0, 0.95); color: black; padding: 16px; border-radius: 20px; z-index: 50; box-shadow: 0 10px 40px rgba(0,0,0,0.5); }
        .nav-panel h3 { margin: 0 0 8px 0; font-weight: 900; font-size: 16px; font-family: 'Montserrat'; }
        .nav-panel p { margin: 0 0 12px 0; font-size: 14px; opacity: 0.8; }
        .nav-panel .progress { background: rgba(0,0,0,0.2); height: 4px; border-radius: 2px; margin: 8px 0; overflow: hidden; }
        .nav-panel .progress-bar { background: black; height: 100%; transition: width 0.3s; }
        .nav-panel .meta { display: flex; gap: 12px; font-size: 13px; font-weight: 700; margin: 8px 0; }
        .nav-panel .actions { display: flex; gap: 8px; margin-top: 12px; }
        .nav-panel button { flex: 1; padding: 10px; border: none; border-radius: 12px; font-weight: 800; cursor: pointer; font-size: 13px; transition: all 0.2s; text-transform: uppercase; }
        .nav-panel .btn-next { background: black; color: var(--accent); }
        .nav-panel .btn-next:hover { transform: scale(1.05); }
        .nav-panel .btn-stop { background: rgba(0,0,0,0.2); color: black; }
        
        /* POI HIGHLIGHT */
        .poi.active { width: 28px; height: 28px; background: #ef4444; box-shadow: 0 0 20px rgba(239, 68, 68, 0.8); animation: pulse-red 1s infinite; }
        @keyframes pulse-red { 0%, 100% { box-shadow: 0 0 20px rgba(239, 68, 68, 0.8); } 50% { box-shadow: 0 0 30px rgba(239, 68, 68, 1); } }
        .poi.inactive { opacity: 0.3; cursor: default !important; }
        /* BALADES CREATOR STYLES */
    .creator-step {
        display: none;
        animation: fadeIn 0.3s ease;
    }
    .creator-step.active {
        display: block;
    }
    
    /* `.balade-theme-btn` et `.step-filter-btn` vivaient ici (38 et 32 px, coins carres) :
       c'est `.pilule` depuis le 19/09 — voir « LA FAMILLE DES PILULES ». */
    
    .place-select-item {
        background: var(--n2);
        border: 1px solid rgba(255,255,255,0.06);
        border-radius: 12px;
        padding: 12px;
        cursor: pointer;
        transition: all 0.2s;
    }
    .place-select-item:hover {
        background: var(--n1);
        border-color: rgba(255,215,0,0.3);
    }
    .place-select-item.selected {
        background: rgba(255,215,0,0.1);
        border-color: #FFD700;
    }
    
    .selected-step-item {
        background: rgba(255,215,0,0.1);
        border: 1px solid rgba(255,215,0,0.3);
        border-radius: 12px;
        padding: 10px;
        animation: slideIn 0.3s ease;
    }
    
    @keyframes slideIn {
        from { opacity: 0; transform: translateY(10px); }
        to { opacity: 1; transform: translateY(0); }
    }
    
    .apercu-carte { border-radius: var(--r-ctl); border: 1px solid var(--trait-fort); overflow: hidden; }
    .semaine.recap li span:last-child { color: var(--craie); font-weight: 700; }
    #balade-preview-map {
        position: relative !important;
        background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%) !important;
        background-image: 
            repeating-linear-gradient(0deg, transparent, transparent 35px, rgba(255,255,255,0.03) 35px, rgba(255,255,255,0.03) 36px),
            repeating-linear-gradient(90deg, transparent, transparent 35px, rgba(255,255,255,0.03) 35px, rgba(255,255,255,0.03) 36px) !important;
    }

    #balade-preview-map {
        position: relative !important;
        background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%) !important;
        background-image: 
            repeating-linear-gradient(0deg, transparent, transparent 35px, rgba(255,255,255,0.03) 35px, rgba(255,255,255,0.03) 36px),
            repeating-linear-gradient(90deg, transparent, transparent 35px, rgba(255,255,255,0.03) 35px, rgba(255,255,255,0.03) 36px) !important;
    }
    
    
    
    
    
    
    
    /* `.balade-tab-btn` vivait ici : c'est `.segment .pilule` depuis le 19/09. */

    /* Les pastilles etaient du blanc a 5 % : des fantomes. Elles sont
       desormais pleines, avec un trait, et l'active passe en or franc. */
    /* `.balade-filter-btn` vivait ici (33,5 px) : c'est `.pilule` depuis le 19/09. */

    
    /* AMÉLIORATION POPUPS LEAFLET - MEILLEUR CONTRASTE */
    /* Le popup est une fenetre posee sur la carte : il suit le meme plan que
       les autres (--n3 + lisere or), au lieu du noir d'avant la refonte. */
    .leaflet-popup-content-wrapper {
        background: var(--n3) !important;
        color: white !important;
        border-radius: 16px !important;
        box-shadow: 0 10px 40px rgba(0, 0, 0, 0.8) !important;
        border: 1px solid rgba(255,255,255,.14) !important;
        border-top: 3px solid var(--ambre) !important;
        padding: 0 !important;
    }
    
    .leaflet-popup-content {
        margin: 0 !important;
        font-family: 'Inter', sans-serif !important;
    }
    
    .leaflet-popup-tip {
        background: var(--n3) !important;
        border: 2px solid rgba(255, 215, 0, 0.3) !important;
        border-top: none !important;
        border-right: none !important;
    }
    
    .leaflet-popup-close-button {
        color: #FFD700 !important;
        font-size: 24px !important;
        font-weight: bold !important;
        padding: 8px !important;
    }
    
    .leaflet-popup-close-button:hover {
        color: white !important;
    }
    
    /* Style du contenu de la popup */
    .leaflet-popup-content h3 {
        color: #FFD700 !important;
    }
    
    .leaflet-popup-content p {
        color: #e5e5e5 !important;
    }
    
    .leaflet-popup-content .text-gray-500 {
        color: #999 !important;
    }
    
    .leaflet-popup-content .text-yellow-400 {
        color: #fbbf24 !important;
    }

    
    /* La fiche est une feuille (08/09) : ses styles sont plus haut, avec
       les autres feuilles. Ne jamais remettre un `color: … !important` sur
       ses descendants : c'est ce qui aplatissait toute sa hierarchie. */

    

/* ⭐ LES « J'AIME » PUBLICS (28/09) — le cœur du rail d'une carte de LIEUX porte son compteur dessous (palier micro,
   11 px). La cible garde ses 44 px, elle s'allonge seulement du nombre. Aimé : le cœur plein, en --rouge. */
.rail-actions .cible.coeur-c { flex-direction: column; height: auto; min-height: 44px; gap: 1px; }
.rail-actions .cible.coeur-c[aria-pressed="true"] { color: var(--rouge); }
.rail-actions .cible.coeur-c .n-jaime { font-size: 11px; font-weight: 800; line-height: 1; color: var(--fumee); }

/* ⭐ L'AGENDA (28/09, docs/mockups/agenda.html) — le bloc de l'accueil, la page « Tout l'agenda », la feuille d'un
   événement. Les valeurs du contrat ; les couleurs par les jetons (les deux thèmes). */
.ag-tete { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.ag-tete .n-kick { margin-bottom: 8px; }
.ag-tout { display: inline-flex; align-items: center; gap: 2px; min-height: 44px; color: var(--accent); font-weight: 800; font-size: 14px; white-space: nowrap; }
.ag-tout .i { width: 17px; height: 17px; }
.ag-carte { padding: 2px 18px; }
.ag-ligne { display: flex; gap: 14px; align-items: flex-start; width: 100%; text-align: left; padding: 13px 0; min-height: 64px;
    border-top: 1px solid var(--trait); color: inherit; }
.ag-ligne:first-child { border-top: 0; }
.ag-h { flex: 0 0 48px; font-family: var(--display); font-weight: 900; font-size: 15px; line-height: 20px; color: var(--accent); letter-spacing: -.3px; }
.ag-h small { display: block; font-family: var(--corps); font-weight: 800; font-size: 11px; line-height: 14px; letter-spacing: .06em; color: var(--fumee); }
.ag-h.long { font-family: var(--corps); font-weight: 800; font-size: 11px; line-height: 15px; letter-spacing: .06em; text-transform: uppercase;
    color: var(--fumee); padding-top: 3px; }
.ag-t { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.ag-titre { font-weight: 800; font-size: 15px; line-height: 20px; }
.ag-lieu { font-size: 13px; line-height: 18px; color: var(--fumee); margin-top: 3px; display: flex; flex-wrap: wrap; gap: 0 8px; }
.ag-lieu .d { display: inline-flex; align-items: center; gap: 3px; white-space: nowrap; }
.ag-lieu .d .i { width: 14px; height: 14px; }
.ag-chev { width: 18px; height: 18px; flex: 0 0 18px; color: var(--fumee); margin-top: 1px; }
.ag-page { padding: 0 24px 40px; }
.ag-jour { margin-top: 22px; }
.ag-photo { margin: 6px -20px 10px; }
.ag-photo[hidden] { display: none; }
.ag-phrase { font-size: 15px; line-height: 22px; margin: 4px 0 6px; }
.ag-rang { display: flex; align-items: center; gap: 12px; width: 100%; min-height: 56px; border-top: 1px solid var(--trait); padding: 8px 0;
    text-align: left; color: inherit; }
.ag-ico { width: 36px; height: 36px; flex: 0 0 36px; border-radius: 12px; background: var(--n2); display: flex; align-items: center; justify-content: center; }
.ag-ico .i { width: 19px; height: 19px; }
.ag-rt { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.ag-rt b { font-size: 15px; line-height: 19px; }
.ag-rt span { font-size: 13px; line-height: 18px; color: var(--fumee); }
.ag-geste { color: var(--or); font-weight: 800; font-size: 14px; white-space: nowrap; }
#e-aller[hidden] { display: none; }

