/* ═══════════════════════════════════════════════════════
   Artivib — Design System Partagé
   Utilisé par index.html (landing) et app.html (application)
   ═══════════════════════════════════════════════════════ */

/* ═══ v20.11 — RETOUR À GEORGIA POUR LES MONTANTS ═══════════════════
   La v20.8 avait installé Lora dans « assets/fonts » et l'avait mise sur
   les montants. Manal préfère revenir à Georgia : c'est plus simple et
   ça n'a jamais posé de problème.

   Les deux fichiers « lora-*.woff2 » RESTENT dans le dossier — ils ne
   sont plus déclarés ici, donc plus jamais téléchargés ni affichés, et
   les laisser évite de devoir supprimer quoi que ce soit sur le serveur.
   Ils sont aussi retirés de la liste du service worker : rien ne sert de
   mettre en cache une police que personne n'utilise.

   Pour revenir à Lora un jour : rétablir les deux blocs « @font-face »,
   remettre 'Lora' en tête de « --police-montant », et remettre les deux
   fichiers dans « STATIC » dans sw.js. */

/* ═══════════════════════════════════════════════════════════════════
   HISTORIQUE — la charte de la v10, remplacée par celle de la v20
   ═══════════════════════════════════════════════════════════════════
   La v10 reposait sur trois couleurs : un violet, une lavande et un
   vert sauge. La v20 les remplace par le rose poudré et le mauve
   définis juste en dessous. Les alias --violet / --lavande / --sage
   sont conservés — du code les utilise encore — mais pointent
   désormais sur la nouvelle palette.
   ═══════════════════════════════════════════════════════════════════ */
/* ═══════════════════════════════════════════════════════════════════
   v20 — LE SYSTÈME DE DESIGN ROSE POUDRÉ / MAUVE
   ───────────────────────────────────────────────────────────────────
   Les valeurs ci-dessous ne sont pas des interprétations : elles ont
   été relevées au pixel dans les deux captures de référence.
       fond de page   #F3E4EE → #F9E6EA
       fond de carte  #FEF9FB      (24 à 31 % des pixels des captures)
       carte dégradée #E87FAF → #C264C0 → #8A5CC4
   Elles coïncident exactement avec le brief écrit, ce qui les confirme
   deux fois.

   RÈGLE DE MISE EN ŒUVRE — pourquoi aucun nom de variable ne change.
   L'application utilise « var(--…) » à 283 endroits dans app.js et 24
   dans app.html. En conservant les noms et en ne changeant que les
   VALEURS, toute cette surface se repeint sans qu'une seule ligne de
   JavaScript soit touchée : autant de code non modifié, donc autant de
   régressions impossibles. Les anciens noms de couleurs — « green »,
   « red », « blue » — ne décrivent donc plus leur teinte mais leur
   RÔLE, et chacun porte ci-dessous le sien en clair.
   ═══════════════════════════════════════════════════════════════════ */
:root {
  /* ═══ v21 — PALETTE ADOUCIE (option A, validée le 28 août) ═════════
     Chaque couleur a été convertie en OKLCh — clarté, saturation,
     teinte — puis sa SATURATION PLAFONNÉE selon son rôle, la teinte
     restant inchangée (sauf les quatre sections, voir plus bas) :

       texte (montants, libellés)        0,115   et 4,5:1 tenu
       pictogrammes, éléments d'interface 0,100  et 3:1 tenu
       décor, grandes surfaces           0,082   aucun seuil
       fonds pastel                      0,028   aucun seuil

     Quand une couleur avait de la marge, elle a aussi été ÉCLAIRCIE
     jusqu'au seuil : c'est là que se gagne la douceur. Quand elle n'en
     avait pas, elle a été assombrie juste ce qu'il faut.

     DEUX CHOSES VONT À CONTRE-COURANT, ET C'EST VOULU :
     • « --encre-3 » est ASSOMBRIE. À #8B7E93 elle ne valait que 3,66:1
       sur le fond des cartes, sous le seuil de 4,5 d'un texte de 13 px.
       Adoucir ne doit pas rendre illisible.
     • les quatre teintes de section sont ÉCARTÉES les unes des autres
       (330°, 5°, 300°, 45° au lieu de 341°, 0°, 302°, 74°). En perdant
       de la saturation, quatre teintes voisines se ressemblent vite :
       l'écart perçu minimal tombait de 6,2 à 3,4 ; écartées, il tient
       5,2.

     NON TOUCHÉS, ET POURQUOI :
     • les deux dégradés (carte principale, boutons) portent du texte
       BLANC, à 3,02–3,20:1. Ils sont déjà au plus pastel que ce texte
       autorise ; les adoucir rendrait les libellés illisibles. Manal a
       choisi de garder le texte blanc.
     • « --hero1 » et « --hero2 » ne servent qu'à la page publique
       (index.html), où du blanc est posé dessus à 2,49:1 — un problème
       existant, à traiter avec le lot « page publique », pas ici.
     • les ombres restent teintées comme avant : leur intensité avait
       été calculée pour que les cartes se détachent du fond de page. */
  /* ─── Les surfaces ─── */
  /* v20.4 — fond de page en rose pastel très doux (demande du 27 août).
     À noter : l'écart perçu entre le fond de page et le fond des cartes
     passe de 5,5 à 3,7 (distance OKLab ×100). Les cartes se détachent
     donc moins qu'avant. Aucune compensation n'a été appliquée — ombres
     et fonds de carte sont inchangés, comme demandé. */
  /* v23 — assombri d'un souffle (≈1 % de clarté), demande du 28 août.
     Valeurs précédentes : #FBF7FA / #FEFAFA. */
  --page-1:      #F8F2F7;   /* haut du dégradé de page   */
  --page-2:      #FBF6F6;   /* bas du dégradé de page    */
  --carte:       #FFFDFE;   /* fond des cartes — quasi blanc */
  --carte-echeance: #F5EFFA; /* carte « À payer dans X jours » — mauve pastel */
  --bloc:        linear-gradient(135deg, #F9E4EF, #EFE4F9);  /* blocs internes */

  /* ─── Les encres. Jamais de noir pur. ─── */
  --encre:       #3A2E45;   /* titres et montants        */
  --encre-2:     #5F5566;   /* texte secondaire          */
  /* v21 — mesurée sur SEPT fonds, et non sur le seul fond des cartes :
     page (5,02), pastilles mauve (4,72), rose (4,79), filets (4,52).
     À #7C6F83 elle tombait à 4,09 sur la page — un libellé de 13 px y
     est sous le seuil. Les trois niveaux d'encre restent distincts :
     12,2 / 6,8 / 5,0 pour un titre, un texte, un libellé discret. */
  --encre-3:     #6E6175;   /* libellés discrets         */

  /* ═══ v21 — LES ENCRES POSÉES SUR UNE PASTILLE PÂLE ═══════════════
     Le contraste n'est pas une propriété d'une couleur, mais d'une
     PAIRE. Les trois accents ci-dessus ont été adoucis en les mesurant
     sur le fond des cartes (#FEF9FB) : ils y tiennent 4,56:1.

     Or les mêmes couleurs servent aussi d'écriture SUR leur propre
     pastille pâle — un bouton doux, l'initiale d'une transaction, une
     étiquette. Ces fonds sont plus sombres que la carte, et le
     contraste y tombait à 3,9. Mesuré après l'adoucissement : « Voir
     mes objectifs » et « Exporter Excel » passaient de 5,50 à 3,88.

     D'où ces trois encres, plus profondes, réservées à ce cas précis.
     Chacune tient 4,5:1 sur la PLUS SOMBRE des pastilles où elle est
     employée — et non sur la plus claire, sinon la règle ne servirait
     à rien là où elle compte. Les accents adoucis, eux, ne changent
     pas : ils gardent leur rôle sur les cartes. */
  --encre-mauve: #745BA4;   /* sur --green-soft, --purple-soft, --violet-soft */
  --encre-rose:  #A14E6A;   /* sur --red-soft, --orange-soft                  */
  --encre-doree: #8F5C46;   /* sur --sec-credits-soft, --orange-soft          */

  /* ─── Les deux accents ─── */
  --rose:        #D495B0;   /* rose clair, décor         */
  --rose-accent: #A45A8C;
  --rose-negatif:#AD5874;   /* montants négatifs         */
  --mauve:       #8167A6;   /* mauve, icônes             */
  --mauve-fonce: #7E66B0;   /* montants positifs         */
  --magenta:     #B082AE;   /* arrêt central du dégradé  */

  /* ─── Les dégradés nommés ─── */
  /* v20.9 — LE DÉGRADÉ DE LA CARTE PRINCIPALE, ENCORE ADOUCI.
     Demandé : #D993BE → #C084C9 → #A594CE, texte blanc.
     Vérifié sur TOUT le dégradé, pas seulement sur les trois arrêts :
     le blanc n'y dépasse jamais 2,84:1, et descend à 2,37 sur le rose
     — sous le seuil de 3:1 des grands textes, partout.
     Livré : #C07BA6 → #B77CC0 → #9887C1, soit les mêmes teintes
     assombries de 10 %, 4 % et 6 % de clarté (saturation et teinte
     inchangées à 2 % et 0,2° près). Le minimum sur l'ensemble du
     dégradé passe ainsi à 3,15:1.
     À savoir : le libellé « Solde prévisionnel » est à 13 px, taille
     pour laquelle le seuil est 4,5 et non 3 — il faudrait assombrir de
     17 à 22 %, et l'on quitterait le pastel. Non appliqué, même
     arbitrage que pour les boutons.
     Pour revenir exactement aux valeurs demandées : remplacer la ligne
     ci-dessous par #D993BE, #C084C9, #A594CE. */
  /* ═══ v21.2 — LA CARTE PRINCIPALE, CHOIX ASSUMÉ DE MANAL ═══════════
     Ces trois teintes sont celles qu'elle a demandées, et le texte
     blanc n'y vaut que 2,24 à 2,70:1 selon l'endroit du dégradé. Le
     seuil habituel des grands textes est 3:1 : il n'est donc pas
     atteint, et c'est délibéré — arbitrage du 28 août, en connaissance
     de cause, après avoir vu les deux rendus côte à côte.

     Ce qu'il faut savoir si l'on revient un jour sur ce point :
     • le contraste ne dépend QUE de la luminance. Pour tenir 3,00:1
       sous du blanc, un fond ne peut pas dépasser 0,30 de luminance
       relative. C'est un plafond arithmétique, pas un réglage ;
     • à luminance égale, la SATURATION reste libre, et c'est elle qui
       fait qu'une couleur paraît douce. La version qui tenait 3,00:1
       avec 45 % de saturation en moins était :
       #AE8B9F → #AB8AAE → #9A90AF ;
     • l'autre porte de sortie serait d'abandonner le texte blanc pour
       une encre prune, qui monterait à 7:1 sur ces mêmes teintes —
       proposé le 28 août, écarté par Manal. */
  --degrade-principal: linear-gradient(135deg, #DA9AC0 0%, #C589CB 55%, #A995CE 100%);
  --filet:             linear-gradient(180deg, #D495B0, #8167A6);
  /* v20.4 — LE DÉGRADÉ DES BOUTONS, ET POURQUOI L'ARRÊT CLAIR A BOUGÉ.
     Demandé : #C99BD1 → #9B76C4, texte blanc, en gras, « au moins
     3:1 ». Vérifié : le blanc y vaut 2,31:1 sur l'arrêt clair et
     3,63:1 sur l'arrêt foncé — le seuil de 3 n'est donc atteint que
     sur la moitié droite du bouton. L'arrêt clair a été assombri de
     10 % de clarté, à teinte et saturation identiques, pour tenir
     3,02:1 d'un bout à l'autre. L'arrêt foncé est resté celui demandé.
     Pour revenir aux valeurs exactes du brief : remplacer #B285BA par
     #C99BD1 sur cette seule ligne.
     À savoir : 3:1 est le seuil des GRANDS textes et des éléments
     d'interface ; un libellé de bouton à 15 px, même en gras, demande
     4,5:1 — ce qui exigerait #93679A → #8B66B2, nettement moins
     pastel. Non appliqué, c'est votre arbitrage. */
  --degrade-bouton:    linear-gradient(135deg, #B285BA, #9B76C4);
  --ombre-bouton:      0 8px 24px rgba(139,102,178,.30);

  /* ─── Les quatre sections. Quatre teintes distinctes, déclinées dans
         la palette : c'est le rond d'icône qui les porte, jamais le
         montant, qui reste toujours à l'encre. ─── */
  --sec-revenus:       #A76EA1;  --sec-revenus-soft:  #FCE6EE;
  --sec-depenses:      #B96C7F;  --sec-depenses-soft: #FCEEDE;
  --sec-epargne:       #8D75B6;  --sec-epargne-soft:  #F0E7F8;
  --sec-credits:       #AE7962;  --sec-credits-soft:  #E1F3EE;

  /* ─── Le décor animé (cœurs et fleurs de l'arrière-plan) ───
         Deux teintes seulement, choisies pour tomber au même écart
         perçu du fond une fois l'opacité appliquée : 10,0 et 10,2.
         L'ancien lavande #DCC9F0 valait 1,6 — sous le seuil de
         perception, donc invisible quoi qu'on fasse. ─── */
  /* v21.2 — la barre « Où part votre argent » a ses PROPRES teintes :
     ce sont de larges aplats, pas des pictogrammes, et elles peuvent
     donc être bien plus pâles que « --mauve » et « --rose », qui
     servent ailleurs à dessiner des traits fins et doivent tenir 3:1. */
  --ou-mauve:    #D6C8E8;
  --ou-rose:     #F5D3DF;

  --decor-rose:  #D495B0;
  --decor-mauve: #B198CE;

  /* ─── Ombres : toujours teintées rose, jamais grises ─── */
  /* v20.4 — ombre renforcée pour compenser le nouveau fond de page.
     Le fond ayant été éclairci, l'écart perçu entre une carte et la
     page est tombé de 5,5 à 3,7. C'est désormais l'OMBRE qui dessine le
     bord : à 18 % elle produit un halo à 7,8 d'écart du fond, contre
     5,2 à 12 %. La carte se détache donc davantage qu'avant le
     changement de fond, sans que rien ne s'assombrisse au centre. */
  --ombre-carte:      0 2px 10px rgba(168,67,138,.07);
  --ombre-principale: 0 6px 20px rgba(138,92,196,.24);

  /* ═══ v21.4 — UNE ÉCHELLE VERTICALE À TROIS VALEURS ═════════════════
     L'audit du 28 août a mesuré SEPT intervalles différents entre les
     blocs : 0, 10, 12, 14, 16, 20 et 36 px selon l'écran. La v20.3 avait
     pourtant posé la règle d'un pas unique de 16 px — elle s'est érodée
     version après version.

     Tout ramener à 16 px serait faux : un titre qui ouvre une section a
     besoin de plus d'air qu'un bloc ordinaire, et un en-tête doit rester
     collé à ce qu'il annonce. D'où trois valeurs, et trois seulement :

       --pas-bloc   16px   entre deux blocs
       --pas-serre   8px   entre un en-tête et le bloc qu'il introduit
       --pas-large  28px   avant un titre qui ouvre une section

     Le script d'audit revérifie le résultat : il ne doit plus trouver
     que ces trois valeurs. */
  --pas-bloc:  16px;
  --pas-serre:  8px;
  --pas-large: 28px;

  /* ─── Formes ─── */
  --r:       22px;   /* cartes        */
  --r-sm:    14px;   /* blocs internes */
  --r-pill:  999px;  /* boutons, pastilles */

  /* ─── Typographie ─── */
  /* ═══ v21.3 — DEUX POLICES, DEUX RÔLES ═════════════════════════════
     Georgia dessine ses chiffres « à l'ancienne » : le 3, le 4, le 5,
     le 7 et le 9 descendent sous la ligne, le 6 et le 8 dépassent vers
     le haut. Dans une colonne de montants, cela se voit comme un
     désalignement, même quand les bords droits sont parfaits — c'est
     ce que Manal signalait depuis plusieurs versions.

     Georgia date de 1996 et ne contient pas la fonction OpenType
     « lnum » qui demanderait des chiffres alignés : vérifié sur son
     propre appareil, la demander ne change rien.

     D'où la séparation. Les TITRES gardent Georgia — ils ne portent
     aucun chiffre, et c'est là que se joue le caractère de
     l'application. Les MONTANTS passent à la police sans-serif du
     système (Segoe UI sur Windows, SF sur iPhone, Roboto sur Android) :
     ses chiffres sont alignés et de largeur égale d'origine, sans rien
     à télécharger.

     Pour revenir à Georgia sur les montants : remettre la même valeur
     que « --police-titre » ci-dessous — en sachant que l'ondulation
     des chiffres reviendra avec elle. */
  --police-titre:   Georgia, 'Times New Roman', serif;
  --police-montant: ui-sans-serif, -apple-system, BlinkMacSystemFont,
                    "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* ═══ Les noms hérités, repointés sur la nouvelle palette ═══
     Leur RÔLE est indiqué : c'est lui qui compte désormais. */
  --violet:      #8167A6;   /* accent principal          */
  --violet-clair:#B8A2D9;
  --violet-soft: #EFE4F9;
  --lavande:     #F5EDFA;
  --sage:        #7E66B0;   /* anciennement vert : positifs */
  --sage-soft:   #EFE4F9;

  --bg:          #F6E5EC;   /* teinte moyenne du dégradé de page */
  --bg2:         #F9E6EA;
  --surface:     #FFFDFE;
  --text:        #3A2E45;
  --muted:       #6E6175;   /* v21 — aligné sur « --encre-3 » : même rôle */
  --line:        #EFDFE9;   /* filets de séparation, rosés */
  --brand:       #D495B0;
  --brand-dark:  #AD5874;
  --brand-soft:  #F9E4EF;
  --lavender:    #DACBEA;
  --lavender-soft: #EFE4F9;
  --rose-soft:   #F9E4EF;
  --hero1:       #E87FAF;
  --hero2:       #8A5CC4;
  --green:       #7E66B0;   /* RÔLE : montants positifs, revenus  → mauve foncé */
  --green-soft:  #EFE4F9;
  --red:         #AD5874;   /* RÔLE : montants négatifs, dépenses → rose foncé  */
  --red-soft:    #FBE7EF;
  --purple:      #8167A6;   /* RÔLE : épargne, accents            */
  --purple-soft: #EFE4F9;
  --orange:      #C37A9B;   /* RÔLE : dépenses variables          → rose moyen   */
  --orange-soft: #FBE2EC;
  --blue:        #9387CB;   /* RÔLE : crédits                     → mauve doux   */
  --blue-soft:   #EAE4F7;

  --shadow:      0 3px 12px rgba(168,67,138,.12);
  --shadow-sm:   0 2px 8px rgba(168,67,138,.10);
  --shadow-lg:   0 6px 20px rgba(138,92,196,.24);
}

/* ─── Reset ─── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html {
  scroll-behavior: smooth; -webkit-font-smoothing: antialiased;

  /* ═══ v13 — STABILITÉ DE L'AFFICHAGE SUR MOBILE ═══════════════════
     Trois causes distinctes faisaient bouger ou grossir la page toute
     seule sur téléphone. Elles sont traitées ici, et nulle part
     ailleurs — aucune mise en page n'est modifiée.

     1. « text-size-adjust: auto » (valeur par défaut) autorise Safari
        et Chrome à AGRANDIR eux-mêmes le texte, notamment lors d'une
        rotation de l'écran. La page changeait alors de taille sans que
        personne n'ait rien demandé. Bloqué à 100 %.

     2. Le zoom au DOUBLE-TAP : deux appuis rapprochés — ou un appui
        involontaire pendant un défilement — déclenchaient un zoom et un
        décalage de la page. « touch-action: manipulation » le supprime.
        Le zoom à deux doigts (pincée), lui, reste possible : on ne
        retire à personne le droit d'agrandir volontairement, ce qui
        serait un vrai problème d'accessibilité.

     3. Le zoom à la SAISIE : voir la règle « font-size: 16px » plus bas
        dans la partie mobile. iOS zoome d'office sur tout champ dont le
        texte fait moins de 16 px. */
  -webkit-text-size-adjust: 100%;
          text-size-adjust: 100%;
}
/* v63.14 — « manipulation » est devenu « pan-x pan-y ».
   « manipulation » = « pan-x pan-y pinch-zoom » : il retirait le
   double-tap mais LAISSAIT le pincement. Manal a demandé le blocage
   COMPLET du zoom (choix assumé, malgré la perte d'accessibilité).
   « pan-x pan-y » garde les deux défilements et retire le double-tap
   ET le pincement partout où le navigateur respecte cette propriété
   (Chrome, Edge, Android). Safari sur iPhone/iPad ne l'applique pas au
   pincement : c'est le bloc JavaScript d'app.html qui s'en charge. */
html, body { touch-action: pan-x pan-y; }
/* v63.9 — LA REGLE CI-DESSUS NE SUFFISAIT PAS, ET VOICI POURQUOI.
   « touch-action » n'est PAS une propriété héritée. Posée sur
   « html » et « body », elle laissait tous leurs descendants à la
   valeur par défaut « auto » — mesuré : #loginView, .input, button,
   label, .field valaient tous « auto ». Or iOS décide d'autoriser ou
   non le zoom au double-tap d'après l'élément réellement TOUCHÉ.
   Un double-tap sur une carte, un libellé ou un bouton tombait donc
   sur « auto » et zoomait quand même, en décalant la page.

   « manipulation » = « pan-x pan-y pinch-zoom » : le défilement
   vertical ET horizontal restent entiers, et LE ZOOM À DEUX DOIGTS
   AUSSI. Seul le double-tap est retiré. C'est la différence avec
   « user-scalable=no », qui supprimerait le pincement — et qui de
   toute façon est ignoré par iOS depuis la version 10.

   Aucun élément de Budgo n'a besoin d'un geste tactile particulier :
   vérifié, 0 « input type=range », 0 « draggable », 0 « canvas ».
   La règle peut donc couvrir tout le document sans rien casser. */
body * { touch-action: pan-x pan-y; }   /* v63.14 — voir ci-dessus */
body {
  font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--text);
  background: var(--bg);
  line-height: 1.55;
}
button, input, select, textarea { font: inherit; }
button { cursor: pointer; border: none; background: none; }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; }

/* ─── Typography ─── */
.serif   { font-family: Georgia, 'Times New Roman', serif; }
.muted   { color: var(--muted); }
.small   { font-size: 13px; }
.lbl     { font-size: 12px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }

/* ─── Layout helpers ─── */
.container  { max-width: 1160px; margin: 0 auto; padding: 0 28px; }
.hidden     { display: none !important; }

/* ─── Buttons ─── */
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 13px 22px; border-radius: var(--r-sm);
  font-weight: 700; font-size: 15px;
  transition: all .17s ease;
  white-space: nowrap; cursor: pointer; border: none;
  /* v13 — CORRECTIF E1.
     « white-space: nowrap » empêche un bouton de passer à la ligne. Sur
     un libellé long — « 📲 Installer Budgo sur mon écran d'accueil »,
     351 px incompressibles — le bouton imposait sa largeur à toute la
     grille des Réglages, qui débordait alors de l'écran : la page
     mesurait 411 px de large sur un téléphone de 320, 360 ou 390 px, et
     l'application se décalait horizontalement.

     « min-width: 0 » autorise le bouton à se comprimer à l'intérieur
     d'une grille ou d'une boîte flexible ; « max-width: 100% » l'empêche
     de dépasser son parent. Le texte reste sur une seule ligne — donc
     rien ne change visuellement sur un écran large — mais il ne peut
     plus élargir la page. Le passage à la ligne, lui, est traité juste
     en dessous pour les écrans étroits. */
  min-width: 0; max-width: 100%;
}

/* v13 — CORRECTIF E1, suite : sous 430 px, un libellé trop long a le
   droit de passer à la ligne plutôt que de déborder. Le bouton reste
   lisible et centré, et la page ne dépasse plus jamais l'écran. */
@media (max-width: 430px) {
  .btn {
    white-space: normal;
    text-align: center;
    word-break: break-word;
    line-height: 1.35;
  }
}
.btn-sm  { padding: 9px 16px; font-size: 14px; border-radius: 12px; }
.btn-lg  { padding: 16px 32px; font-size: 17px; border-radius: 18px; }
.btn-xl  { padding: 18px 38px; font-size: 18px; border-radius: 20px; }

.btn-primary {
  /* v20.4 — mauve pastel au lieu du fuchsia. L'ombre suit la couleur du
     bouton : une ombre rose sous un bouton mauve se voit. */
  background: var(--degrade-bouton);
  color: #fff;
  box-shadow: var(--ombre-bouton);
}
.btn-primary:hover  { transform: translateY(-2px); box-shadow: 0 14px 32px rgba(139,102,178,.38); }
.btn-primary:active { transform: translateY(0); }

.btn-outline {
  background: rgba(255,255,255,.9);
  border: 1.5px solid var(--line);
  color: var(--encre-2);
}
.btn-outline:hover { border-color: var(--brand); color: var(--brand); background: #fff; }

.btn-soft   { background: var(--green-soft); color: var(--encre-mauve); }
.btn-soft:hover { background: var(--green-soft); }

.btn-ghost  { background: transparent; color: var(--muted); }
.btn-ghost:hover { background: var(--carte); color: var(--text); }

.btn-danger { background: var(--red-soft); color: var(--encre-rose); }
.btn-danger:hover { background: var(--red-soft); }

/* ─── Form controls ─── */
.input {
  border: 1.5px solid var(--line);
  border-radius: 14px;
  padding: 13px 16px;
  background: #fff;
  width: 100%;
  transition: border-color .15s, box-shadow .15s;
  color: var(--text);
}
.input:focus  { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px rgba(234,142,152,.12); }
.input::placeholder { color: var(--encre-3); }

.select {
  border: 1px solid var(--line); border-radius: 12px;
  padding: 9px 14px; background: #fff; color: var(--muted);
  cursor: pointer;
}

.field { display: flex; flex-direction: column; gap: 7px; }

/* ─── Icon ─── */
.icon {
  width: 20px; height: 20px;
  stroke: currentColor; stroke-width: 1.85;
  fill: none; stroke-linecap: round; stroke-linejoin: round;
  flex-shrink: 0;
}
.icon-sm { width: 16px; height: 16px; }
.icon-lg { width: 24px; height: 24px; }

/* ─── Card ─── */
.card {
  background: rgba(255,255,255,.92);
  border: 1px solid rgba(220,212,234,.65);
  border-radius: 30px; padding: 22px;
  box-shadow: 0 8px 30px rgba(180,167,217,.1);
}
.card-head {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 10px; margin-bottom: 12px;
}
.card-head h2 { font-size: 18px; margin: 0 0 2px; }

/* ─── Badge ─── */
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: 999px;
  font-size: 13px; font-weight: 700;
}
.badge-green  { background: var(--green-soft);  color: var(--green); }
.badge-red    { background: var(--red-soft);    color: var(--red); }
.badge-purple { background: var(--purple-soft); color: var(--purple); }
.badge-brand  { background: var(--brand-soft);  color: var(--brand-dark); }
.badge-white  { background: rgba(255,255,255,.74); color: var(--green); }

/* ─── Round button ─── */
.round-btn {
  width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(255,255,255,.85); border: 1px solid var(--line);
  box-shadow: var(--shadow-sm); cursor: pointer; transition: all .15s;
  /* v20.10 — POINT 2 : LA CLOCHE ÉTAIT BLEUE SUR IPHONE.
     Aucune couleur n'était déclarée ici. Un <button> sans couleur ne
     prend PAS celle de la page : il retombe sur la couleur par défaut
     du navigateur, qui est le bleu système sur iOS. Le dessin de la
     cloche, tracé en « currentColor », héritait donc de ce bleu.
     On déclare l'encre secondaire de l'application : la cloche et le
     bouton de déconnexion deviennent gris neutres partout. */
  color: var(--encre-2, #5F5566);
}
.round-btn:hover { background: #fff; border-color: var(--brand); color: var(--brand); }

/* ─── Spinner ─── */
.spin {
  width: 36px; height: 36px; border-radius: 50%;
  border: 4px solid var(--rose-soft); border-top-color: var(--brand);
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ─── Animations ─── */
@keyframes fadeUp {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes fadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
/* v20.9 — voir le commentaire de render() dans app.js : l'animation
   d'entrée ne joue qu'en ARRIVANT sur un écran, jamais quand on
   redessine celui qu'on regarde déjà. */
#app.sans-animation .anim-up,
#app.sans-animation .anim-in { animation: none; }
.anim-up { animation: fadeUp .4s ease both; }
.anim-in { animation: fadeIn .3s ease both; }

/* ─── Dialog ─── */
.dialog-backdrop {
  position: fixed; inset: 0; background: rgba(35,28,26,.4);
  display: none; align-items: center; justify-content: center;
  padding: 18px; z-index: 40; backdrop-filter: blur(4px);
}
.dialog-backdrop.show { display: flex; animation: fadeIn .2s ease; }
.dialog {
  background: #fff; width: min(560px, 100%);
  border-radius: 28px; padding: 26px;
  box-shadow: var(--shadow-lg); animation: fadeUp .25s ease;
}
.dialog-head {
  display: flex; justify-content: space-between; align-items: center;
  gap: 10px; margin-bottom: 20px;
}
.dialog-head h3 { margin: 0; font-size: 20px; }
.form-grid    { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.form-actions { display: flex; gap: 10px; margin-top: 20px; justify-content: flex-end; }

/* ─── Alert ─── */
.alert {
  padding: 13px 16px; border-radius: 12px;
  font-size: 14px; font-weight: 600;
  display: flex; align-items: center; gap: 8px;
}
.alert-error   { background: var(--red-soft);   color: var(--rose-negatif); }
.alert-success { background: var(--green-soft);  color: var(--green); }

/* ─── Toast ─── */
.toast {
  position: fixed; bottom: 28px; right: 28px;
  background: var(--encre); color: #fff;
  padding: 14px 20px; border-radius: 16px;
  font-size: 14px; font-weight: 600;
  box-shadow: 0 8px 28px rgba(0,0,0,.22);
  z-index: 100; animation: fadeUp .3s ease;
  display: flex; align-items: center; gap: 8px;
}

/* ═══════════════════════════════════════════════════════
   LANDING PAGE STYLES
   ═══════════════════════════════════════════════════════ */

/* Navbar */
.lp-nav {
  position: sticky; top: 0; z-index: 30;
  background: rgba(248,241,235,.92); backdrop-filter: blur(14px);
  border-bottom: 1px solid rgba(234,223,215,.7);
}
.lp-nav-inner {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 0; gap: 20px;
}
.lp-logo {
  font-family: Georgia, 'Times New Roman', serif;
  font-size: 26px; color: var(--brand);
  display: flex; align-items: flex-start; gap: 3px; line-height: 1;
}
.lp-logo em { font-style: normal; font-size: 16px; transform: translateY(-5px); }
.lp-nav-links { display: flex; align-items: center; gap: 6px; }
.lp-nav-links a {
  padding: 8px 16px; border-radius: 12px; font-weight: 600;
  color: var(--encre-2); transition: all .15s;
}
.lp-nav-links a:hover { background: #fff; color: var(--brand); }

/* Hero */
.lp-hero {
  padding: 80px 0 60px;
  text-align: center;
  /* v10 — position:relative pour accueillir les pétales, et z-index sur le
     contenu pour qu'ils restent strictement décoratifs (derrière le texte). */
  position: relative;
  overflow: hidden;
  background: radial-gradient(circle at 20% 80%, rgba(245,210,206,.5), transparent 40%),
              radial-gradient(circle at 80% 10%, rgba(214,200,240,.45), transparent 40%);
}
.lp-hero > .container { position: relative; z-index: 1; }
.lp-hero-pill {
  display: inline-flex; align-items: center; gap: 8px;
  background: linear-gradient(135deg, var(--rose-soft) 0%, var(--lavender-soft) 100%);
  border: 1px solid rgba(180,167,217,.35);
  color: var(--brand-dark);
  padding: 8px 20px; border-radius: 999px;
  font-size: 13px; font-weight: 700;
  margin-bottom: 28px;
  box-shadow: 0 2px 12px rgba(180,167,217,.18);
}
.lp-hero h1 {
  font-family: Georgia, 'Times New Roman', serif;
  font-size: clamp(36px, 5vw, 62px);
  font-style: italic;
  line-height: 1.15; letter-spacing: -.02em;
  margin-bottom: 22px;
  color: var(--text);
}
.lp-hero h1 em { font-style: italic; color: var(--brand); }
.lp-hero p {
  font-size: 18px; color: var(--muted);
  max-width: 600px; margin: 0 auto 36px;
  line-height: 1.65;
}
.lp-hero-cta { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }

/* App preview / mockup */
.lp-preview {
  margin: 52px auto 0;
  max-width: 880px;
  background: rgba(255,255,255,.7);
  border: 1px solid rgba(234,223,215,.9);
  border-radius: 28px;
  box-shadow: 0 30px 80px rgba(104,85,76,.13);
  overflow: hidden;
  position: relative;
}
.lp-preview-bar {
  background: rgba(248,241,235,.95); border-bottom: 1px solid var(--line);
  padding: 12px 18px; display: flex; align-items: center; gap: 8px;
}
.lp-preview-dot { width: 11px; height: 11px; border-radius: 50%; }
.lp-preview-inner { padding: 24px; }
.lp-preview-stats {
  display: grid; grid-template-columns: repeat(4,1fr); gap: 12px; margin-bottom: 18px;
}
.lp-stat-mini {
  background: rgba(255,255,255,.85); border: 1px solid rgba(234,223,215,.8);
  border-radius: 18px; padding: 14px; box-shadow: var(--shadow-sm);
}
.lp-stat-mini .ico { width: 36px; height: 36px; border-radius: 12px; display: grid; place-items: center; margin-bottom: 10px; font-size: 17px; }
.lp-stat-mini .val { font-size: 18px; font-weight: 800; font-family: Georgia, serif; }
.lp-stat-mini .lbl2 { font-size: 12px; color: var(--muted); font-weight: 600; margin-top: 2px; }
.lp-chart-bar {
  background: rgba(255,255,255,.85); border: 1px solid rgba(234,223,215,.8);
  border-radius: 18px; padding: 16px; box-shadow: var(--shadow-sm);
}
.lp-bars { display: flex; align-items: flex-end; gap: 6px; height: 80px; margin-top: 10px; }
.lp-bar { flex: 1; border-radius: 6px 6px 0 0; min-width: 0; transition: height .3s; }

/* Logos / stats bar */
.lp-trust {
  padding: 40px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  text-align: center;
}
.lp-trust p { font-size: 13px; font-weight: 700; color: var(--muted); letter-spacing: .06em; text-transform: uppercase; margin-bottom: 28px; }
.lp-numbers { display: flex; justify-content: center; gap: 60px; flex-wrap: wrap; }
.lp-number { text-align: center; }
.lp-number strong { display: block; font-size: 38px; font-family: Georgia, serif; color: var(--brand); line-height: 1; }
.lp-number span { font-size: 14px; color: var(--muted); font-weight: 600; }

/* Features */
.lp-section { padding: 72px 0; }
.lp-section-label {
  display: inline-flex; align-items: center; gap: 8px;
  background: linear-gradient(135deg, var(--rose-soft) 0%, var(--lavender-soft) 100%);
  border: 1px solid rgba(180,167,217,.3);
  color: var(--brand-dark);
  padding: 7px 16px; border-radius: 999px;
  font-size: 13px; font-weight: 700; margin-bottom: 16px;
  box-shadow: 0 2px 10px rgba(180,167,217,.15);
}
.lp-section h2 {
  font-family: Georgia, 'Times New Roman', serif;
  font-size: clamp(26px, 3.5vw, 42px);
  font-style: italic;
  line-height: 1.2; letter-spacing: -.02em;
  margin-bottom: 14px;
}
.lp-section > .container > p {
  font-size: 17px; color: var(--muted); max-width: 560px; line-height: 1.65;
}
.lp-features { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; margin-top: 44px; }
.lp-feat {
  background: rgba(255,255,255,.88); border: 1px solid rgba(220,212,234,.7);
  border-radius: 32px; padding: 28px; box-shadow: 0 8px 32px rgba(180,167,217,.1);
  transition: all .2s;
}
.lp-feat:hover { transform: translateY(-4px); box-shadow: 0 16px 48px rgba(180,167,217,.2); }
.lp-feat-ico {
  width: 56px; height: 56px; border-radius: 50%;
  display: grid; place-items: center; font-size: 24px;
  margin-bottom: 20px;
  box-shadow: 0 4px 16px rgba(180,167,217,.18);
}
.lp-feat h3 { font-size: 17px; margin-bottom: 8px; }
.lp-feat p { font-size: 14px; color: var(--muted); line-height: 1.6; }

/* Steps */
.lp-steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-top: 44px; position: relative; }
.lp-steps::before {
  content: ''; position: absolute; top: 32px; left: 15%; right: 15%;
  height: 2px; background: linear-gradient(90deg, var(--hero1), var(--hero2));
  z-index: 0;
}
.lp-step { text-align: center; position: relative; z-index: 1; }
.lp-step-num {
  width: 64px; height: 64px; border-radius: 50%;
  background: linear-gradient(135deg, var(--hero1), var(--hero2));
  color: #fff; font-size: 22px; font-family: Georgia, serif; font-weight: 700;
  display: grid; place-items: center; margin: 0 auto 18px;
  box-shadow: 0 8px 24px rgba(244,199,193,.5);
}
.lp-step h3 { font-size: 16px; margin-bottom: 8px; }
.lp-step p { font-size: 14px; color: var(--muted); line-height: 1.6; }

/* Pricing */
.lp-pricing { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin: 44px auto 0; max-width: 800px; }
.lp-plan {
  background: rgba(255,255,255,.85); border: 1.5px solid rgba(234,223,215,.9);
  border-radius: 28px; padding: 32px; box-shadow: var(--shadow-sm);
  position: relative; transition: all .2s;
}
.lp-plan.featured {
  background: linear-gradient(160deg, var(--carte), var(--purple-soft));
  border-color: rgba(180,167,217,.4);
  box-shadow: 0 16px 48px rgba(180,167,217,.2);
}
.lp-plan-badge {
  position: absolute; top: -12px; right: 24px;
  background: linear-gradient(135deg, var(--brand), var(--brand-dark));
  color: #fff; padding: 5px 14px; border-radius: 999px;
  font-size: 12px; font-weight: 700;
}
.lp-plan-name { font-size: 14px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; margin-bottom: 10px; }
.lp-plan-price { font-family: Georgia, serif; font-size: 46px; line-height: 1; margin-bottom: 6px; }
.lp-plan-price span { font-size: 18px; font-family: inherit; }
.lp-plan-period { font-size: 14px; color: var(--muted); margin-bottom: 24px; }
.lp-plan-feats { list-style: none; display: flex; flex-direction: column; gap: 10px; margin-bottom: 28px; }
.lp-plan-feats li { display: flex; align-items: center; gap: 10px; font-size: 14px; font-weight: 600; }
.lp-plan-feats li::before { content: '✓'; color: var(--green); font-weight: 800; }
.lp-plan-feats li.dimmed { color: var(--muted); }
.lp-plan-feats li.dimmed::before { content: '–'; color: var(--line); }

/* Testimonials */
.lp-testi { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; margin-top: 44px; }
.lp-testi-card {
  background: rgba(255,255,255,.9); border: 1px solid rgba(220,212,234,.7);
  border-radius: 32px; padding: 28px; box-shadow: 0 8px 32px rgba(180,167,217,.1);
  transition: all .2s;
}
.lp-testi-card:hover { transform: translateY(-2px); box-shadow: 0 12px 40px rgba(180,167,217,.18); }
.lp-testi-stars { font-size: 16px; margin-bottom: 14px; letter-spacing: 2px; }
.lp-testi-text { font-size: 15px; line-height: 1.65; color: var(--encre); margin-bottom: 20px; font-style: italic; }
.lp-testi-author { display: flex; align-items: center; gap: 12px; }
.lp-testi-avatar {
  width: 42px; height: 42px; border-radius: 50%;
  display: grid; place-items: center;
  font-weight: 800; font-size: 16px; color: #fff;
  flex-shrink: 0;
}
.lp-testi-name { font-weight: 700; font-size: 14px; }
.lp-testi-role { font-size: 12px; color: var(--muted); }

/* CTA Banner */
.lp-cta-banner {
  margin: 0 0 60px;
  background: linear-gradient(135deg, var(--hero1), var(--hero2));
  border-radius: 32px; padding: 56px 48px; text-align: center;
  position: relative; overflow: hidden;
  box-shadow: 0 24px 64px rgba(244,199,193,.4);
}
.lp-cta-banner::before {
  content: ''; position: absolute; right: -40px; top: -40px;
  width: 320px; height: 320px;
  background: radial-gradient(circle, rgba(255,255,255,.25), transparent 60%);
}
.lp-cta-banner h2 {
  font-family: Georgia, serif; font-size: clamp(26px, 3.5vw, 40px);
  font-style: italic;
  color: #fff; margin-bottom: 14px; line-height: 1.2;
}
.lp-cta-banner p { color: rgba(255,255,255,.85); font-size: 17px; margin-bottom: 32px; }
.lp-cta-banner .lp-hero-cta { margin: 0; }

/* Footer */
.lp-footer {
  background: rgba(255,255,255,.6); border-top: 1px solid var(--line);
  padding: 40px 0;
}
.lp-footer-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px; flex-wrap: wrap;
}
.lp-footer p { font-size: 13px; color: var(--muted); }
/* v10 — les 7 liens du pied de page ne tenaient pas sur un mobile et
   provoquaient un défilement horizontal de toute la landing (bug présent
   avant le Lot 4). Ils passent maintenant à la ligne. */
.lp-footer-links { display: flex; gap: 24px; flex-wrap: wrap; justify-content: center; max-width: 100%; }
.lp-footer-links a { font-size: 13px; color: var(--muted); font-weight: 600; transition: color .15s; }
.lp-footer-links a:hover { color: var(--brand); }

/* ═══════════════════════════════════════════════════════
   APPLICATION STYLES
   ═══════════════════════════════════════════════════════ */

/* Auth page */
.auth-page {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  padding: 20px;
  /* v10 — relative + overflow pour les pétales décoratifs */
  position: relative; overflow: hidden;
  background: radial-gradient(circle at 10% 90%, rgba(245,210,206,.4), transparent 30%),
              radial-gradient(circle at 90% 10%, rgba(214,200,240,.38), transparent 30%),
              linear-gradient(180deg, var(--bg), var(--carte));
}
.auth-box {
  position: relative; z-index: 1;
  width: min(440px, 100%);
  background: rgba(255,255,255,.9); border: 1px solid rgba(234,223,215,.9);
  border-radius: 32px; padding: 36px;
  box-shadow: var(--shadow-lg);
  animation: fadeUp .4s ease;
}
.auth-logo {
  font-family: Georgia, serif; font-size: 32px; color: var(--brand);
  display: flex; align-items: flex-start; gap: 3px; line-height: 1;
  margin-bottom: 28px;
}
.auth-logo em { font-style: normal; font-size: 18px; transform: translateY(-6px); }
.auth-title { font-size: 22px; font-weight: 800; margin-bottom: 6px; }
.auth-sub   { font-size: 14px; color: var(--muted); margin-bottom: 28px; }
.auth-form  { display: flex; flex-direction: column; gap: 16px; }
.auth-divider {
  display: flex; align-items: center; gap: 12px;
  color: var(--muted); font-size: 13px; font-weight: 600; margin: 4px 0;
}
.auth-divider::before, .auth-divider::after {
  content: ''; flex: 1; height: 1px; background: var(--line);
}
.auth-switch { text-align: center; font-size: 14px; color: var(--muted); margin-top: 16px; }
.auth-switch a { color: var(--brand); font-weight: 700; cursor: pointer; }
.auth-switch a:hover { text-decoration: underline; }

/* App layout */
/* CORRECTIF v20.1 — LA CAUSE RÉELLE DU DÉCOR INVISIBLE.
   Ce bloc enveloppe TOUTE l'application et peignait un fond OPAQUE sur
   la hauteur entière de la page. Le décor animé, lui, vit derrière
   l'application : il était donc recouvert à 100 %, non pas discret mais
   littéralement effacé. Preuve : en forçant les cœurs à 70 px, en rouge
   vif et à pleine opacité, la capture ne changeait pas d'UN SEUL pixel.
   Aucun réglage d'opacité n'y aurait rien changé.

   Ce fond était par ailleurs un reste de la v19 : deux halos beige et
   vert amande (rgba(223,232,206)) qui n'appartiennent pas à la palette
   validée, et qui délavaient le bas de chaque écran. Il est donc retiré,
   et c'est le dégradé rose de la page — celui du brief, posé sur
   « body » — qui redevient visible, décor compris.
   La hauteur minimale, elle, reste : c'est elle qui garantit que la
   page occupe l'écran même sur un contenu court. */
.app-bg {
  min-height: 100vh;
  background: transparent;
}
/* v20.9 — LE BLOC S'ARRÊTAIT À 1 400 px, LA COLONNE À 860 : SUR UN
   ÉCRAN LARGE, TOUT LE VIDE TOMBAIT À DROITE.
   Le bloc est centré, mais la rangée qu'il contient est alignée à
   gauche : barre latérale + colonne = 1 157 px, et le reste — jusqu'à
   250 px — restait inoccupé, entièrement du même côté. D'où la bande
   vide constatée sur un écran de bureau.
   Le bloc peut désormais aller jusqu'à 1 760 px et la colonne de
   lecture jusqu'à 1 280 : l'espace disponible est occupé, et ce qui
   reste au-delà se répartit également des deux côtés. */
.app-layout { display: flex; gap: 24px; max-width: 1760px; padding: 28px; margin: 0 auto; }

/* Sidebar */
.sidebar {
  width: 245px; background: rgba(255,255,255,.82); backdrop-filter: blur(12px);
  border: 1px solid rgba(234,223,215,.9); border-radius: 34px;
  box-shadow: var(--shadow); padding: 26px 18px;
  display: flex; flex-direction: column;
  min-height: calc(100vh - 56px); position: sticky; top: 28px;
}
.sidebar-logo {
  font-family: Georgia, serif; font-size: 32px; color: var(--brand);
  display: flex; align-items: flex-start; gap: 4px; line-height: 1;
}
.sidebar-logo em { font-style: normal; font-size: 18px; transform: translateY(-5px); }
.nav { display: flex; flex-direction: column; gap: 6px; margin-top: 26px; }
.nav-btn {
  border: none; background: transparent; border-radius: 18px;
  padding: 15px 16px; display: flex; align-items: center; gap: 12px;
  color: var(--encre-2); text-align: left; font-weight: 600; font-size: 15px;
  transition: all .15s; width: 100%;
}
.nav-btn.active { background: var(--brand-soft); color: var(--brand-dark); }
.nav-btn:hover:not(.active) { background: var(--carte); }
.sidebar-footer {
  margin-top: auto;
  background: linear-gradient(180deg, var(--carte), var(--carte));
  border: 1px solid var(--line); border-radius: 22px;
  padding: 16px; color: var(--muted); font-size: 13px; line-height: 1.5;
}
.sidebar-footer .plant { font-size: 38px; display: block; margin-top: 10px; text-align: right; }

/* Main */
.app-main { flex: 1; min-width: 0; }

/* Topbar */
.topbar {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 16px; padding: 12px 6px 20px;
}
.topbar h1 { margin: 0; font-family: Georgia, serif; font-size: 28px; font-weight: 700; }
.topbar-sub { color: var(--muted); margin-top: 4px; font-size: 14px; }
.top-actions { display: flex; gap: 10px; align-items: center; }
.year-pill {
  display: flex; align-items: center; gap: 8px; border-radius: 16px; padding: 10px 14px;
  background: rgba(255,255,255,.85); border: 1px solid var(--line);
  box-shadow: var(--shadow-sm); color: var(--encre-2); font-weight: 700; font-size: 14px;
}
.avatar {
  width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center;
  background: linear-gradient(135deg, var(--rose-soft), var(--line));
  font-weight: 800; color: var(--encre-2); font-size: 17px;
  border: 1px solid var(--line); box-shadow: var(--shadow-sm); cursor: pointer;
}

/* Hero stat */
.hero-card {
  background: linear-gradient(135deg, var(--hero1) 0%, var(--hero2) 60%, var(--lavender) 100%);
  border-radius: 28px; padding: 26px 28px; position: relative;
  overflow: hidden; box-shadow: var(--shadow); color: #fff; min-height: 166px;
}
.hero-card::before {
  content: ''; position: absolute; right: -10px; top: -10px;
  width: 260px; height: 260px;
  background: radial-gradient(circle, rgba(255,255,255,.22), transparent 60%);
}
/* v10 — motif cœur du logo, en remplacement de l'ancienne fleur « ❀ »
   v20.10 — POINT 6 : UN SEUL CŒUR, ENTIER, DANS LE COIN BAS-DROIT.
   Il y en avait deux : un grand qui débordait de 22 px sous le bord de
   la carte — coupé net par le « overflow: hidden », d'où l'impression
   d'un contour plutôt que d'un cœur — et un petit posé plus haut, deux
   fois et demie plus opaque. Le petit disparaît ; le grand rentre
   entièrement dans la carte et descend à 16 % d'opacité, assez pour se
   deviner, trop peu pour concurrencer le montant. */
.hero-card .hero-motif {
  position: absolute; right: 18px; bottom: 14px;
  width: 132px; height: 121px; color: #fff; opacity: .16;
  pointer-events: none;
}
@media (max-width: 560px) {
  /* Sur mobile la carte ne fait que 140 px de haut : le cœur est réduit
     pour rester sous le montant et à droite de la bascule Mois/Année. */
  .hero-card .hero-motif { width: 96px; height: 88px; right: 12px; bottom: 10px; }
}
@media (max-width: 360px) {
  /* Mesuré : à 320 px, un cœur de 96 px touchait le bouton « Année » de
     2 px. Réduit, il repasse à 12 px de distance. */
  .hero-card .hero-motif { width: 84px; height: 77px; right: 10px; bottom: 10px; }
}
.hero-label { font-size: 14px; font-weight: 600; opacity: .95; display: flex; gap: 8px; align-items: center; }
.hero-value {
  font-size: 58px; font-family: Georgia, serif;
  margin: 8px 0 12px; line-height: 1; letter-spacing: -.03em;
}
.hero-badge {
  display: inline-flex; align-items: center; gap: 8px;
  /* v21 — le voile passe de 74 % à 88 % et l'encre est celle des
     pastilles pâles : à 74 %, le fond réel restait teinté par le
     dégradé et le texte n'y valait que 3,66:1. */
  background: rgba(255,255,255,.88); color: var(--encre-mauve);
  padding: 9px 14px; border-radius: 13px; font-weight: 700; font-size: 14px;
}

/* Stats grid */
.stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-top: 16px; }
.stat-card {
  background: rgba(255,255,255,.92); border: 1px solid rgba(220,212,234,.65);
  border-radius: 28px; padding: 18px; box-shadow: 0 6px 24px rgba(180,167,217,.1);
  transition: all .2s;
}
.stat-card:hover { transform: translateY(-2px); box-shadow: 0 12px 36px rgba(180,167,217,.18); }
.stat-top    { display: flex; gap: 12px; align-items: flex-start; }
.stat-ico    { width: 42px; height: 42px; border-radius: 50%; display: grid; place-items: center; flex-shrink: 0; }
.stat-title  { font-size: 13px; font-weight: 600; color: var(--muted); }
.stat-value  { font-size: 17px; font-weight: 800; margin-top: 3px; }
.stat-foot   { margin-top: 10px; font-size: 13px; font-weight: 700; }

/* Colors */
.c-green  { color: var(--green); }  .c-red   { color: var(--red); }
.c-purple { color: var(--purple); } .c-orange { color: var(--orange); }
.c-green  { color: var(--green); }  .c-red   { color: var(--red); }
.c-purple { color: var(--purple); } .c-orange { color: var(--orange); }
.c-blue   { color: var(--blue); }
.bg-green  { background: var(--green-soft); }  .bg-red   { background: var(--red-soft); }
.bg-purple { background: var(--purple-soft); } .bg-orange { background: var(--orange-soft); }
.bg-blue   { background: var(--blue-soft); }   .bg-lavender { background: var(--lavender-soft); }

/* Charts */
.chart-grid { display: grid; grid-template-columns: 1.1fr .95fr; gap: 16px; margin-top: 16px; }
.legend { display: flex; gap: 18px; flex-wrap: wrap; font-size: 13px; color: var(--muted); font-weight: 600; margin-top: 8px; }
.legend span { display: inline-flex; align-items: center; gap: 7px; }
.dot { width: 8px; height: 8px; border-radius: 50%; }
.donut-wrap { display: grid; grid-template-columns: 190px 1fr; gap: 18px; align-items: center; }
.donut-center { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; flex-direction: column; color: var(--muted); font-size: 13px; }
.donut-center strong { font-size: 22px; color: var(--text); font-family: Georgia, serif; }
/* ═══ v21.3 — UNE SEULE GRILLE POUR TOUTE LA LÉGENDE ═══════════════
   Chaque ligne était SA PROPRE grille. La colonne du pourcentage se
   calait donc sur la largeur du montant voisin, montant qui change à
   chaque ligne : mesuré, les pourcentages dérivaient de 22,5 px.
   La liste devient une grille unique et les lignes deviennent
   transparentes (« display: contents ») : les quatre colonnes sont
   alors partagées, exactement comme dans un tableau. */
.cat-list {
  display: grid; grid-template-columns: auto 1fr auto auto;
  gap: 10px; align-items: center; font-size: 13px;
}
.cat-item { display: contents; }
.cat-item .pct, .cat-item b { text-align: right; }
.cat-item b { font-weight: 700; }
.cat-item .pct { color: var(--muted); font-weight: 700;
  /* v21.3 — les pourcentages aussi : même largeur de chiffre, sinon la
     colonne fraîchement alignée ondulerait encore. */
  font-variant-numeric: lining-nums tabular-nums; }

/* Advice card */
.advice {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px; margin-top: 16px; padding: 18px 20px;
  background: linear-gradient(180deg, var(--carte), var(--brand-soft));
  border: 1px solid rgba(234,223,215,.85); border-radius: 24px;
  box-shadow: var(--shadow-sm);
}
.advice-left { display: flex; align-items: center; gap: 14px; }
.plant-icon {
  width: 54px; height: 54px; border-radius: 18px;
  background: var(--green-soft); display: grid; place-items: center; font-size: 26px;
  flex-shrink: 0;
}

/* Month nav */
.month-bar { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: var(--pas-bloc); }
.arrow-btn {
  width: 42px; height: 42px; border-radius: 50%;
  background: #fff; border: 1px solid var(--line); display: grid; place-items: center;
  cursor: pointer; transition: all .15s;
}
.arrow-btn:hover { border-color: var(--brand); color: var(--brand); }
/* v20.8 — ce titre était le seul à nommer Georgia en dur : il serait
   resté en Georgia pendant que tout le reste passait à Lora. */
.month-title { font-family: var(--police-titre); font-size: 26px; text-align: center; flex: 1; }
.month-title span { font: 600 13px ui-sans-serif; color: var(--muted); display: block; margin-top: 3px; }

/* Segmented control */
.segmented {
  display: flex; gap: 5px;
  background: var(--carte); border: 1px solid var(--line);
  border-radius: 999px; padding: 4px; width: max-content;
}
.seg-btn {
  border: none; background: transparent; border-radius: 999px;
  padding: 8px 16px; font-weight: 700; color: var(--muted); font-size: 14px; cursor: pointer;
  transition: all .15s;
}
.seg-btn.active { background: #fff; color: var(--brand-dark); box-shadow: var(--shadow-sm); }

/* Transaction list */
.tx-list { display: flex; flex-direction: column; }
.tx-row {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 0; border-bottom: 1px solid var(--line); cursor: pointer;
  transition: background .12s; border-radius: 8px; padding-left: 4px; padding-right: 4px;
}
.tx-row:last-child { border-bottom: none; }
.tx-row:hover { background: var(--carte); }
.tx-ico { width: 46px; height: 46px; border-radius: 16px; display: grid; place-items: center; font-weight: 800; flex-shrink: 0; font-size: 15px; }
.tx-main { flex: 1; min-width: 0; }
.tx-title { font-weight: 700; font-size: 14px; }
.tx-sub { font-size: 12px; color: var(--muted); margin-top: 3px; }
.tx-amount { text-align: right; }
.amt { font-weight: 800; font-size: 15px; }
.amt.pos { color: var(--green); } .amt.neg { color: var(--red); }

/* Section head */
.section-head {
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px; margin: var(--pas-large) 0 var(--pas-serre);
}

/* v32 — la rangée d'onglets passe à six : sur un téléphone elle défile
   horizontalement plutôt que de comprimer les libellés jusqu'à les
   rendre illisibles. La barre de défilement est masquée, le geste reste. */
.tx-onglets {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  max-width: 100%;
}
.tx-onglets::-webkit-scrollbar { display: none; }

/* ═══ v33 — FILTRE MULTI-CATÉGORIES ══════════════════════════════════
   Pastilles de la sélection, bouton de dépliage, panneau de cases à
   cocher. Tout est dimensionné pour le doigt : 40 px de haut minimum
   sur les zones cliquables. */
.cat-puces { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.cat-puce {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--lavender-soft); color: var(--purple);
  border: 1px solid rgba(159,146,216,.35); border-radius: 999px;
  padding: 7px 12px; font-size: 13px; font-weight: 700;
  font-family: inherit; cursor: pointer; line-height: 1.2;
}
.cat-puce:hover  { background: var(--purple-soft); }
.cat-puce span   { opacity: .65; font-weight: 800; }

.cat-panneau {
  margin-top: 10px; background: #fff;
  border: 1.5px solid var(--line); border-radius: 14px;
  padding: 12px 14px; max-height: 46vh; overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.cat-groupe + .cat-groupe { margin-top: 12px; }
.cat-groupe-titre {
  font-size: 11px; font-weight: 800; letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 4px;
}
/* v34 — plus de cases à cocher : le nom seul, et une coche verte à
   droite pour ce qui est pris. Trente cases carrées alignées chargeaient
   l'écran bien plus que l'information qu'elles portaient. */
.cat-ligne {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; min-height: 40px; padding: 8px 2px;
  background: none; border: none; border-radius: 8px;
  font-family: inherit; font-size: 14px; color: var(--text);
  text-align: left; cursor: pointer;
}
.cat-ligne:hover        { background: var(--lavender-soft); }
.cat-ligne.prise        { font-weight: 700; }
.cat-ligne .cat-coche   { color: var(--green); font-weight: 800; font-size: 15px; flex: 0 0 auto; }

.cat-detail {
  background: #fff; border: 1px solid var(--line); border-radius: 12px;
  padding: 4px 12px; margin: 0 0 12px;
}
.cat-detail-ligne {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; padding: 8px 0; font-size: 13px;
  border-bottom: 1px solid var(--line);
}
.cat-detail-ligne:last-child { border-bottom: none; }

/* ═══ v37 — PROJECTION : UNE LIGNE, UN MONTANT ═══════════════════════
   Le tableau à huit colonnes et les cartes à cinq nombres colorés ont
   disparu au profit d'une seule liste, valable à toutes les largeurs :
   deux mises en page à tenir en phase pour un écran dont le propos est
   la clarté, c'était une complication qui ne servait personne. */
.proj-mois {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 12px 4px; background: none; border: none;
  border-bottom: 1px solid var(--line); border-radius: 10px;
  font-family: inherit; font-size: 15px; color: var(--text);
  text-align: left; cursor: pointer;
}
.proj-mois:hover   { background: var(--lavender-soft); }
.proj-nom          { font-weight: 700; }
.proj-val          { margin-left: auto; font-weight: 800; font-size: 18px; white-space: nowrap; }
.proj-fl           { flex: 0 0 auto; font-size: 15px; font-weight: 800; }
.proj-haut         { color: var(--green); }
.proj-bas          { color: var(--red); }
.proj-encours      { background: var(--lavender-soft); }
.proj-negatif .proj-val { color: var(--red); }

.proj-detail       { padding: 2px 4px 12px; border-bottom: 1px solid var(--line); }
.proj-dl           { display: flex; justify-content: space-between; gap: 12px; padding: 5px 0; font-size: 13.5px; }
.proj-dl span      { color: var(--muted); }
.proj-dl b         { font-weight: 700; white-space: nowrap; }
.proj-total        { border-top: 1.5px solid var(--line); margin-top: 6px; padding-top: 8px; font-size: 14.5px; }
.proj-total span   { color: var(--text); font-weight: 700; }
.tx-onglets .seg-btn { white-space: nowrap; flex: 0 0 auto; }
.section-head h3 { font-size: 13px; text-transform: uppercase; color: var(--muted); letter-spacing: .08em; margin: 0; font-weight: 700; }
/* v21.4 — le titre de section devient une étiquette de la couleur de sa
   section. Le fond et l'encre sont posés en ligne (ils changent d'une
   section à l'autre) ; la forme est ici. */
.etiquette-section {
  display: inline-block; border-radius: var(--r-pill);
  padding: 4px 11px; font-size: 12px; font-weight: 800; letter-spacing: .05em;
  line-height: 1.25;
}
.add-link { background: none; border: none; color: var(--encre-rose); font-weight: 700; cursor: pointer; font-size: 14px; }
.add-link:hover { text-decoration: underline; }
.empty { padding: 30px 10px; text-align: center; color: var(--muted); font-size: 14px; }

/* Goal & credit cards */
.goal-card, .credit-card {
  display: flex; align-items: center; gap: 14px; padding: 16px;
  border: 1px solid var(--line); border-radius: 22px;
  background: #fff; box-shadow: var(--shadow-sm); margin-bottom: 12px; cursor: pointer;
  transition: all .15s;
}
.goal-card:hover, .credit-card:hover { box-shadow: var(--shadow); transform: translateY(-1px); }
.goal-body, .credit-body { flex: 1; }
.goal-title, .credit-title { font-weight: 800; font-size: 15px; }
.goal-meta, .credit-meta { font-size: 13px; color: var(--muted); margin-top: 4px; }
.goal-topline { font-size: 14px; font-weight: 700; margin-top: 6px; }
.ring { position: relative; width: 76px; height: 76px; flex: 0 0 auto; }
.ring svg { transform: rotate(-90deg); }
.ring span { position: absolute; inset: 0; display: grid; place-items: center; font-weight: 800; color: var(--encre-2); font-size: 13px; }

/* Settings */
.settings-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }

/* Footer nav (mobile) */
.footer-nav { display: none; }

/* Éléments visibles uniquement sur mobile */
.mobile-only { display: none; }

/* FAB */
.fab {
  position: fixed; right: calc(22px + env(safe-area-inset-right, 0px)); bottom: 24px;
  width: 62px; height: 62px; border: none; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--degrade-bouton); color: #fff;   /* v20.4 — mauve pastel */
  box-shadow: 0 18px 34px rgba(139,102,178,.34); z-index: 10; cursor: pointer;
  transition: all .18s;
}
.fab:hover { transform: scale(1.08); box-shadow: 0 22px 40px rgba(139,102,178,.4); }
.mobile-note { display: none; }

/* ─── Tableau prévisionnel vs réel ─── */
.bc-card { margin-top: 16px; }
.bc-table { display: flex; flex-direction: column; gap: 2px; }
.bc-header {
  display: grid; grid-template-columns: 1fr repeat(3, 120px);
  padding: 6px 10px; gap: 8px;
  font-size: 11px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: .05em;
}
.bc-col-label { text-align: right; }
.bc-row {
  display: grid; grid-template-columns: 1fr repeat(3, 120px);
  align-items: center; gap: 8px;
  padding: 11px 10px; border-radius: 12px; transition: background .12s;
}
.bc-row:hover { background: var(--carte); }
.bc-cat { font-size: 13px; font-weight: 600; color: var(--text); }
.bc-val { text-align: right; font-size: 13px; }
.bc-diff { text-align: right; font-size: 13px; }
.bc-sep { height: 1px; background: var(--line); margin: 4px 10px; }

@media (max-width: 600px) {
  .bc-header { grid-template-columns: 1fr repeat(3, 80px); }
  .bc-row    { grid-template-columns: 1fr repeat(3, 80px); }
  .bc-val, .bc-diff { font-size: 12px; }
  .bc-cat { font-size: 12px; }
}

/* v13 — CORRECTIF E1, suite : le tableau « Prévisionnel vs Réel ».
   Ses trois colonnes de chiffres étaient figées à 80 px chacune, plus
   les écarts et les marges : le tableau réclamait environ 390 px et
   élargissait la page sur tout téléphone plus étroit. Des colonnes
   proportionnelles s'adaptent à n'importe quelle largeur, et
   « minmax(0, …) » les autorise à descendre sous la taille de leur
   contenu au lieu de pousser les voisines. */
@media (max-width: 430px) {
  .bc-header,
  .bc-row {
    grid-template-columns: minmax(0, 1.3fr) repeat(3, minmax(0, 1fr));
    gap: 4px;
    padding-left: 6px;
    padding-right: 6px;
  }
  .bc-val, .bc-diff { font-size: 11.5px; }
  .bc-cat { font-size: 11.5px; min-width: 0; overflow-wrap: anywhere; }
  .bc-header { font-size: 10px; letter-spacing: 0; }
}

/* ─── Responsive (App) ─── */
/* v20.3 — cette bande s'arrêtait à 1 100 px, c'est-à-dire EN PLEIN
   milieu de la zone ordinateur (qui commence à 1 025 px) : entre
   1 025 et 1 100 px on obtenait 2 tuiles sur 2 rangées et des
   graphiques empilés, puis 4 tuiles et 2 graphiques 10 px plus loin.
   La borne rejoint celle des autres : 1 024 px. La zone ordinateur
   a désormais UNE seule mise en page, quelle que soit la largeur. */
@media (max-width: 1024px) {
  .app-layout { padding: 18px; gap: 16px; }
  .sidebar { width: 215px; }
  .stats-grid { grid-template-columns: repeat(2, 1fr); }
  .chart-grid { grid-template-columns: 1fr; }
  .hero-value { font-size: 48px; }
}
@media (max-width: 1024px) {
  .app-layout { padding: 0; display: block; }
  .sidebar { display: none; }
  /* v20.10 — POINT 1 : DE L'AIR EN HAUT DE L'ÉCRAN.
     Deux choses, et il faut les deux.

     a) LA BARRE SYSTÈME. « app.html » demande « viewport-fit=cover » :
        installée sur l'écran d'accueil, l'application s'étend SOUS
        l'heure et la batterie de l'iPhone. Rien ne réservait cette
        bande, d'où le titre collé en haut.
        « env(safe-area-inset-top) » vaut exactement la hauteur occupée
        par cette barre — 47 px sur un iPhone à encoche, 0 sur un écran
        plat — donc mieux qu'un nombre fixe, qui serait juste sur un
        modèle et faux sur tous les autres. Le retrait est posé sur
        « #app », et non sur la colonne : le bandeau « Vous utilisez
        Artivib Gratuit » est rendu AU-DESSUS d'elle et serait resté,
        lui, sous la barre système.

     b) L'AIR AU-DESSUS DU TITRE. Indépendamment de l'encoche, la barre
        du haut ne laissait que 8 px avant « Bonjour Manal ». Elle passe
        à 22 px. */
  /* v63.8 — c) LES CÔTÉS. « viewport-fit=cover » ne découvre pas que le
     haut : il étend AUSSI la page sous l'encoche et sous les coins
     arrondis, à GAUCHE et à DROITE. En paysage sur un iPhone à encoche,
     « env(safe-area-inset-left/right) » vaut 44 à 59 px de chaque côté.
     Ces deux retraits n'étaient nulle part : le contenu — et jusqu'aux
     éléments en « position: fixed », qui se calent sur la fenêtre et non
     sur la colonne — passaient sous l'encoche et sous les coins, donc
     rognés des DEUX bords à la fois. Un navigateur de bureau renvoie 0
     pour ces variables : le défaut est par construction invisible en
     émulation, il ne se voit que sur un vrai iPhone. */
  #app {
    padding-top: env(safe-area-inset-top, 0px);
    padding-left: env(safe-area-inset-left, 0px);
    padding-right: env(safe-area-inset-right, 0px);
  }
  .app-main { padding: 14px 14px 92px; }
  .topbar { padding: 22px 2px 16px; flex-wrap: wrap; gap: 10px; }
  .topbar h1 { font-size: 22px; }
  .topbar-sub { font-size: 13px; }
  /* v13 — la barre du haut porte une icône de plus depuis que le bouton
     « Installer » y reste visible en permanence. Sur un écran de 360 px
     (beaucoup d'Android), la rangée dépassait de 12 px et la page
     glissait latéralement. Elle a maintenant le droit de passer à la
     ligne et de se comprimer, au lieu d'élargir la page. */
  .top-actions { gap: 6px; flex-wrap: wrap; justify-content: flex-end; min-width: 0; }
  .year-pill { padding: 8px 10px; font-size: 13px; }
  .hero-card { padding: 20px; min-height: 140px; }
  .hero-value { font-size: 36px; }
  .hero-label { font-size: 13px; }
  .stats-grid { grid-template-columns: 1fr 1fr; gap: 10px; }
  .stat-value { font-size: 15px; }
  .donut-wrap { grid-template-columns: 1fr; justify-items: center; }
  .advice { flex-direction: column; align-items: flex-start; }
  .settings-grid { grid-template-columns: 1fr; }
  .form-grid { grid-template-columns: 1fr; }
  .month-title { font-size: 22px; }
  .dialog { padding: 20px; border-radius: 20px; }
  .footer-nav {
    display: flex; position: fixed; bottom: 12px;
    /* v63.8 — les barres fixes se calent sur la FENÊTRE : elles doivent
       porter elles-mêmes le retrait d'encoche, le retrait posé sur #app
       ne les concerne pas. */
    left:  calc(12px + env(safe-area-inset-left, 0px));
    right: calc(12px + env(safe-area-inset-right, 0px));
    background: rgba(255,255,255,.92); backdrop-filter: blur(10px);
    border: 1px solid var(--line); box-shadow: var(--shadow);
    border-radius: 26px; padding: 10px 8px; justify-content: space-around; z-index: 12;
  }
  .footer-nav button {
    border: none; background: none; color: var(--encre-3);
    display: flex; flex-direction: column; gap: 3px; align-items: center;
    font-size: 9px; font-weight: 700; flex: 1; min-width: 0;
  }
  .footer-nav button .icon { width: 18px; height: 18px; }
  .footer-nav button.active { color: var(--brand-dark); }
  .fab { bottom: 94px; right: calc(18px + env(safe-area-inset-right, 0px)); }
  .mobile-note { display: block; font-size: 13px; color: var(--muted); margin-top: 8px; }
  .toast { bottom: 104px; left: calc(14px + env(safe-area-inset-left, 0px)); right: calc(14px + env(safe-area-inset-right, 0px)); }
  /* Éléments visibles uniquement sur mobile */
  .mobile-only { display: block; }
  /* Bannière PWA : masquée si l'appli est déjà installée */
  .pwa-installed .pwa-install-banner,
  .pwa-installed #mobileInstallBtn { display: none !important; }
}

/* ─── Responsive (Landing) ─── */
@media (max-width: 900px) {
  .lp-features { grid-template-columns: repeat(2, 1fr); }
  .lp-steps { grid-template-columns: 1fr; }
  .lp-steps::before { display: none; }
  .lp-step-num { margin-bottom: 12px; }
  .lp-testi { grid-template-columns: 1fr; max-width: 480px; margin-left: auto; margin-right: auto; }
  .lp-pricing { grid-template-columns: 1fr; max-width: 380px; }
  .lp-preview-stats { grid-template-columns: repeat(2, 1fr); }
  .lp-numbers { gap: 36px; }
  .lp-cta-banner { padding: 40px 24px; }
  .lp-nav-links .hide-mobile { display: none; }
}
@media (max-width: 600px) {
  .lp-features { grid-template-columns: 1fr; }
  .lp-hero { padding: 50px 0 40px; }
  .lp-trust { padding: 28px 0; }
  .lp-section { padding: 48px 0; }
  .lp-preview-stats { grid-template-columns: 1fr 1fr; }
  .lp-footer-inner { flex-direction: column; align-items: flex-start; }
  /* v10 — les cartes de tarif imposaient leur largeur minimale (bouton en
     white-space:nowrap) et débordaient de l'écran : elles peuvent maintenant
     se rétrécir, et les libellés des boutons passent à la ligne. */
  .lp-pricing { max-width: 100%; }
  .lp-pricing > * { min-width: 0; }
  .lp-plan { padding: 26px 20px; }
  .lp-plan .btn { white-space: normal; width: 100%; justify-content: center; text-align: center; }
  .lp-footer-links { justify-content: flex-start; gap: 14px 18px; }
}

/* ══════════════════════════════════════
   FREEMIUM — Styles Premium
══════════════════════════════════════ */

/* ─── Bandeau Gratuit ─── */
.free-upgrade-banner {
  background: linear-gradient(90deg, var(--lavender-soft), var(--purple-soft));
  border-bottom: 1px solid var(--lavender);
  padding: 9px 28px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  font-size: 13px;
  font-weight: 600;
  flex-wrap: wrap;
}
.upgrade-pill {
  background: linear-gradient(135deg, var(--mauve-fonce), var(--mauve));
  color: #fff;
  border: none;
  border-radius: 999px;
  padding: 6px 16px;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  transition: all .17s ease;
  white-space: nowrap;
}
.upgrade-pill:hover { transform: translateY(-1px); box-shadow: 0 4px 14px rgba(159,146,216,.35); }

/* ─── Badge plan ─── */
.plan-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  padding: 4px 12px;
  border-radius: 999px;
  letter-spacing: .02em;
}

/* ─── Avatar profil (grande version) ─── */
.profile-avatar-large {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--hero1), var(--hero2));
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: Georgia, serif;
  font-size: 22px;
  color: #fff;
  font-weight: 700;
  flex-shrink: 0;
  box-shadow: 0 6px 20px rgba(180,167,217,.3);
}

/* ─── Nav Premium pill ─── */
.nav-premium { position: relative; }
.nav-premium-pill {
  background: linear-gradient(135deg, var(--mauve-fonce), var(--mauve));
  color: #fff;
  font-size: 10px;
  font-weight: 800;
  padding: 2px 7px;
  border-radius: 999px;
  margin-left: auto;
  letter-spacing: .03em;
}

/* ─── Page Premium ─── */
.premium-feature-card {
  cursor: pointer;
  transition: all .2s ease;
  border: 1.5px solid transparent;
}
.premium-feature-card:hover {
  transform: translateY(-3px);
  border-color: var(--lavender);
  box-shadow: 0 12px 32px rgba(159,146,216,.18);
}
.premium-locked-card {
  opacity: .82;
  border: 1px dashed var(--lavender);
  position: relative;
  overflow: hidden;
}
.premium-locked-card::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(240,236,251,.4), rgba(255,255,255,.2));
  pointer-events: none;
}

/* ─── Pricing boxes ─── */
.pricing-box {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: 16px;
  margin-bottom: var(--pas-bloc);
  margin-top: var(--pas-bloc);
}
.pricing-free {
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: 24px;
  padding: 28px 24px;
}
.pricing-premium {
  background: linear-gradient(135deg, var(--mauve-fonce), var(--mauve-fonce));
  border-radius: 24px;
  padding: 28px 24px;
  color: #fff;
  box-shadow: 0 12px 36px rgba(127,108,196,.32);
  position: relative;
  overflow: hidden;
}
.pricing-premium::before {
  content: '⭐';
  position: absolute;
  top: -20px; right: -20px;
  font-size: 80px;
  opacity: .08;
}
.pricing-badge {
  display: inline-block;
  background: var(--line);
  color: var(--encre-2);   /* v21 — 3,68:1 en gris discret : illisible à 12 px */
  font-size: 12px;
  font-weight: 700;
  padding: 4px 12px;
  border-radius: 999px;
  margin-bottom: 12px;
}
.pricing-badge-prem {
  display: inline-block;
  background: rgba(255,255,255,.2);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  padding: 4px 12px;
  border-radius: 999px;
  margin-bottom: 12px;
}
.pricing-price {
  font-family: var(--police-montant);
  font-variant-numeric: lining-nums tabular-nums;
  font-size: 42px;
  font-weight: 700;
  line-height: 1;
  margin-bottom: 4px;
}
.pricing-price-prem {
  font-family: var(--police-montant);
  font-variant-numeric: lining-nums tabular-nums;
  font-size: 42px;
  font-weight: 700;
  line-height: 1;
  margin-bottom: 4px;
  color: #fff;
}
.pricing-list {
  list-style: none;
  margin-top: 20px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: 14px;
  color: var(--muted);
}
.pricing-list-prem {
  list-style: none;
  margin-top: 20px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: 14px;
  color: rgba(255,255,255,.9);
}

/* ─── Gate overlay (si on l'utilise en plein écran) ─── */
.premium-gate-overlay {
  position: absolute;
  inset: 0;
  background: rgba(248,241,235,.85);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
  border-radius: inherit;
}
.premium-gate-box {
  background: #fff;
  border: 1.5px solid var(--lavender);
  border-radius: 24px;
  padding: 32px 28px;
  text-align: center;
  max-width: 320px;
  box-shadow: 0 16px 40px rgba(159,146,216,.2);
}

/* ─── Responsive pricing ─── */
@media (max-width: 600px) {
  .pricing-box { grid-template-columns: 1fr; }
  .free-upgrade-banner { flex-direction: column; align-items: flex-start; gap: 8px; }
}

/* ─── Confetti micro-animation ─── */
.confetti-particle {
  position: fixed;
  pointer-events: none;
  z-index: 9998;
  user-select: none;
  animation: confettiFall 1.2s ease-out forwards;
}

@keyframes confettiFall {
  0%   { transform: translateY(0) scale(1) rotate(0deg); opacity: 1; }
  60%  { opacity: 1; }
  100% { transform: translateY(-80px) scale(0.5) rotate(40deg); opacity: 0; }
}

/* ─── Year dropdown ─── */
#yearDropdown button:first-child { border-radius: 14px 14px 0 0; }
#yearDropdown button:last-child  { border-radius: 0 0 14px 14px; }

/* ─── v7 — Actions groupées dans l'en-tête d'une carte ───
   Sur mobile, deux boutons côte à côte comprimaient le titre sur trois lignes :
   on les repasse en pleine largeur sous le titre. */
.card-head-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

@media (max-width: 560px) {
  .card-head { flex-direction: column; align-items: stretch; }
  .card-head-actions { width: 100%; }
  .card-head-actions .btn { flex: 1 1 auto; justify-content: center; }
}

/* ─── v7 — Barre de filtres des transactions sur petit écran ───
   `width: max-content` empêchait la barre de se réduire : à 375 px elle
   dépassait de 17 px et faisait glisser toute la page horizontalement.
   On la contient dans la largeur disponible, avec défilement interne en
   dernier recours (et sans barre de défilement visible). */
@media (max-width: 560px) {
  .segmented {
    max-width: 100%;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .segmented::-webkit-scrollbar { display: none; }
  .seg-btn { padding: 8px 11px; font-size: 13px; white-space: nowrap; }
}

/* ═══════════════════════════════════════════════════════
   v7 — Champs de saisie de date : anti-débordement
   ═══════════════════════════════════════════════════════
   Sur iOS/Safari, <input type="date"> se dimensionne sur son widget natif
   et ignore width:100% : le champ dépassait la largeur du formulaire.
   Trois protections complémentaires :
     1. les cellules de grille/flex peuvent rétrécir (min-width:0 —
        par défaut un élément de grille refuse de passer sous sa largeur
        min-content, ce qui suffit à faire déborder toute la fenêtre) ;
     2. le champ lui-même est borné et son habillage natif neutralisé ;
     3. les sous-éléments WebKit du widget de date sont remis à plat.
   ═══════════════════════════════════════════════════════ */

/* 1 — Autoriser le rétrécissement des cellules */
.form-grid > *,
.field { min-width: 0; }
.field > .input,
.field > div { min-width: 0; max-width: 100%; }

/* 2 — Le champ ne dépasse jamais son conteneur */
.input[type="date"],
.input[type="month"],
.input[type="time"],
.input[type="datetime-local"] {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  min-height: 52px;          /* iOS réduit sinon la hauteur — on s'aligne sur les autres champs */
  font-family: inherit;
  font-size: 16px;           /* < 16px déclenche le zoom automatique sur iOS */
  background-color: #fff;
}

/* 3 — Habillage interne du widget WebKit */
.input[type="date"]::-webkit-date-and-time-value,
.input[type="month"]::-webkit-date-and-time-value,
.input[type="time"]::-webkit-date-and-time-value,
.input[type="datetime-local"]::-webkit-date-and-time-value {
  text-align: left;
  margin: 0;
  min-width: 0;
}
.input::-webkit-datetime-edit { padding: 0; overflow: hidden; }
.input::-webkit-datetime-edit-fields-wrapper { padding: 0; }
.input::-webkit-calendar-picker-indicator {
  margin: 0 0 0 auto;
  flex-shrink: 0;
  cursor: pointer;
}

/* Filet de sécurité : la fenêtre de dialogue ne défile jamais horizontalement */
.dialog { max-width: 100%; }

/* ─── v7 — Très petits écrans (iPhone SE, 320 px) ───
   La grille de statistiques et la barre de filtres dépassaient encore
   de quelques pixels, ce qui rendait toute la page glissante latéralement. */
@media (max-width: 360px) {
  .stats-grid  { gap: 8px; }
  .stat-card   { padding: 14px; border-radius: 22px; min-width: 0; }
  .stat-top    { gap: 8px; }
  .stat-ico    { width: 36px; height: 36px; }
  .stat-value  { font-size: 14px; }
  .seg-btn     { padding: 8px 9px; font-size: 12px; }
}

/* ═══════════════════════════════════════════════════════
   v7 — Fenêtres de dialogue adaptées à toutes les hauteurs d'écran
   ═══════════════════════════════════════════════════════
   Un formulaire qui s'allonge (case « Régulier » cochée, par exemple)
   dépassait la hauteur visible : impossible de faire défiler ni d'atteindre
   les boutons du bas. Désormais :
     · la fenêtre ne dépasse jamais la hauteur de l'écran ;
     · son contenu défile tout seul ;
     · le titre reste en haut, les boutons d'action restent visibles en bas.
   `dvh` tient compte de la barre d'adresse mobile qui se replie ;
   `vh` sert de repli pour les navigateurs plus anciens.
   ═══════════════════════════════════════════════════════ */

.dialog-backdrop {
  overflow-y: auto;                    /* filet de sécurité si la fenêtre déborde encore */
  -webkit-overflow-scrolling: touch;
}

.dialog {
  display: flex;
  flex-direction: column;
  margin: auto;                        /* reste centrée quand elle tient dans l'écran */
  max-height: calc(100vh - 36px);
  max-height: calc(100dvh - 36px);
}

.dialog-head { flex-shrink: 0; }

.dialog-body {
  flex: 1 1 auto;
  min-height: 0;                       /* indispensable pour que le défilement s'active */
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;        /* le défilement ne « fuit » pas vers la page */
  /* marge négative + padding : l'ombre de focus des champs n'est pas rognée */
  margin: -6px;
  padding: 6px;
}

/* Les boutons d'action restent collés en bas de la zone défilante */
.dialog-body > .form-actions {
  position: sticky;
  bottom: -6px;
  background: #fff;
  padding: 12px 0 6px;
  margin-top: 18px;
  z-index: 2;
}
/* Léger dégradé pour signaler qu'il reste du contenu au-dessus des boutons */
.dialog-body > .form-actions::before {
  content: '';
  position: absolute; left: 0; right: 0; top: -18px; height: 18px;
  background: linear-gradient(to bottom, rgba(255,255,255,0), #fff);
  pointer-events: none;
}

@media (max-width: 1024px) {
  .dialog-backdrop { padding: 12px; align-items: flex-end; }
  .dialog {
    max-height: calc(100vh - 24px);
    max-height: calc(100dvh - 24px);
  }
  /* Zone de défilement confortable au pouce, boutons en pleine largeur */
  .dialog-body > .form-actions { flex-wrap: wrap; }
  .dialog-body > .form-actions .btn { flex: 1 1 auto; justify-content: center; }
}

/* ═══════════════════════════════════════════════════════════════════
   v9 — Lot 3 partie B : sélecteurs de période et filtrage au clic
   ═══════════════════════════════════════════════════════════════════ */

/* Bandeau d'information neutre (variante manquante de .alert) */
.alert-info { background: var(--lavender-soft); color: var(--purple); }

/* Bascule Ce mois / Année sur la carte du solde prévisionnel */
.hero-scope {
  position: relative; z-index: 2;
  display: inline-flex; gap: 4px; margin-top: 14px;
  background: rgba(255, 255, 255, .22); border-radius: 999px; padding: 4px;
}
.hero-scope-btn {
  border: none; background: transparent; color: #fff;
  border-radius: 999px; padding: 6px 14px;
  font: 700 13px inherit; font-family: inherit; cursor: pointer;
  opacity: .8; transition: all .15s;
}
.hero-scope-btn:hover  { opacity: 1; }
/* v20.10 — POINT 4 : « ANNÉE » SE VOIT MÊME QUAND IL N'EST PAS CHOISI.
   Le bouton inactif était totalement transparent : seul un mot blanc
   flottait sur le dégradé, et rien ne disait qu'il était cliquable. Il
   reçoit maintenant sa propre pastille blanche à 20 %, discrète mais
   dessinée. Le voile du conteneur est retiré : deux voiles superposés
   donneraient trois niveaux de blanc au lieu de deux. */
/* v21 — LE VOILE DU BOUTON INACTIF PASSE DU BLANC AU PRUNE.
   Mesuré : un voile BLANC à 20 % éclaircit le dégradé sous le libellé,
   et le blanc du texte n'y valait plus que 2,44:1 — moins bien qu'avant
   la v20.10, où le bouton était transparent (3,15). Un voile PRUNE à
   22 % assombrit au contraire : le texte monte à 4,15–4,23:1, et la
   pastille reste exactement aussi visible qu'avec le voile blanc
   (écart perçu au dégradé : 7,1 contre 6,9). */
.hero-scope { background: transparent; padding: 0; gap: 6px; }
.hero-scope-btn { background: rgba(58, 46, 69, .22); opacity: 1; }
.hero-scope-btn:hover { background: rgba(58, 46, 69, .30); }
.hero-scope-btn.active { background: #fff; color: var(--purple); opacity: 1; }

/* Titre de section cliquable (filtrage — point 3.4) */
.sec-filter-btn {
  border: none; background: none; padding: 0; margin: 0;
  font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit;
  cursor: pointer; transition: color .15s;
}
.sec-filter-btn:hover  { color: var(--purple); text-decoration: underline; }
.sec-filter-btn.active { color: var(--purple); text-decoration: underline; }

/* Rappel du filtre actif, avec bouton de réinitialisation */
.tx-active-filter {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  flex-wrap: wrap; margin: 0 0 12px;
  background: var(--lavender-soft); border: 1px solid var(--lavender);
  border-radius: 14px; padding: 10px 14px; font-size: 13px; color: var(--purple);
}
.tx-active-filter .btn { flex-shrink: 0; }

/* Vue Jour : champ date intégré au bandeau de navigation */
.tx-day-pick {
  display: inline-block; width: auto; min-width: 0; max-width: 100%;
  margin-top: 6px; padding: 6px 10px; font-size: 14px; min-height: 0;
}

/* Vue Année : regroupement par mois */
.tx-year-month + .tx-year-month { margin-top: 6px; }
.tx-month-jump {
  display: flex; align-items: center; gap: 8px; width: 100%;
  border: none; background: none; cursor: pointer;
  padding: 12px 2px 6px; margin: 0;
  font: 700 14px inherit; font-family: inherit; color: var(--text); text-align: left;
  border-bottom: 1px solid var(--line);
}
.tx-month-jump:hover { color: var(--purple); }
.tx-month-bal { margin-left: auto; font-weight: 800; }

/* Tableaux récapitulatifs cliquables */
.tx-year-table tbody tr:hover,
.fc-month-table tbody tr:hover { background: var(--lavender-soft); }

/* Mobile : les barres de période et de filtres restent lisibles */
@media (max-width: 560px) {
  .segmented.tx-period { width: 100%; justify-content: space-between; }
  .segmented.tx-period .seg-btn { flex: 1 1 0; padding: 8px 6px; }
  .month-title { font-size: 20px; }
  .tx-active-filter { font-size: 12px; }
  .tx-active-filter .btn { width: 100%; justify-content: center; }
}

/* Détail du solde prévu (2 colonnes, pas les 4 du tableau Prévu/Réel/Écart) */
.solde-detail .bc-row { grid-template-columns: 1fr auto; }
@media (max-width: 600px) {
  .solde-detail .bc-row { grid-template-columns: 1fr auto; }
}

/* ═══════════════════════════════════════════════════════════════════
   v10 — Lot 4 : motif cœur, animations douces, message multi-appareils
   ═══════════════════════════════════════════════════════════════════ */

/* ─── 4.3 — Pétales et cœurs flottants (landing) ─── */
.petales {
  position: absolute; inset: 0;
  overflow: hidden; pointer-events: none; z-index: 0;
}
.petale {
  position: absolute; top: -8%;
  font-size: 20px; line-height: 1;
  color: var(--lavender); opacity: 0;
  animation: petale-chute 18s linear infinite;
}
.petale:nth-child(1) { left:  6%; font-size: 16px; color: var(--rose); animation-delay:  0s;   animation-duration: 19s; }
.petale:nth-child(2) { left: 17%; font-size: 22px; color: var(--lavender); animation-delay:  2.4s; animation-duration: 22s; }
.petale:nth-child(3) { left: 29%; font-size: 14px; color: var(--rose); animation-delay:  5.1s; animation-duration: 17s; }
.petale:nth-child(4) { left: 41%; font-size: 19px; color: var(--lavender); animation-delay:  7.8s; animation-duration: 24s; }
.petale:nth-child(5) { left: 53%; font-size: 16px; color: var(--rose); animation-delay: 10.2s; animation-duration: 20s; }
.petale:nth-child(6) { left: 65%; font-size: 24px; color: var(--lavender); animation-delay: 12.6s; animation-duration: 23s; }
.petale:nth-child(7) { left: 77%; font-size: 15px; color: var(--rose); animation-delay: 15.0s; animation-duration: 18s; }
.petale:nth-child(8) { left: 87%; font-size: 20px; color: var(--lavender); animation-delay:  3.6s; animation-duration: 21s; }
.petale:nth-child(9) { left: 95%; font-size: 14px; color: var(--rose); animation-delay:  9.0s; animation-duration: 25s; }

@keyframes petale-chute {
  0%   { transform: translateY(0)      rotate(0deg)   scale(.9); opacity: 0;   }
  8%   {                                                          opacity: .45; }
  50%  { transform: translateY(46vh)   rotate(140deg) scale(1);   opacity: .38; }
  92%  {                                                          opacity: .06; }
  100% { transform: translateY(96vh)   rotate(300deg) scale(.85); opacity: 0;   }
}

/* Respect du réglage système « réduire les animations » */
@media (prefers-reduced-motion: reduce) {
  .petales { display: none; }
  .anim-up, .anim-in { animation: none; }
}

/* ─── 4.4 — Message « ordinateur ET mobile » ─── */
.multi-appareils {
  display: inline-flex; align-items: center; gap: 10px;
  margin: 18px auto 0; padding: 11px 20px;
  background: var(--lavande);
  border: 1.5px solid var(--lavender);
  border-radius: 999px;
  font-size: 14px; font-weight: 600; color: var(--violet);
  box-shadow: 0 4px 16px rgba(109,74,160,.08);
  max-width: 100%;
}
.multi-appareils strong { font-weight: 800; }
.multi-appareils-ico { font-size: 17px; line-height: 1; }
.multi-appareils-ico + .multi-appareils-ico { margin-left: -6px; }

@media (max-width: 620px) {
  .multi-appareils {
    display: flex; border-radius: 20px; text-align: left;
    font-size: 13px; padding: 12px 16px; align-items: flex-start;
  }
  .multi-appareils-ico { margin-top: 1px; }
}

/* Variante intégrée à l'application (carte, pas pilule) */
.multi-appareils-app {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 18px; margin-bottom: 14px;
  background: linear-gradient(135deg, var(--lavande), var(--purple-soft));
  border: 1.5px solid var(--lavender);
  border-radius: 20px;
  font-size: 13px; font-weight: 600; color: var(--encre-mauve);
  line-height: 1.5;
}
.multi-appareils-app .emo { font-size: 22px; line-height: 1; flex-shrink: 0; }

/* v10 — en-têtes de carte compacts : les deux listes déroulantes du donut
   restent côte à côte au lieu de passer à la ligne l'une sous l'autre. */
.card-head-actions { flex-shrink: 0; }
.card-head-actions .select { padding: 8px 11px; font-size: 13px; }
@media (min-width: 1025px) {
  .chart-grid .card-head-actions { flex-wrap: nowrap; }
}

/* ═══════════════════════════════════════════════════════════════════
   v12 — POINT 4 : bloc « Plus de détails » des formulaires de saisie
   ═══════════════════════════════════════════════════════════════════
   Les champs secondaires (date, montant prévu, récurrence) sont repliés
   par défaut pour alléger la première saisie. Ils restent présents dans
   la page : rien n'est supprimé, seulement masqué.
   ═══════════════════════════════════════════════════════════════════ */

.adv-fields {
  grid-column: 1 / -1;
  margin-top: 2px;
  border-top: 1px dashed var(--line, var(--purple-soft));
  padding-top: 12px;
}

.adv-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  list-style: none;              /* masque la flèche native */
  font-size: 13.5px;
  font-weight: 700;
  color: var(--accent, var(--mauve-fonce));
  padding: 7px 10px;
  margin: 0 -10px;
  border-radius: 10px;
  user-select: none;
  transition: background .15s;
}
.adv-summary::-webkit-details-marker { display: none; }   /* Safari */
.adv-summary:hover { background: var(--lavender-soft, var(--lavande)); }

/* Repère visuel au clavier — l'élément reste pleinement accessible. */
.adv-fields > summary:focus-visible {
  outline: 2px solid var(--accent, var(--mauve-fonce));
  outline-offset: 2px;
}

.adv-chevron {
  display: inline-block;
  font-size: 17px;
  line-height: 1;
  transition: transform .18s ease;
}
.adv-fields[open] .adv-chevron { transform: rotate(90deg); }

.adv-hint {
  font-weight: 400;
  font-size: 12px;
  color: var(--muted, var(--encre-3));
  margin-left: auto;
}
@media (max-width: 420px) {
  .adv-hint { display: none; }   /* place limitée sur petit écran */
}

.adv-inner { margin: 12px 0 0 !important; }

/* Respecte le réglage système « moins d'animations ». */
@media (prefers-reduced-motion: reduce) {
  .adv-chevron { transition: none; }
}

/* ═══ v13 — pas de zoom automatique à la saisie sur mobile ═════════
   iOS agrandit la page dès qu'on touche un champ dont le texte fait
   moins de 16 px, puis laisse l'écran décalé. Deux menus déroulants de
   l'accueil étaient à 13 px : il suffisait de les toucher pour que tout
   se déplace. 16 px est la taille minimale qui évite ce zoom ; elle ne
   s'applique qu'aux écrans tactiles étroits, l'affichage sur ordinateur
   est inchangé. */
@media (max-width: 1024px) {
  /* v20 — « !important » est ici volontaire, et c'est le seul endroit du
     fichier où il l'est.

     RAISON : plusieurs champs portent leur taille EN LIGNE
     (style="font-size:14px"), et un style en ligne l'emporte sur
     n'importe quelle règle de feuille de style — sauf celle-ci. Sans
     « !important », la règle ci-dessous ne s'appliquait donc PAS à ces
     champs-là, et le zoom revenait dès qu'on les touchait : champ des
     seuils de l'écran Alertes (14 px), champs numériques et zones de
     texte des modèles d'email du back-office (12 et 13 px).

     Ce n'est pas un contournement de confort : il s'agit d'une
     contrainte de la plateforme. iOS agrandit la page dès qu'on touche
     un champ dont le texte fait moins de 16 px, puis laisse l'écran
     décalé. 16 px est la seule taille qui l'évite.

     Portée : sous 1024 px de large — téléphone en portrait (390 px) ET
     en paysage (844 px), et tablette (768 à 1024 px). Le paysage compte
     autant que le portrait : c'est au-delà de 780 px que la protection
     tombait dans les versions antérieures. Au-delà de 1024 px on est sur
     un ordinateur, où iOS n'impose rien, donc
     uniquement sur mobile et petites tablettes. L'affichage sur
     ordinateur n'est pas touché. */
  input, select, textarea, .input, .select { font-size: 16px !important; }
  /* Les deux menus de l'accueil (« Mensuel / Annuel » et le mois) sont
     réglés à 13 px par une règle plus précise ; il faut la neutraliser
     explicitement — et c'est pourquoi ce bloc est placé en toute fin de
     fichier, sinon eux seuls continueraient de provoquer le zoom. */
  .card-head-actions .select { font-size: 16px !important; }
}

/* v13 — écrans les plus étroits (≤ 380 px) : on resserre la rangée du
   haut pour qu'elle tienne sur une seule ligne malgré le bouton
   « Installer ». Le passage à la ligne réglé plus haut reste le filet
   de sécurité. */
@media (max-width: 380px) {
  .top-actions { gap: 4px; }
  .year-pill   { padding: 8px 9px; }
  /* Le bouton d'installation garde son icône mais perd son libellé :
     c'est ce qui permet à la rangée de tenir sur une seule ligne. Le
     grand encart « Installer Budgo sur votre mobile » de l'accueil, lui,
     reste entièrement lisible. */
  #topbarInstallBtn span { display: none; }
  #topbarInstallBtn      { padding: 6px 9px; }
}

/* ═══════════════════════════════════════════════════════════════════
   v22 — TÂCHES B & J : accompagnement des premiers pas
   ───────────────────────────────────────────────────────────────────
   Deux animations discrètes, jamais simultanées (la décision est prise
   dans app.js, fonction animationOnboarding()). Elles n'agissent que
   sur l'ombre : aucune taille ne change, donc aucun décalage de la
   page. Ajoutées EN FIN DE FICHIER, comme toutes les règles récentes.
   ═══════════════════════════════════════════════════════════════════ */
@keyframes budgo-halo {
  0%, 100% { box-shadow: 0 18px 34px rgba(223,127,139,.34), 0 0 0 0 rgba(223,127,139,.42); }
  55%      { box-shadow: 0 18px 34px rgba(223,127,139,.34), 0 0 0 14px rgba(223,127,139,0); }
}
.fab.fab-onboarding { animation: budgo-halo 2.6s ease-in-out infinite; }

.fab-hint {
  position: fixed; right: 22px; bottom: 94px; z-index: 10;
  max-width: 200px; text-align: right;
  background: #fff; color: var(--purple);
  border: 1.5px solid var(--lavender); border-radius: 14px;
  padding: 8px 12px; font-size: 13px; font-weight: 700; line-height: 1.35;
  box-shadow: 0 10px 26px rgba(124,92,180,.18);
  animation: budgo-apparait .5s ease both;
  pointer-events: none;
}
@keyframes budgo-apparait { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

@keyframes budgo-halo-lavande {
  0%, 100% { box-shadow: 0 2px 10px rgba(180,167,217,.18), 0 0 0 0 rgba(159,146,216,.38); }
  55%      { box-shadow: 0 2px 10px rgba(180,167,217,.18), 0 0 0 12px rgba(159,146,216,0); }
}
.install-onboarding > button { animation: budgo-halo-lavande 2.8s ease-in-out infinite; border-radius: 18px; }

.install-anim-close {
  position: absolute; top: -10px; right: 2px; z-index: 2;
  width: 26px; height: 26px; border-radius: 50%;
  border: 1.5px solid var(--lavender); background: #fff; color: var(--muted);
  font-size: 13px; line-height: 1; cursor: pointer;
  box-shadow: 0 4px 12px rgba(124,92,180,.16);
}
.install-anim-close:hover { color: var(--purple); }

/* Un téléphone réglé sur « réduire les animations » n'en voit aucune :
   la mise en avant reste, le clignotement disparaît. */
@media (prefers-reduced-motion: reduce) {
  .fab.fab-onboarding,
  .install-onboarding > button,
  .fab-hint { animation: none; }
}

/* Le micro-texte suit le bouton « + » quand celui-ci remonte au-dessus
   de la barre de navigation mobile (règle existante : .fab { bottom: 94px }). */
@media (max-width: 1024px) {
  .fab-hint { bottom: 164px; right: calc(18px + env(safe-area-inset-right, 0px)); }
}

/* v23 — TÂCHE J, correctif : la mise en avant du bouton d'installation
   porte désormais un message, comme le bouton « + ». Un halo seul
   pouvait passer inaperçu sur un écran lumineux.

   Le message est en ROSE BUDGO — la couleur du bouton « + » — et non
   en violet : sur un encart déjà lavande, un badge violet se fondait
   dans le décor au lieu d'attirer l'œil. */
.install-hint {
  position: absolute; top: -13px; left: 14px; z-index: 2;
  background: linear-gradient(135deg, var(--brand), var(--brand-dark));
  color: #fff;
  font-size: 12.5px; font-weight: 800; letter-spacing: .01em;
  padding: 6px 13px; border-radius: 999px;
  box-shadow: 0 6px 18px rgba(223,127,139,.42);
  animation: budgo-apparait .5s ease both;
}

/* ═══════════════════════════════════════════════════════════════════
   v24 — LE BOUTON D'INSTALLATION REVIENT DANS LA BARRE DU HAUT
   ───────────────────────────────────────────────────────────────────
   La grande carte du bas de l'accueil est supprimée : un seul point
   d'entrée, visible sans faire défiler la page.

   La pastille reprend le principe de mise en avant de la carte — halo
   qui pulse + message ROUGE PASTEL + croix pour l'éteindre —, mais en
   rose Budgo plutôt qu'en lavande : dans une barre déjà claire, un halo
   lavande se fondait dans le décor.

   Ajouté EN FIN DE FICHIER, comme toutes les règles récentes ; aucune
   règle existante n'est modifiée.
   ═══════════════════════════════════════════════════════════════════ */
.topbar-install-wrap { position: relative; display: inline-flex; }

/* UN SEUL point d'entrée par appareil : sur ordinateur, c'est le bouton
   au bas de la colonne de gauche qui joue déjà ce rôle ; la pastille du
   haut est donc réservée au mobile. (La règle « .mobile-only » générique
   ne suffit pas ici : elle est déclarée plus haut dans le fichier, donc
   perdante face à la ligne ci-dessus.) */
.topbar-install-wrap.mobile-only { display: none; }
@media (max-width: 1024px) {
  .topbar-install-wrap.mobile-only { display: inline-flex; }
}

.topbar-install {
  display: inline-flex; align-items: center; gap: 6px;
  background: linear-gradient(135deg, var(--red-soft), var(--rose-soft));
  border: 1.5px solid var(--brand); color: var(--brand-dark);
  border-radius: 999px; padding: 9px 14px;
  font-family: inherit; font-size: 13px; font-weight: 800; line-height: 1;
  white-space: nowrap; cursor: pointer;
  box-shadow: 0 2px 10px rgba(223,127,139,.20);
}
.topbar-install:hover { background: linear-gradient(135deg, var(--rose-soft), var(--rose-soft)); }

@keyframes budgo-halo-rose {
  0%, 100% { box-shadow: 0 2px 10px rgba(223,127,139,.20), 0 0 0 0 rgba(223,127,139,.45); }
  55%      { box-shadow: 0 2px 10px rgba(223,127,139,.20), 0 0 0 12px rgba(223,127,139,0); }
}
/* Plus spécifique que « .install-onboarding > button » : la pastille
   garde sa forme arrondie et prend le halo rose. */
.topbar-install-wrap.install-onboarding > .topbar-install {
  border-radius: 999px;
  animation: budgo-halo-rose 2.8s ease-in-out infinite;
}
/* La croix, elle, ne clignote pas : elle sert à faire cesser le
   clignotement, elle ne doit pas y participer. */
.topbar-install-wrap.install-onboarding > .install-anim-close {
  animation: none; top: -9px; right: -8px; width: 22px; height: 22px; font-size: 11px;
}

/* Le message se place SOUS la pastille — au-dessus, il serait coupé par
   le haut de l'écran — et pointe donc vers le haut. */
.install-hint-top {
  top: calc(100% + 8px); bottom: auto; left: auto; right: 0;
  white-space: nowrap; font-size: 11.5px; padding: 5px 11px;
}

@media (prefers-reduced-motion: reduce) {
  .topbar-install-wrap.install-onboarding > .topbar-install { animation: none; }
}

/* Écrans étroits : la pastille se resserre — mais garde son libellé,
   qui est ce qui rend le bouton compréhensible. Mesuré sur un écran de
   390 px : l'année, la cloche, la pastille, la déconnexion et l'avatar
   tiennent alors sur UNE seule ligne, à quelques pixels près. */
@media (max-width: 430px) {
  .topbar-install { padding: 8px 10px; font-size: 12.5px; gap: 5px; }
  .install-hint-top { font-size: 11px; padding: 5px 10px; }
}

/* En dessous de 380 px (iPhone SE et assimilés), la place manque
   vraiment : le libellé s'efface et l'icône reste seule, plutôt que de
   renvoyer l'avatar sur une deuxième ligne. */
@media (max-width: 379px) {
  .topbar-install > span { display: none; }
  .topbar-install { padding: 8px 11px; }
}

/* ═══════════════════════════════════════════════════════════════════
   v20 — COUCHE DE FINITION DU NOUVEAU SYSTÈME
   ───────────────────────────────────────────────────────────────────
   Cette couche est volontairement placée EN FIN DE FICHIER. Elle
   ajuste les formes, la typographie et les ombres par-dessus les
   règles existantes, au lieu de les réécrire une par une au milieu de
   1 900 lignes. Deux avantages : le changement se lit d'un bloc, et
   chaque règle d'origine reste en place — donc récupérable — si l'une
   d'elles devait finalement être conservée.
   ═══════════════════════════════════════════════════════════════════ */

/* ─── 1. Le fond de page ───────────────────────────────────────────
   Un dégradé, pas un aplat. Fixé en arrière-plan pour qu'il ne se
   répète pas quand la page est plus haute que l'écran. */
body {
  background: linear-gradient(170deg, var(--page-1), var(--page-2)) fixed;
  color: var(--encre);
}

/* ─── 2. TOUS les montants en Georgia ──────────────────────────────
   Le reste de l'interface demeure en sans-serif.

   v20.8 — DEUX CHANGEMENTS ICI, ET LE SECOND EST LE PLUS IMPORTANT.

   • Graisse 700, comme demandé. Lora supporte le gras bien mieux que
     Georgia aux grandes tailles : c'est justement pour cela qu'elle a
     été choisie.
   • « font-variant-numeric: tabular-nums » donne à tous les chiffres la
     MÊME largeur. Sans cette ligne, les chiffres de Lora sont
     proportionnels — le « 1 » mesure 378 unités quand le « 4 » en
     mesure 626 — et une colonne de montants ne s'alignerait pas d'une
     ligne à l'autre. Vérifié dans le fichier de police : Lora contient
     bien la fonction « tnum » nécessaire. */
.hero-value, .stat-value, .tx-amount, .proj-val, .proj-detail b,
.donut-center strong, .cat-item b, .montant, .bc-cell, .bc-total,
.solde-detail b, .kpi-value, td.num, .num {
  font-family: var(--police-montant);
  font-weight: 700;
  /* « lining-nums » réclame des chiffres alignés, « tabular-nums » des
     chiffres de largeur égale. La police système fournit les deux
     d'origine ; la demande explicite protège des polices de secours. */
  font-variant-numeric: lining-nums tabular-nums;
  font-feature-settings: "lnum" 1, "tnum" 1;
  letter-spacing: 0;
}

/* Un montant ne doit jamais déborder de son bloc, à aucune largeur :
   il rétrécit plutôt que de sortir du cadre. */
/* v20.8 — le plancher passe de 34 à 24 px. Lora en gras avec des
   chiffres tabulaires est plus large que Georgia en 500 : à 195 px de
   fenêtre — un téléphone de 390 px zoomé à 200 % — « 1 707,00 € » ne
   tenait plus sur la ligne à 34 px. Mesuré, corrigé, revérifié. Aux
   largeurs courantes le montant garde exactement la même taille : le
   plancher ne sert que dans ce cas extrême. */
.hero-value  { font-size: clamp(24px, 11vw, 54px); line-height: 1.05; }
/* v20.8 — LES MONTANTS DES TUILES SE COUPAIENT EN DEUX.
   Lora en gras avec des chiffres tabulaires est sensiblement plus large
   que Georgia en 500 : dans une tuile de 174 px, « 2 800,00 € » ne
   tenait plus sur une ligne et le « € » tombait tout seul en dessous —
   à 320, 360 et 390 px, donc sur la plupart des téléphones.
   Deux corrections : le montant ne se coupe JAMAIS (nowrap), et il
   rétrécit un peu plus tôt pour tenir. Contrôle ajouté à la suite de
   tests : un montant dont la hauteur dépasse une ligne et demie. */
.stat-value  { font-size: clamp(14px, 4.7vw, 20px); white-space: nowrap; }
.proj-val    { font-size: clamp(14px, 4.4vw, 18px); white-space: nowrap; }
.montant     { white-space: nowrap; }
/* v20.8 — le « nowrap » est réservé aux montants qui disposent d'une
   place garantie. Il avait d'abord été posé sur le montant d'une ligne
   de transaction aussi : à 195 px — un téléphone zoomé à 200 % — la
   ligne ne pouvait alors plus se replier et la page débordait de 23 px.
   Un montant de transaction a le droit de passer à la ligne : c'est
   toujours mieux qu'une page qui glisse latéralement. */
.hero-value { white-space: nowrap; }

/* ─── 3. Les formes ────────────────────────────────────────────────
   Cartes 20-22 px, blocs internes 12-16 px, pastilles pleinement
   arrondies, ronds d'icône de 34-36 px. */
.card, .bc-card, .stat-card, .hero-card {
  border-radius: var(--r);
  background: var(--carte);
  border: none;
  box-shadow: var(--ombre-carte);
}
.stat-card:hover { transform: none; box-shadow: var(--ombre-carte); }
.stat-ico {
  width: 34px; height: 34px; border-radius: var(--r-pill);
  background: var(--bloc);
}
.btn, .seg-btn, .hero-scope-btn, .year-pill, .tx-onglets button {
  border-radius: var(--r-pill);
}
.lbl { text-transform: none; letter-spacing: 0; font-weight: 600; color: var(--encre-3); font-size: 12px; }

/* Titres de carte : 15 px, graisse 500, encre foncée. Aucune capitale
   espacée nulle part — la règle est appliquée ici une fois pour toutes. */
.card-head h2, .card h2 { font-size: 15px; font-weight: 500; color: var(--encre); letter-spacing: 0; }
.card-head .small.muted, .card .small.muted { font-size: 13px; color: var(--encre-3); }

/* ─── 4. Le filet d'accent, sur le bord gauche des cartes ─────────
   Posé en pseudo-élément : aucune marge intérieure n'est modifiée,
   donc aucun texte ne se déplace. */
.card.filet, .bc-card.filet { position: relative; overflow: hidden; }
.card.filet::before, .bc-card.filet::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0;
  width: 4px; background: var(--filet); border-radius: var(--r-pill);
}

/* ─── 5. Le motif cœur en filigrane ───────────────────────────────
   Décoratif et rien d'autre : hors du flux, non cliquable, invisible
   des lecteurs d'écran. Sur fond clair il est rose ; sur le dégradé,
   blanc translucide. */
.motif-coeur {
  position: absolute; pointer-events: none; user-select: none;
  opacity: 1; z-index: 0;
}
.motif-coeur svg { display: block; }
/* Le contenu passe au-dessus du filigrane — mais le filigrane lui-même
   doit rester détaché du flux, sinon « right » et « top » ne
   s'appliquent plus et il se recale en haut à gauche. */
/* v20.10 — « .reste-coin » a disparu de cette liste en même temps que le
   pan mauve : la carte « À dépenser » est redevenue un rectangle uni. */
.card > *:not(.motif-coeur):not(.hero-motif),
.hero-card > *:not(.motif-coeur):not(.hero-motif) { position: relative; z-index: 1; }

/* ─── 6. La carte dégradée ────────────────────────────────────────*/
.hero-card {
  background: var(--degrade-principal);
  box-shadow: var(--ombre-principale);
  color: #fff; position: relative; overflow: hidden;
}
.hero-label { font-size: 13px; font-weight: 600; opacity: .96; }
.hero-scope-btn { font-weight: 600; }
.hero-scope-btn.active { background: #fff; color: var(--mauve-fonce); }

/* ─── 7. Adaptation aux écrans ────────────────────────────────────
   Quatre largeurs, et quatre seulement. Les points de rupture
   quasi-jumeaux d'origine (379/380, 420/430, 600/620) étaient des
   doublons accumulés au fil des versions ; ils sont ramenés ici sur
   les bornes canoniques.

   Le principe de fond : l'application est une COLONNE de cartes qu'on
   lit de haut en bas. Sur grand écran on ne l'étire pas — on plafonne
   la colonne de lecture et on met côte à côte ce qui se répond. */

/* Rien ne déborde : c'est l'oubli de « min-width: 0 » sur un enfant
   souple qui provoque l'essentiel des débordements horizontaux. */
.app-main, .card, .stat-card, .tx-row, .cat-item, .proj-mois, .bc-row {
  min-width: 0;
}
/* v63.7 — « anywhere » coupait les mots ordinaires (« Inscription »,
   « paiement ») et laissait les colonnes se réduire à rien.
   « break-word » garde la protection contre les adresses trop longues
   sans rien couper d'autre. Mesuré : 2 mots coupés → 1, URL toujours
   contenue dans les deux cas. */
.card, .stat-card { overflow-wrap: break-word; }
/* v63.7 — un libellé de bouton ne se coupe jamais en deux : c'est la
   rangée qui passe à la ligne, pas le mot. Constaté sur iPhone 390 px,
   « Rembourser » devenait « Rembour / ser ». max-width garde le bouton
   dans son conteneur si le libellé est vraiment trop long. */
.btn, button { overflow-wrap: normal; white-space: nowrap; max-width: 100%; }
/* Un tableau large défile dans son propre cadre, jamais la page. */
.table-scroll, .bc-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ─── Tablette : 768 à 1024 px ────────────────────────────────────
   CORRECTIF v20.1. La bande tablette commençait à 781 px, alors que
   le brief la fait commencer à 768 px : une tablette de 768 px de
   large — la plus répandue — tombait donc DANS la bande téléphone et
   s'affichait en pleine largeur, sans colonne centrée. Les treize
   pixels manquants suffisaient à annuler tout le traitement tablette.
   Les bornes sont désormais celles du brief, sans exception :
   téléphone jusqu'à 1024 px pour la MISE EN PAGE (menu du bas, boîtes
   de dialogue au pouce, champs à 16 px), et à partir de 768 px une
   colonne de 760 px centrée. Une tablette est un grand téléphone,
   pas un petit ordinateur — c'est ce qui a été validé. */
@media (min-width: 768px) and (max-width: 1024px) {
  .app-layout { max-width: 760px; margin: 0 auto; padding: 24px 20px 0; }
  .app-main   { width: 100%; padding: 0 0 100px; }
  /* Le menu du bas et le bouton « + » suivent la colonne : en position
     fixe ils se calent sur la FENÊTRE, pas sur le contenu, et se
     retrouveraient collés aux bords sur une tablette large. */
  .footer-nav {
    left: 50%; right: auto; transform: translateX(-50%);
    width: min(720px, calc(100% - 24px));
  }
  .fab { right: calc(50% - 380px + 18px); }
  .toast { left: 50%; right: auto; transform: translateX(-50%); width: min(720px, calc(100% - 28px)); }
}

/* Ordinateur : la colonne de lecture est plafonnée. La largeur qui
   reste sert à la barre latérale, pas à étirer les cartes. */
@media (min-width: 1025px) {
  /* v20.9 — 860 → 1280. Les blocs qui se répondent restent côte à côte
     (.duo-bureau, .chart-grid) : une carte plus large ne veut donc pas
     dire des lignes de texte interminables. Plafond à 1 600 px : au-delà,
     le bloc lui-même est plafonné et le reste se partage également des
     deux côtés. */
  .app-main { max-width: 1600px; }
  /* CORRECTIF v20.1 — et surtout : sur ordinateur, les blocs courts se
     répondent CÔTE À CÔTE au lieu de s'empiler sur toute la largeur.
     C'est la différence entre un écran conçu pour l'ordinateur et un
     mobile étiré. Le regroupement est porté par un conteneur dédié
     (.duo-bureau) : sur téléphone et tablette il reste une simple
     pile, donc rien ne change là-bas. */
  /* v20.3 — « align-items: start » laissait chaque carte à sa hauteur
     naturelle : « Reste à dépenser » faisait 168 px et « À payer dans
     6 jours » 52 px, côte à côte, et la rangée paraissait bancale.
     En « stretch », les deux cartes d'une rangée font la MÊME hauteur
     et leurs bords bas s'alignent. C'est la règle de base d'une grille
     propre : ce sont les rangées qui portent l'alignement, pas les
     contenus. */
  .duo-bureau {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 16px; align-items: stretch;
  }
  .duo-bureau > * { margin-top: 0 !important; margin-bottom: 0 !important; }
  /* Une carte étirée doit répartir son contenu, pas le laisser collé en
     haut d'un cadre trop grand. */
  .duo-bureau > .ligne-echeance { display: flex; align-items: center; }
}

/* Petits téléphones : les quatre tuiles restent en 2×2, resserrées. */
@media (max-width: 430px) {
  .stats-grid { grid-template-columns: 1fr 1fr; gap: 10px; }
  .stat-card  { padding: 13px 14px; border-radius: 20px; }
}
@media (max-width: 360px) {
  .stats-grid { gap: 8px; }
  .stat-card  { padding: 11px 12px; }
}

/* ─── 8. Réserve de bas de page ───────────────────────────────────
   v63.8 — MESURÉ, pas estimé. Trois éléments flottent au-dessus du
   contenu, et la réserve doit couvrir le PLUS HAUT des trois :
     · nav.footer-nav : bottom 12 px + 57 px de haut  →  69 px
     · .fab           : bottom 94 px + 62 px de haut  → 156 px  ← le pire
   L'ancienne réserve de 96 px n'avait été calculée que pour la barre
   du bas : le bouton « + » mordait donc sur 60 px de contenu, et ce
   contenu restait caché même page défilée à fond (dernier montant du
   prévisionnel, dernières lignes du tableau Premium, bouton
   « + Nouvelle alerte », sélecteurs du graphique…).
   172 px = 156 mesurés + 16 px de respiration, plus l'encoche du bas
   de l'iPhone (viewport-fit=cover est actif sur app.html). */
@media (max-width: 1024px) {
  .app-main { padding-bottom: calc(172px + env(safe-area-inset-bottom, 0px)); }
}
/* Sur ordinateur la barre du bas est masquée, MAIS le bouton « + »
   reste affiché à 24 px du bord sur 62 px de haut → 86 px à réserver.
   Il n'y avait aucune réserve : le dernier élément de chaque écran
   passait sous le bouton. 104 px = 86 mesurés + 18 px de respiration. */
@media (min-width: 1025px) {
  .app-main { padding-bottom: 104px; }
}

/* ═══════════════════════════════════════════════════════════════════
   v20 — LES BLOCS DE L'ACCUEIL
   ═══════════════════════════════════════════════════════════════════ */

/* La salutation : « Salut Prénom ♡ ». Le cœur est décoratif, il porte
   la couleur d'accent sans jamais remplacer un mot. */
.topbar h1 { font-family: var(--police-titre); font-weight: 500; letter-spacing: -.01em; }
.salut-coeur { color: var(--rose); font-size: .8em; vertical-align: middle; }
.topbar-sub  { color: var(--encre-3); font-size: 13px; }

/* ─── Carte « Reste à dépenser » ─── */
/* CORRECTIF v20.2 — cette carte touchait la carte dégradée du dessus.
   Elle n'a jamais eu de marge propre : jusqu'ici elle héritait de
   l'espacement d'un voisin, et le regroupement « .duo-bureau » a
   supprimé ce voisinage. La marge est donc portée par le groupe
   lui-même, à toutes les largeurs — 16 px, comme les autres blocs de
   l'accueil. Les marges d'un enfant fusionnent avec celles de ce
   conteneur : rien ne s'additionne sur l'écran Prévision. */
/* v21.4 — deux cartes qui se suivent gardent toujours un pas entre
   elles : l'audit en trouvait qui se touchaient (0 px) sur Prévision
   et sur Premium. */
.app-main .card + .card,
.app-main .card + .bc-card,
.app-main .duo-bureau + .card { margin-top: var(--pas-bloc); }

/* v20.3 — UN SEUL PAS VERTICAL : 16 px.
   Les blocs de l'accueil s'espaçaient de 12, 14, 16 puis 18 px, valeurs
   accumulées version après version. L'œil ne lit pas « 14 » ou « 18 »,
   mais il voit que les intervalles ne se répondent pas. Tous les blocs
   de l'accueil — tuiles, graphiques, « Où part votre argent »,
   « Prochaine échéance », groupes côte à côte — sont désormais à 16 px,
   et les gouttières des grilles aussi. */
.duo-bureau { margin-top: 16px; }
.carte-reste { position: relative; overflow: hidden; padding: 12px 15px; }

/* ═══ v20.10 — POINT 3 : LE RECTANGLE UNI ════════════════════════════
   Le pan mauve coupé en biais (« .reste-coin ») a été retiré : la carte
   est un rectangle crème uni, comme toutes les autres.

   Ce qui NE change pas — et c'est l'essentiel :

   1. LA HAUTEUR. Le gain de la v20.7 est conservé. Ce n'était pas le
      biais qui faisait la hauteur, mais la mise en ligne : le rythme
      quotidien reste logé à DROITE du montant, sur la même ligne, et
      non dans un bloc supplémentaire en dessous.

   2. LA LARGEUR RÉSERVÉE. « .reste-jour » garde sa largeur minimale.
      Sans elle, la colonne se recale sur la longueur du texte et les
      deux lignes glissent d'un mois à l'autre — « 103 €/jour » ne
      commencerait pas au même endroit que « 1 246 €/jour ».

   3. LA COULEUR DU LIBELLÉ. « .reste-jour-lib » garde l'encre
      secondaire (#5F5566). Elle avait été choisie pour tenir le
      contraste sur le pastel ; sur le crème elle monte à 7,4 pour 1,
      et reste lue comme un texte discret à côté du chiffre coloré.
   ═══════════════════════════════════════════════════════════════════ */
.reste-rang { display: flex; align-items: flex-end; gap: 10px; }
.reste-libelle { font-size: 13px; color: var(--encre-3); }
.reste-montant {
  font-family: var(--police-montant); font-weight: 700; font-variant-numeric: tabular-nums;
  font-size: clamp(24px, 7.6vw, 30px); line-height: 1.1; margin: 1px 0 0;
}
.reste-positif { color: var(--mauve-fonce); }
.reste-vide    { color: var(--rose-negatif); }

.reste-jour { margin-left: auto; text-align: left; min-width: 108px; }
.reste-jour-val {
  font-family: var(--police-montant); font-weight: 700; font-variant-numeric: tabular-nums;
  font-size: 19px; line-height: 1.15; color: var(--mauve-fonce); white-space: nowrap;
}
.reste-jour-lib { font-size: 11.5px; color: var(--encre-2); margin-top: 1px; white-space: nowrap; }

/* Petits écrans : la colonne de droite se resserre pour laisser au
   montant la place dont il a besoin ; en dessous de 340 px les deux
   informations se rangent l'une sous l'autre. */
@media (max-width: 380px) {
  .reste-jour { min-width: 96px; }
  .reste-jour-val { font-size: 17px; }
}
@media (max-width: 320px) {
  .reste-rang { flex-wrap: wrap; }
  .reste-jour { margin-left: 0; min-width: 0; margin-top: 6px; }
}

/* ═══ v20.10 — POINT 5 : LE « + » NE RECOUVRE PLUS AUCUN MONTANT ═════
   Le bouton « + » est FLOTTANT : il est fixé à l'écran, pas à la page,
   et reste au même endroit pendant qu'on fait défiler. Le déplacer ne
   règle donc rien — il recouvrirait simplement autre chose.

   La seule solution qui tienne à toutes les positions de défilement est
   de lui réserver un couloir. Sur un écran de 390 px, le bouton occupe
   les 62 px situés entre 18 et 80 px du bord droit ; le montant d'une
   catégorie s'arrête, lui, à 36 px du bord (14 px de marge de page +
   22 px de marge de carte). Le recouvrement est donc de 44 px.

   On réserve 70 px : les 44 px du recouvrement, plus 26 px d'air pour
   l'ombre portée du bouton et pour les écrans plus étroits, où la marge
   de page se réduit. Seule la liste des catégories est concernée — la
   colonne du libellé est élastique, elle se contente de se resserrer.

   La règle n'est PAS réservée au mobile : la mesure montre que sur
   ordinateur (1240 et 1440 px), où le bouton se cale sur la colonne de
   contenu, les montants passaient sous lui de 34 px. Le même couloir
   règle les deux cas. */
.donut-wrap .cat-list { padding-right: 70px; }
/* La réserve de la carte « Où part votre argent » est posée plus bas,
   sur la règle « .ou-ligne » elle-même : cette règle-là écrit le retrait
   en abrégé (« padding: 7px 0 »), ce qui remettrait à zéro une réserve
   déclarée ici. Mesuré : sans cette précaution, la correction n'avait
   aucun effet et les montants restaient recouverts de 49 px. */
@media (max-width: 340px) {
  /* Mesuré sur un écran de 320 px : 70 px font passer « Alimentation »
     sur deux lignes. 56 px dégagent encore le bouton de 12 px et
     laissent toutes les lignes entières. */
  .donut-wrap .cat-list { padding-right: 56px; }
}

/* Le cœur en filigrane, version claire */
.motif-coeur-clair { top: -10px; right: 8px; color: var(--brand-soft); opacity: .75; }
.motif-coeur-clair svg { width: 68px; height: 62px; }

/* ─── Ligne « Prochaine échéance » ─── */
.ligne-echeance {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 14px; margin-top: 16px;
  /* v20.4 — fond mauve pastel. Texte et icône passent à l'encre foncée :
     contraste mesuré 10,3:1, contre 5,7:1 avec l'encre secondaire. */
  background: var(--carte-echeance);
}
.echeance-ico {
  width: 30px; height: 30px; border-radius: 10px; background: var(--bloc);
  display: grid; place-items: center; color: var(--encre); flex: 0 0 30px;
}
.echeance-ico .icon { width: 16px; height: 16px; }
.echeance-texte  { font-size: 14px; color: var(--encre); min-width: 0; }
.echeance-texte strong { color: var(--encre); font-weight: 700; }
.echeance-montant { margin-left: auto; font-size: 15px; color: var(--encre); white-space: nowrap; }

/* ─── Bloc « Où part votre argent » ─── */
.carte-ou-part { margin-top: 16px; padding: 15px 17px; }
.ou-barre {
  display: flex; height: 14px; border-radius: var(--r-pill);
  overflow: hidden; gap: 2px; margin: 12px 0 4px;
}
.ou-barre i { display: block; height: 100%; }
.ou-barre .ou-var { flex: 1; }
.ou-fixe { background: var(--ou-mauve); }
.ou-var  { background: var(--ou-rose); }
.ou-ligne {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 0; font-size: 14px; color: var(--encre-2); min-width: 0;
}
/* v20.10 — POINT 5 : le couloir réservé au bouton « + » est posé sur la
   CARTE, et non sur les seules lignes de montants (voir le pavé « LE +
   NE RECOUVRE PLUS AUCUN MONTANT »). Sur les lignes seules, les
   montants reculaient de 70 px pendant que la barre colorée, elle,
   gardait toute la largeur : le décalage se voyait. Posé sur la carte,
   le retrait vaut pour le titre, la barre et les deux lignes — tout
   s'arrête au même endroit et la réserve ne se remarque pas.
   Cette règle vient APRÈS « .carte-ou-part » ci-dessus, qui écrit son
   retrait en abrégé : dans l'autre ordre, elle serait sans effet. */
/* v22.6 — le couloir de 87 px réservé au bouton « + » est supprimé :
   sur l'image de référence du 28 août, la barre court sur TOUTE la
   largeur de la carte. La carte garde son retrait propre (15px 17px).
   Conséquence assumée : pendant le défilement, le bouton flottant peut
   passer au-dessus des deux montants de cette carte, comme il le fait
   déjà sur les tuiles. */
.ou-ligne > span:nth-child(2) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ou-ligne b { margin-left: auto; color: var(--encre); white-space: nowrap; }
.ou-pastille { width: 10px; height: 10px; border-radius: var(--r-pill); flex: 0 0 10px; }

/* ─── Les ronds d'icône des quatre tuiles ─── */
.sec-revenus  { background: var(--sec-revenus-soft);  color: var(--sec-revenus); }
.sec-depenses { background: var(--sec-depenses-soft); color: var(--sec-depenses); }
.sec-epargne  { background: var(--sec-epargne-soft);  color: var(--sec-epargne); }
.sec-credits  { background: var(--sec-credits-soft);  color: var(--sec-credits); }
.stat-card .stat-value { color: var(--encre); }

/* ═══════════════════════════════════════════════════════════════════
   v20 — L'ÉCRAN PRÉVISION
   ═══════════════════════════════════════════════════════════════════ */
.prev-entete h1 {
  font-family: var(--police-montant); font-weight: 500;
  font-size: clamp(22px, 6.5vw, 28px); color: var(--encre); letter-spacing: -.01em;
}
.prev-sous { font-size: 13px; color: var(--encre-3); margin-top: 1px; }

/* La bascule : deux moitiés dans une gouttière pleinement arrondie. */
.prev-bascule {
  display: flex; background: var(--carte); border-radius: var(--r-pill);
  padding: 4px; margin: var(--pas-bloc) 0; box-shadow: var(--ombre-carte);
}
.prev-bascule button {
  /* v20.5 — « min-width: 0 » est la correction, et elle est invisible à
     la lecture : par défaut un élément souple refuse de descendre sous
     la largeur de son texte. « Années suivantes » étant plus long que
     « Cette année », les deux boutons cessaient d'être égaux dès que la
     place manquait — mesuré à 195 px (soit un zoom à 200 % sur un
     téléphone) : 64 px contre 75. Avec « min-width: 0 » ils restent
     rigoureusement de même largeur, et le libellé passe sur deux lignes
     plutôt que d'imposer la sienne. */
  flex: 1; min-width: 0; padding: 10px 8px; border-radius: var(--r-pill);
  font-size: 14px; font-weight: 500; color: var(--encre-3);
  min-height: 40px; line-height: 1.2; transition: background .18s, color .18s;
}
@media (max-width: 340px) {
  .prev-bascule button { font-size: 12.5px; padding: 9px 4px; }
}
.prev-bascule button.actif {
  background: #fff; color: var(--mauve-fonce); font-weight: 700;
  box-shadow: var(--ombre-carte);
}

/* La carte dégradée de tête */
.prev-hero { padding: 18px 20px; margin-bottom: var(--pas-bloc); }
.prev-hero .hero-value { margin: 4px 0 6px; }
.prev-hero-sous { font-size: 13px; opacity: .92; font-weight: 500; }

/* Les listes de lignes — mois, années, postes */
.prev-liste { margin-top: 10px; }
.prev-mois, .prev-detail-ligne, .prev-tab-ligne {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 10px; border-bottom: 1px solid var(--line); min-width: 0;
}
.prev-liste > *:last-child { border-bottom: none; }
.prev-mois-nom  { font-weight: 700; color: var(--encre); }
.prev-mois-val  { margin-left: auto; color: var(--encre); white-space: nowrap; }
.prev-negatif   { color: var(--rose-negatif); }
.prev-mauve     { color: var(--encre-mauve); }   /* v21 — sur le bloc rosé, pas sur une carte */
/* v20.5 — largeur fixe : la flèche réserve sa place même quand elle est
   absente, sinon la colonne des montants se décale d'une ligne à l'autre. */
.prev-fleche    { font-size: 14px; flex: 0 0 16px; text-align: center; }
/* v21 — ces deux flèches sont posées sur le bloc rosé de la ligne du
   mois en cours, pas sur une carte : elles prennent l'encre des
   pastilles pâles, comme le montant à côté d'elles. */
.prev-haut      { color: var(--encre-mauve); }
.prev-bas       { color: var(--encre-rose); }
/* Le mois en cours est surligné, coins arrondis, sans filet dessous. */
.prev-encours {
  background: var(--bloc); border-radius: var(--r-sm); border-bottom-color: transparent;
}
.prev-encours .prev-mois-nom { color: var(--encre-mauve); }

.prev-lien {
  margin-top: 10px; padding: 8px 2px; min-height: 40px;
  color: var(--encre-mauve); font-size: 14px; font-weight: 500;
}

/* Le graphique en barres des années suivantes */
.prev-barres {
  display: flex; align-items: flex-end; gap: 10px;
  height: 104px; margin: 16px 0 4px; padding: 0 2px;
}
.prev-barre-col { flex: 1; display: flex; flex-direction: column;
                  align-items: center; justify-content: flex-end; min-width: 0; }
.prev-barre { width: 100%; border-radius: 8px 8px 4px 4px; }
.prev-barre-an { font-size: 11px; color: var(--encre-3); margin-top: 7px; }

/* Le détail du mois */
.prev-detail-ico {
  width: 34px; height: 34px; border-radius: var(--r-pill);
  display: grid; place-items: center; flex: 0 0 34px;
}
.prev-detail-ico .icon { width: 17px; height: 17px; }
.prev-detail-nom { font-size: 14px; color: var(--encre-2); min-width: 0; }
.prev-detail-ligne b, .prev-tab-ligne b { margin-left: auto; color: var(--encre); white-space: nowrap; }

.prev-total {
  display: flex; align-items: center; gap: 10px; margin-top: 12px;
  background: var(--bloc); border-radius: var(--r-sm); padding: 12px 14px;
  font-weight: 700; color: var(--encre);
}
.prev-total b { margin-left: auto; white-space: nowrap; }

/* Le tableau prévu / réel : deux colonnes, jamais trois. */
/* ═══ v21.4 — LES TROIS RANGÉES PARTAGENT ENFIN LEURS COLONNES ══════
   L'en-tête, les lignes et le total sont trois grilles DISTINCTES. Avec
   des colonnes « auto », chacune calculait la sienne d'après son propre
   contenu : aux largeurs confortables les nombres faisaient par chance
   la même largeur et tout s'alignait, mais à 320 px la colonne du
   libellé se comprime, les largeurs divergent et la colonne « Prévu »
   affichait quatre bords droits différents — 13 px de dérive, mesurés.
   Une largeur FIXE et partagée règle le problème par construction. */
.prev-tab-entete, .prev-tab-ligne, .prev-total-tab {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--col-montant) var(--col-montant);
  gap: 14px; align-items: center;
}
:root { --col-montant: 72px; }
@media (max-width: 360px) { :root { --col-montant: 66px; } }
.prev-tab-entete { padding: 10px 10px 4px; font-size: 12px; color: var(--encre-3); }
.prev-tab-entete b, .prev-tab-ligne b, .prev-total-tab b {
  min-width: 62px; text-align: right; font-weight: 500;
}
.prev-tab-entete b { font-weight: 600; }
.prev-tab-ligne > span:first-child { font-size: 14px; color: var(--encre-2); min-width: 0; }
.prev-total-tab { margin-top: 12px; padding: 12px 10px; }

/* Le solde de départ — un réglage, donc sobre. */
.prev-depart { display: flex; gap: 10px; margin-top: 12px; align-items: center; flex-wrap: wrap; }
.prev-depart .input { flex: 1 1 120px; min-width: 0; background: var(--bloc); border: none; }
.prev-depart .btn { flex: 0 1 auto; min-width: 0; background: var(--degrade-bouton); color: #fff; }
/* Sous 340 px, le champ et le bouton passent l'un sous l'autre plutôt
   que de se serrer jusqu'à l'illisible. */
@media (max-width: 340px) {
  .prev-depart .input, .prev-depart .btn { flex: 1 1 100%; justify-content: center; }
}

/* Le cœur de la carte dégradée de l'écran Prévision : cette carte est
   plus basse que celle de l'accueil, le filigrane s'y adapte pour ne
   pas devenir un aplat. */
.prev-hero .hero-motif { width: 104px; height: 95px; right: -4px; bottom: -20px; opacity: .34; }
.prev-hero .hero-motif-petit { display: none; }

/* Les cinq onglets de type doivent tenir SANS défilement à 390 px, et
   rester lisibles à 320 px. Mesuré : à 14 px de police et 9 px de
   marge intérieure, la rangée fait 358 px de large — elle tient. */
@media (max-width: 430px) {
  .tx-onglets .seg-btn { padding: 9px 9px; font-size: 13px; }
}
/* Sous 360 px, la rangée défile plutôt que de comprimer les libellés
   jusqu'à l'illisible — mais un dégradé sur le bord droit annonce
   qu'il y a quelque chose de plus à voir, ce qui manquait jusqu'ici. */
/* Sous 380 px, cinq onglets ne tiennent plus sur une ligne sans
   rendre les libellés illisibles. Ils passent donc sur DEUX lignes
   plutôt que de défiler : tout reste visible d'un coup d'œil, ce qui
   était précisément le reproche fait à l'ancienne rangée défilante. */
@media (max-width: 380px) {
  .segmented.tx-onglets { flex-wrap: wrap; overflow-x: visible; }
  .tx-onglets .seg-btn { flex: 1 1 30%; padding: 9px 6px; font-size: 12.5px; }
}

/* ═══════════════════════════════════════════════════════════════════
   v20 — LE DÉCOR ANIMÉ DE L'ARRIÈRE-PLAN
   ───────────────────────────────────────────────────────────────────
   Cœurs et fleurs, très lents, très pâles, derrière tout le contenu.

   POURQUOI CELA NE COÛTE RIEN, concrètement.
   Un navigateur redessine une page en trois étapes : calculer la mise
   en page, peindre les pixels, composer les couches. Seules deux
   propriétés — « transform » et « opacity » — se contentent de la
   TROISIÈME, qui est déportée sur le processeur graphique. C'est donc
   tout ce que cette animation touche : ni « top », ni « left », ni
   « margin », qui obligeraient à tout recalculer soixante fois par
   seconde. Le conteneur est par ailleurs en position fixe : le
   défilement de la page ne le concerne pas.

   POURQUOI CELA NE GÊNE PAS LA LECTURE.
   Les cartes de l'application sont opaques (#FEF9FB) et passent
   au-dessus : le décor n'apparaît que dans les espaces entre elles.
   Son opacité plafonne à 0,18 — quatre fois moins que les pétales de
   l'écran de connexion, où il n'y a rien à lire.
   ═══════════════════════════════════════════════════════════════════ */
.decor-fond {
  position: fixed; inset: 0;
  overflow: hidden; pointer-events: none; z-index: 0;
  /* Le décor ne doit jamais capter un appui ni un clic, ni apparaître
     dans la sélection de texte. */
  user-select: none; -webkit-user-select: none;
}
.decor-item {
  position: absolute; top: -6%;
  font-size: 22px; line-height: 1;
  color: var(--decor-rose); opacity: 0;
  animation: decor-chute 34s linear infinite;
  will-change: transform, opacity;
}
/* CORRECTIF v20.1 — LES DÉLAIS SONT NÉGATIFS, ET C'EST LE POINT CLÉ.
   Avec des délais positifs (0 s, 6 s, 12 s…), la page s'ouvre sur UN
   seul élément en train de tomber : les cinq autres attendent leur
   tour, invisibles, et il faut une demi-minute pour que le décor
   existe. C'est exactement ce que montraient les captures.
   Un délai NÉGATIF démarre l'animation « déjà commencée » : à la
   première image affichée, les six éléments sont déjà répartis sur
   toute la hauteur. Rien ne coûte plus cher, l'effet est là tout de
   suite — et une capture d'écran le montre. */
.decor-item:nth-child(1) { left:  7%; color: var(--decor-rose);  font-size: 20px; animation-delay:  -2s; animation-duration: 31s; }
.decor-item:nth-child(2) { left: 22%; color: var(--decor-mauve); font-size: 26px; animation-delay: -12s; animation-duration: 38s; }
.decor-item:nth-child(3) { left: 38%; color: var(--decor-rose);  font-size: 18px; animation-delay: -21s; animation-duration: 29s; }
.decor-item:nth-child(4) { left: 55%; color: var(--decor-mauve); font-size: 23px; animation-delay:  -7s; animation-duration: 36s; }
.decor-item:nth-child(5) { left: 71%; color: var(--decor-rose);  font-size: 19px; animation-delay: -27s; animation-duration: 33s; }
.decor-item:nth-child(6) { left: 88%; color: var(--decor-mauve); font-size: 24px; animation-delay: -17s; animation-duration: 40s; }
/* CORRECTIF v20.1 — les dix éléments s'affichent partout, téléphone
   compris. Six suffisaient tant que le décor était censé n'être qu'un
   soupçon ; une fois le fond opaque retiré, on constate que sur un
   écran de 390 px les cartes occupent presque toute la largeur et que
   le décor ne dispose que des intervalles : à six, il ne se croise
   presque jamais. À dix, il est présent sans être envahissant.
   Le coût a été remesuré sur un processeur ralenti 4× — la cadence est
   inchangée, ces éléments ne provoquent ni calcul de mise en page ni
   repeinte. */
.decor-item:nth-child(7)  { left: 14%; color: var(--decor-mauve); font-size: 21px; animation-delay: -30s; animation-duration: 35s; }
.decor-item:nth-child(8)  { left: 46%; color: var(--decor-rose);  font-size: 27px; animation-delay:  -5s; animation-duration: 32s; }
.decor-item:nth-child(9)  { left: 63%; color: var(--decor-mauve); font-size: 19px; animation-delay: -24s; animation-duration: 39s; }
.decor-item:nth-child(10) { left: 80%; color: var(--decor-rose);  font-size: 22px; animation-delay: -15s; animation-duration: 30s; }
/* (plus de distinction : les dix éléments sont visibles à toutes les largeurs) */

/* CORRECTIF v20.1 — l'opacité de crête passe de 0,18 à 0,40.
   Le chiffre n'est pas un ressenti, il est calculé. Écart perçu entre
   le décor et le fond rose (distance OKLab ×100) :
     0,18 → 4,4 pour le rose et 1,6 pour l'ancien lavande #DCC9F0.
   1,6, c'est en dessous du seuil où l'œil distingue deux teintes : la
   moitié du décor était littéralement invisible, pas seulement
   discrète. À 0,40 avec les deux teintes retenues, l'écart vaut 10,0
   et 10,2 — visible, et à comparer aux 61,4 du texte courant sur le
   même fond : le décor reste six fois plus effacé que la moindre
   ligne à lire. */
@keyframes decor-chute {
  0%   { transform: translate3d(0, -4vh, 0)  rotate(0deg)   scale(.85); opacity: 0;    }
  10%  {                                                                opacity: .34;  }
  50%  { transform: translate3d(14px, 48vh, 0) rotate(120deg) scale(1);  opacity: .40; }
  90%  {                                                                opacity: .12;  }
  100% { transform: translate3d(0, 104vh, 0) rotate(260deg) scale(.85); opacity: 0;    }
}

/* Le contenu passe au-dessus du décor, sans exception.
   ATTENTION : le menu du bas, le bouton « + » et les messages
   temporaires sont en position FIXE. Leur donner « position: relative »
   les remettrait dans le flux — le menu cesserait de coller au bas de
   l'écran. On ne leur touche donc QUE le plan de superposition. */
#app, .app-layout { position: relative; z-index: 1; }
.footer-nav, .fab { z-index: 12; }
/* ═══ CORRECTIF v37 — LE MESSAGE PASSE AU-DESSUS DES FENÊTRES ═══════
   « .toast » figurait dans la ligne ci-dessus, avec le menu du bas et
   le bouton « + ». Pour ces deux-là, z-index 12 est juste : une fenêtre
   ouverte DOIT les recouvrir.

   Un message, non. Il annonce le résultat de ce qu'on vient de faire —
   souvent depuis une fenêtre encore ouverte. À 12, il passait sous le
   voile (z-index 40) ET sous son « backdrop-filter: blur(4px) » :
   présent dans la page, mais flouté et inaccessible. Mesuré le
   05/09/2026 : z-index calculé 12, et elementFromPoint au centre du
   message renvoyait « dialog-backdrop ».

   Le fichier annonçait pourtant 100 à la ligne 555 ; cette règle-ci,
   plus bas et de même spécificité, l'écrasait sans le dire. On lui rend
   sa valeur, au-dessus du voile. */
.toast { z-index: 100; }
/* CORRECTIF v20.2 — `.dialog-backdrop` figurait dans la première ligne.
   C'est la faute même que le commentaire ci-dessus met en garde : le
   voile des fenêtres est en position FIXE et à z-index 40 ; lui donner
   « position: relative; z-index: 1 » le remettait dans le flux de la
   page. Résultat : toute fenêtre — « + », « Vues », le sélecteur
   d'année, le profil — s'ouvrait tout en bas du document, à 2 475 px
   sur un écran de 844 px, donc invisible. Les boutons paraissaient
   morts alors qu'ils fonctionnaient.
   Le voile n'a besoin d'AUCUNE règle ici : son z-index 40 le place
   déjà bien au-dessus du décor, qui est à 0. */

/* Réglage système « réduire les animations » : le décor s'efface
   entièrement — il est décoratif, rien n'est perdu. */
@media (prefers-reduced-motion: reduce) {
  .decor-fond { display: none; }
}

/* ═══ v20.3 — LE DÉCOR SUR ORDINATEUR : UNE QUESTION DE GÉOMÉTRIE ════
   Sur téléphone, les cartes occupent presque toute la largeur : le
   décor n'a que les interstices, et il y reste. Sur ordinateur, la
   situation est inverse — la lecture tient dans une colonne de 860 px
   au milieu d'un écran de 1 280, et l'espace libre n'est PAS vide :
   c'est là que vivent la salutation, le titre du mois, les intertitres
   « REVENUS », « DÉPENSES FIXES », le libellé « Catégories ». Un décor
   réparti sur toute la largeur tombe donc forcément sur ces textes, et
   sur les coins arrondis des cartes. Baisser l'opacité n'y change
   rien : ce n'est pas un problème d'intensité, c'est un problème de
   PLACE.

   La seule garantie solide est géométrique : sur ordinateur, le décor
   est enfermé dans la marge droite de la page — la bande qui reste à
   droite de la colonne de lecture, où rien n'est jamais dessiné. Le
   conteneur lui-même est en dehors du contenu et masque ce qui
   dépasse : aucun élément ne PEUT chevaucher un texte ou une carte.

   La largeur de cette marge se calcule à partir des mêmes constantes
   que la mise en page : 28 (marge) + 245 (barre latérale) + 24
   (gouttière) + 860 (colonne) = 1 157 px de contenu, et le bloc est
   centré au-delà de 1 400 px.
   En dessous de 1 240 px, cette marge fait moins de 80 px : il n'y a
   pas la place, et le décor ne s'affiche pas du tout plutôt que mal.
   Rien ne change sur téléphone ni sur tablette. */
@media (min-width: 1025px) and (max-width: 1239px) {
  .decor-fond { display: none; }
}
@media (min-width: 1240px) {
  .decor-fond {
    left: auto; right: 6px;
    width: calc(min(100vw - 1157px, 50vw - 457px) - 12px);
  }
  /* Cinq éléments suffisent dans une bande étroite ; à dix, ce serait
     un rideau. */
  .decor-item:nth-child(n+6) { display: none; }
  .decor-item { font-size: 20px; }
  .decor-item:nth-child(2) { font-size: 24px; }
  .decor-item:nth-child(4) { font-size: 22px; }
}

/* ═══ v20.2 — LA TENUE DU MENU DU BAS ═══════════════════════════════
   La barre est revenue à cinq entrées (la Prévision est retournée dans
   le menu « Vues »), mais ces règles restent : elles garantissent la
   tenue des libellés à toutes les largeurs, et laissent la place si une
   sixième entrée devait revenir. À 390 px, la barre occupe la largeur
   moins 12 px de marge de chaque côté, moins son propre rembourrage —
   soit 350 px utiles, donc 70 px par entrée à cinq (58 à six), pour un
   libellé le plus long de 42 px à 9 px de corps. Trois règles :
     1. les libellés ne se coupent JAMAIS en deux lignes (nowrap) —
        une barre à deux hauteurs différentes est illisible ;
     2. le rembourrage latéral se resserre au lieu du texte : réduire
        le corps sous 9 px le rendrait illisible sur un Android
        d'entrée de gamme ;
     3. sous 340 px (ou à 200 % de zoom), les icônes rétrécissent
        avant les mots.
   Vérifié par mesure à 320, 360, 390 et 430 px, à cinq comme à six :
   aucun libellé tronqué et aucun débordement. */
@media (max-width: 1024px) {
  .footer-nav { padding: 10px 4px; gap: 0; }
  .footer-nav button {
    padding: 0 1px; white-space: nowrap; letter-spacing: -.2px;
  }
  .footer-nav button span { display: block; max-width: 100%; }
}
@media (max-width: 360px) {
  .footer-nav { left: calc(8px + env(safe-area-inset-left, 0px)); right: calc(8px + env(safe-area-inset-right, 0px)); padding: 9px 2px; }
  .footer-nav button .icon { width: 17px; height: 17px; }
}
@media (max-width: 340px) {
  .footer-nav button { font-size: 8.5px; }
  .footer-nav button .icon { width: 16px; height: 16px; }
}

/* ═══ v20 — LES LARGEURS EXTRÊMES ═══════════════════════════════════
   Zoomer à 200 % sur un téléphone de 390 px revient, pour la page, à
   une fenêtre de 195 px. Trois éléments n'avaient jamais été prévus
   pour si peu — le bandeau Premium, le graphique en anneau, et les
   pastilles de la barre du haut. Ils sont bornés ici plutôt que dans
   trois endroits différents. */
.upgrade-pill, .upgrade-banner, .topbar, .top-actions {
  max-width: 100%; flex-wrap: wrap;
}
.upgrade-pill { white-space: normal; text-align: center; }
/* L'anneau ne réserve plus une colonne fixe : il prend la place
   disponible, sans jamais la dépasser. */
/* Sur téléphone l'anneau et sa légende restent EMPILÉS, comme avant :
   côte à côte, la légende se réduirait à une colonne où « Alimentation »
   se coupe en quatre. La mise côte à côte ne vaut qu'à partir de 601 px,
   exactement comme la règle d'origine le prévoyait. */
/* L'anneau et sa légende restent EMPILÉS à toutes les largeurs.
   Ils étaient côte à côte au-delà de 600 px, ce qui convenait quand la
   colonne principale s'étirait sur 1 100 px. Depuis qu'elle est
   plafonnée à 860 px et partagée en deux cartes, la carte du graphique
   fait environ 400 px : l'anneau en prend 190, et la légende se
   retrouve à couper « Alimentation » en deux lignes. Empilés, les deux
   respirent, à 320 px comme à 1 280. */
.donut-wrap { grid-template-columns: minmax(0, 1fr); justify-items: center; }
.donut-wrap .cat-list { width: 100%; }
.donut-wrap > * { min-width: 0; }
.donut-wrap svg { max-width: 100%; height: auto; }

@media (max-width: 300px) {
  .donut-wrap { grid-template-columns: minmax(0, 1fr); justify-items: center; }
  .stats-grid { grid-template-columns: 1fr; }
  .upgrade-pill { font-size: 12.5px; padding: 8px 12px; }
  .app-layout { padding: 12px 10px; }
  .card, .stat-card { padding: 12px 13px; }
}

/* L'anneau est dessiné avec une taille en pixels calculée en
   JavaScript, dans un conteneur de la même taille. Plutôt que de
   toucher au calcul — et de risquer un arc mal tracé —, on borne le
   conteneur en CSS : l'aspect carré est préservé, la largeur ne
   dépasse jamais celle de la carte. */
/* La piste de la grille doit pouvoir descendre SOUS la taille de son
   contenu — c'est ce que fait « minmax(0, …) ». Sans cela, la piste
   « 1fr » adopte la largeur de l'anneau (180 px) et le conteneur
   pousse la page : le maximum de 100 % se mesurait alors sur une piste
   dimensionnée par l'anneau lui-même, donc ne bornait rien. */
.donut-wrap > div:first-child {
  width: 100% !important; max-width: 190px;
  height: auto !important; aspect-ratio: 1 / 1;
}
.donut-wrap > div:first-child > svg { width: 100%; height: 100%; }
/* La bascule « Ce mois / Année » de la carte dégradée passe à la ligne
   plutôt que de dépasser, sur les fenêtres très étroites. */
.hero-scope { flex-wrap: wrap; max-width: 100%; }

/* Le tableau prévu / réel réserve 62 px par colonne de montant, ce qui
   convient à un téléphone mais dépasse dès que la fenêtre descend sous
   240 px — c'est-à-dire dès qu'on zoome à 200 %. Les colonnes cessent
   alors de réserver une largeur fixe. */
@media (max-width: 340px) {
  .prev-tab-entete, .prev-tab-ligne, .prev-total-tab { gap: 6px; }
  /* Les montants rétrécissent aussi : à cette largeur, deux colonnes de
     chiffres et un libellé ne tiennent pas autrement. */
  .prev-tab-entete b, .prev-tab-ligne b, .prev-total-tab b { font-size: 13px; }
  .prev-tab-ligne > span:first-child, .prev-total-tab > span:first-child { font-size: 13px; }
  .prev-tab-entete b, .prev-tab-ligne b, .prev-total-tab b { min-width: 0; }
  .prev-total-tab, .prev-tab-entete { padding-left: 6px; padding-right: 6px; }
  .prev-mois, .prev-detail-ligne, .prev-tab-ligne { padding-left: 4px; padding-right: 4px; }
}


/* ═══════════════════════════════════════════════════════════════════
   v22.6 — RETOUR À L'ESPACEMENT D'ORIGINE (image de référence du 28/08)
   ───────────────────────────────────────────────────────────────────
   Les blocs v22.2 à v22.5 (retrait du conteneur à 16 px, cartes à
   12 px) sont RETIRÉS : l'image que Manal a donnée comme référence
   d'espacement est, mesurée au pixel, celle des valeurs d'origine —
   carte du solde 20 px, tuiles 13/14 px, « Où part votre argent »
   15/17 px, 16 px entre les cartes, 10 px entre les tuiles.

   Mesures relevées sur l'image de référence (475 px de large) :
       carte du solde : contenu à 20 px du bord         → déjà le cas
       tuiles : icône à 15 px du bord                   → déjà le cas
       « Où part » : titre et barre à 20 px des bords   → déjà le cas
       barre de répartition : PLEINE LARGEUR            → seul écart
       intervalle entre tuiles : 10 px                  → déjà le cas
   La SEULE différence entre cette référence et les valeurs d'origine
   était donc le couloir de 87 px de « Où part votre argent », supprimé
   plus haut. Aucune autre règle n'est nécessaire : ne rien réécrire
   ici, c'est garantir que l'accueil est identique aux onze autres
   écrans dans son vocabulaire d'espacement.
   ═══════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════
   v23 — LOT DU 28 AOÛT (validé point par point avant développement)
   ───────────────────────────────────────────────────────────────────
   1. Accueil aux standards mobiles (l'ex-proposition v22.7, validée) :
      retrait intérieur UNIQUE de 16 px sur toutes les cartes, et
      quatre chiffres ramenés aux standards — solde 32 px, « 281 € »
      22 px, tuiles 17 px, centre de l'anneau 18 px. Le reste des
      textes est déjà aux standards, « Bonsoir Manal » reste à 22 px.
   2. « À payer dans X jours » : plus compact — moins haut, textes de
      13-14 px, icône plus petite.
   3. Tuiles de l'écran Transactions : un fond pastel de la charte par
      tuile, sans répétition.
   4. La carte « Mois par mois » (Prévision) devient cliquable : les
      lignes sont des boutons, remis à plat ici.
   L'accueil est ciblé par « :has() » et par le frère « ~ » (repli
   pour les navigateurs antérieurs à 2022). */
.app-main:has(> .hero-card:not(.prev-hero)) > .hero-card,
.app-main > .hero-card:not(.prev-hero) {
  padding: 16px !important;
}
.app-main:has(> .hero-card:not(.prev-hero)) .card,
.app-main:has(> .hero-card:not(.prev-hero)) .stat-card,
.app-main:has(> .hero-card:not(.prev-hero)) .bc-card,
.app-main:has(> .hero-card:not(.prev-hero)) .advice,
.app-main:has(> .hero-card:not(.prev-hero)) .multi-appareils-app,
.app-main > .hero-card:not(.prev-hero) ~ .card,
.app-main > .hero-card:not(.prev-hero) ~ .stat-card,
.app-main > .hero-card:not(.prev-hero) ~ .advice,
.app-main > .hero-card:not(.prev-hero) ~ .multi-appareils-app,
.app-main > .hero-card:not(.prev-hero) ~ * .card,
.app-main > .hero-card:not(.prev-hero) ~ * .stat-card,
.app-main > .hero-card:not(.prev-hero) ~ * .bc-card,
.app-main > .hero-card:not(.prev-hero) ~ * .advice,
.app-main > .hero-card:not(.prev-hero) ~ * .multi-appareils-app,
#carteInvitation {
  padding: 16px !important;
}
/* ── typographie de l'accueil ── */
.app-main .hero-card:not(.prev-hero) .hero-value { font-size: 32px; }
.app-main:has(> .hero-card:not(.prev-hero)) .reste-montant,
.app-main > .hero-card:not(.prev-hero) ~ * .reste-montant { font-size: 22px; }
.app-main:has(> .hero-card:not(.prev-hero)) .reste-jour-val,
.app-main > .hero-card:not(.prev-hero) ~ * .reste-jour-val { font-size: 16px; }
.app-main:has(> .hero-card:not(.prev-hero)) .stat-value,
.app-main > .hero-card:not(.prev-hero) ~ * .stat-value { font-size: 17px; }
.app-main:has(> .hero-card:not(.prev-hero)) .donut-center strong,
.app-main > .hero-card:not(.prev-hero) ~ * .donut-center strong { font-size: 18px; }
/* ── « À payer dans X jours », compacte ── */
.app-main:has(> .hero-card:not(.prev-hero)) .ligne-echeance,
.app-main > .hero-card:not(.prev-hero) ~ * .ligne-echeance {
  padding: 9px 16px !important;
}
.ligne-echeance .echeance-texte  { font-size: 13px; }
.ligne-echeance .echeance-montant { font-size: 14px; }
.ligne-echeance .echeance-ico { width: 24px; height: 24px; flex: 0 0 24px; border-radius: 8px; }
.ligne-echeance .echeance-ico .icon { width: 13px; height: 13px; }

/* ── Transactions : un fond pastel par tuile, charte inchangée ── */
.tuile-revenus  { background: var(--sec-revenus-soft); }
.tuile-depenses { background: var(--sec-depenses-soft); }
.tuile-epargne  { background: var(--sec-epargne-soft); }
.tuile-solde    { background: var(--sec-credits-soft); }

/* ── Prévision : les lignes « Mois par mois » sont des boutons ── */
button.prev-mois {
  width: 100%; background: none; border: none; font: inherit;
  color: inherit; text-align: left; cursor: pointer;
}

/* ═══════════════════════════════════════════════════════════════════
   v63.14 — RIEN NE DÉPASSE JAMAIS DU CADRE DE L'ÉCRAN
   ───────────────────────────────────────────────────────────────────
   Le zoom est désormais BLOQUÉ (voir « touch-action: pan-x pan-y » en
   haut de ce fichier, et le bloc JavaScript d'app.html). Tant que le
   pincement restait possible, un débordement de quelques pixels était
   rattrapable par la personne elle-même : elle dézoomait. Ce n'est
   plus le cas. Un contenu qui sort du cadre devient donc DÉFINITIVEMENT
   invisible, et c'est la raison d'être de tout ce bloc.

   Trois niveaux, du plus précis au plus général :
     1. les montants rétrécissent pour tenir (fait en JavaScript, qui
        seul peut MESURER le texte réellement affiché — aucune règle CSS
        ne sait qu'un montant fait 9 chiffres plutôt que 4) ;
     2. les tableaux larges défilent dans leur propre cadre ;
     3. un filet de sécurité général : la page elle-même ne peut plus
        déborder latéralement.

   Ce bloc est ajouté EN FIN DE FICHIER, comme toutes les règles
   récentes : il ne réécrit rien, il ne fait qu'ajouter des bornes.
   ═══════════════════════════════════════════════════════════════════ */

/* ─── 1. Filet de sécurité général ───────────────────────────────────
   « overflow-x: clip » borne la page SANS créer de conteneur de
   défilement — contrairement à « hidden », qui casserait
   « position: sticky » sur les en-têtes et le comportement des barres
   fixes. Les navigateurs qui ne connaissent pas « clip » reçoivent la
   déclaration précédente et se comportent comme avant : rien n'est
   perdu. */
html { max-width: 100%; overflow-x: hidden; overflow-x: clip; }
body { max-width: 100%; overflow-x: clip; }

/* ATTENTION — RÈGLE ESSAYÉE PUIS RETIRÉE, ET IL FAUT SAVOIR POURQUOI.
   Un « #app, #app * { max-width: 100% } » général paraissait le filet
   idéal. Il ne l'est pas : « #app * » pèse un SÉLECTEUR D'IDENTIFIANT,
   il écrase donc « .app-layout { max-width: 1400px } » et toutes les
   largeurs de lecture posées par classe. Mesuré : sur un écran de
   1 920 px, la colonne passait de 377→1812 px (centrée) à 297→1892 px
   (pleine largeur) — toute la mise en page de l'ordinateur et de la
   tablette était à refaire. Défaut vu sur les captures de
   non-régression, avant livraison.
   Les images et les dessins, eux, sont déjà bornés par le « reset »
   en haut de ce fichier (« img { max-width: 100% } ») et par les règles
   de l'anneau. Rien à ajouter ici. */
/* DEUXIÈME RÈGLE ESSAYÉE PUIS RETIRÉE. « #app svg { height: auto } »
   semblait anodin ; il écrase en réalité la hauteur que le graphique
   en courbe se donne lui-même. Mesuré sur l'accueil, iPhone 390 px :
   le graphique passait de 220 px à 122 px de haut — il s'écrasait de
   moitié. Vu sur les captures de non-régression, avant livraison.
   Les images sont déjà bornées par « img { max-width: 100% } » dans le
   « reset » en haut de ce fichier ; l'anneau et le graphique ont leurs
   propres règles, vérifiées depuis la v20. Il n'y a rien à ajouter. */

/* ─── 2. Les tableaux ────────────────────────────────────────────────
   Un tableau ne peut pas rétrécir en dessous de la somme des largeurs
   minimales de ses colonnes : c'est la seule structure de la page qui
   peut légitimement être plus large que l'écran. Elle défile alors dans
   SON PROPRE cadre — jamais la page. Le conteneur « .table-scroll » est
   posé autour du tableau par le bloc JavaScript d'app.html, et
   uniquement lorsqu'il est réellement nécessaire. */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  max-width: 100%;
}
/* Le doigt peut faire défiler ce cadre-là horizontalement, alors que le
   reste de la page n'autorise que le défilement vertical. */
.table-scroll, .table-scroll * { touch-action: pan-x pan-y; }
/* Le tableau placé DANS un cadre défilant doit pouvoir rester plus
   large que lui : c'est tout l'intérêt du cadre. Cette exception
   annule, pour lui seul, la borne générale posée plus haut. */
#app .table-scroll > table, .table-scroll > table { max-width: none; }
#app table { max-width: 100%; }
#app td, #app th { overflow-wrap: break-word; word-break: normal; }
/* Une cellule de montant ne se coupe pas en deux lignes : c'est le
   corps du texte qui diminue (voir le point 3), pas le nombre qui se
   scinde. */
#app td.num, #app th.num, #app .montant { white-space: nowrap; }

/* ─── 3. Les montants qui rétrécissent ───────────────────────────────
   Le JavaScript pose « --fit » sur l'élément quand le texte ne tient
   pas, et RIEN quand il tient. Aucun montant ne change de taille tant
   qu'il n'y a pas de problème réel : à l'affichage courant, cette
   règle ne s'applique à aucun élément.
   La transition est volontairement absente : un montant qui rétrécit en
   douceur pendant qu'on fait défiler la page donne une impression de
   flottement. */
[data-fit] { font-size: var(--fit) !important; line-height: 1.12; }

/* ─── 4. Les textes longs ────────────────────────────────────────────
   Un libellé saisi par la personne (« Remboursement mutuelle
   complémentaire santé famille ») ou une adresse email n'a pas de
   limite de longueur. Il se replie, et se coupe s'il n'existe aucune
   espace où se replier — mais seulement dans ce cas-là. */
#app .tx-title, #app .tx-sub, #app .cat-item span,
#app .stat-title, #app .hero-label, #app .prev-mois,
#app .alert, #app .lbl {
  overflow-wrap: break-word;
  min-width: 0;
}

/* ─── 5. Les fenêtres ────────────────────────────────────────────────
   Elles étaient déjà bornées en hauteur (« max-height: 100dvh - 36px »
   plus haut dans ce fichier). On borne aussi leur CONTENU en largeur :
   sans zoom, une fenêtre plus large que l'écran n'est plus rattrapable.
   « 100dvh » suit la barre d'adresse mobile quand elle se replie ;
   « 100vh » reste pour les navigateurs qui ne connaissent pas dvh. */
/* « .dialog * » a été retiré pour la même raison que ci-dessus.
   « .dialog » seul est déjà borné plus haut dans ce fichier. */
.dialog-body { overflow-wrap: break-word; }
.dialog-body table { max-width: 100%; }

/* ─── 6. Les tuiles de statistique ───────────────────────────────────
   v63.14 — DEFAUT TROUVE PAR LA MESURE, PAS PAR LA LECTURE.
   « .stat-top » est une boite flexible : un rond d'icone de 34 px, puis
   le bloc titre + montant. Ce bloc-la n'avait ni « min-width: 0 » ni
   consigne de souplesse. Or, par defaut, un enfant de boite flexible
   refuse de descendre en dessous de la largeur de son contenu : avec un
   montant a sept chiffres, il gardait 110 px alors qu'il n'en avait que
   64 de disponibles, et sortait de la tuile.
   Mesure a 320 px, tuile « Depenses » : le contenu finissait a 332 px
   pour une fenetre de 320 - soit 12 px purement invisibles, et sans
   zoom, definitivement perdus. Les deux lignes ci-dessous suffisent ;
   le montant, lui, est ensuite reduit par l'ajustement d'app.html. */
#app .stat-top > * { min-width: 0; }
#app .stat-top > *:not(.stat-ico) { flex: 1 1 auto; }

/* Dernier recours de l'ajustement : quand meme le plancher de 11 px ne
   suffit pas, le texte passe a la ligne plutot que d'etre tranche.
   Pose par app.html, et par lui seul. */
[data-fit-replie] {
  white-space: normal !important;
  overflow-wrap: anywhere;
  line-height: 1.15;
}
