/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   NEXSET Ã¢â‚¬â€ Street Workout Tracker
   Dark Premium Fitness UI
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   NEXSET THEMES
   theme-blue | theme-purple | theme-mixed
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */

/* ══════════════════════════════════════════════════════════════════════════
   NEXSET — SOCLE DE DESIGN
   Deux règles, sans exception :
     1. Aucune couleur littérale hors de ce bloc.
     2. Un composant ne connaît jamais son thème. Il consomme des tokens.
        (C'est ce qui tue le bug de spécificité : plus besoin de règles
         [data-theme="light"] .machin, donc plus de guerre de poids CSS.)
   ══════════════════════════════════════════════════════════════════════════ */

:root,
[data-theme="blue"],
[data-theme="purple"],
[data-theme="mixed"] {
  color-scheme: dark;

  /* ── Surfaces : la hiérarchie se fait par la LUMINOSITÉ, jamais par du
        blanc translucide. C'est ce qui cassait tout le thème clair. ── */
  --bg-base:      #0C0E13;
  --surface-1:    #12151B;   /* fond de rangée, carte de second plan */
  --surface-2:    #171B23;   /* carte principale                     */
  --surface-3:    #1E232D;   /* élément dans une carte, input, badge */
  --surface-4:    #272D3A;   /* hover / pressed / pilule active      */

  /* ── Bordures neutres. --border n'est plus teinté indigo : fin du
        liseré cyan sur .btn-icon documenté dans CLAUDE.md. ── */
  --border-0:     rgba(255,255,255,.06);
  --border-1:     rgba(255,255,255,.10);
  --border-2:     rgba(255,255,255,.16);

  /* ── Texte, 3 niveaux. Ratios sur --bg-base : 17.8 / 9.2 / 6.4 ── */
  --text-1:       #F4F6FA;
  --text-2:       #AAB3C6;
  --text-3:       #8B94A9;
  --text-on-fill: #FFFFFF;

  /* ── Accent. #5B8CFF sert au TEXTE et aux ICÔNES (4.98 sur --surface-3).
        Un aplat plein d'accent n'est jamais utilisé avec du blanc dessus
        (3.16 = échec) : le bouton primaire est un dégradé. ── */
  --accent:        #5B8CFF;
  --accent-strong: #7BA0FF;
  --accent-soft:   rgba(91,140,255,.14);
  --accent-ring:   rgba(91,140,255,.28);

  /* ── Sémantique : un sens, une couleur, dans les deux thèmes. ── */
  --ok:     #34D399;  --ok-soft:     rgba(52,211,153,.13);
  --warn:   #FBBF24;  --warn-soft:   rgba(251,191,36,.13);
  /* C11 : or lisible en TEXTE. --warn suffit sur fond sombre, mais pas en clair
     sur le voile dore de la carte compet (#A16207 y plafonne a 4.16:1). */
  --warn-ink: var(--warn);
  --danger: #FB7185;  --danger-soft: rgba(251,113,133,.13);
  /* C25 : --danger sert d'ENCRE sur fond sombre, ou un rouge clair est le bon choix
     (cf. l.768, color: var(--accent-red)). Il ne peut pas servir en meme temps de FOND
     sous du texte blanc : #FB7185 + blanc ne donne que 2.69:1, et c'est le bouton
     « Supprimer » de la confirmation qui devenait dur a lire — le pire endroit possible.
     D'ou un jeton distinct pour les remplissages. Meme teinte (H=351deg, S=95%), clarte
     descendue a 47% : 4.64:1 avec du blanc. */
  --danger-fill: #E90727;

  /* ── LE DÉGRADÉ EST LA SIGNATURE. Deux emplois, deux seulement :
        1. UNE ACTION PRIMAIRE  — .btn-primary et ses variantes, le FAB, le bouton de
           lecture du timer, le bouton d'envoi du support.
        2. L'ÉLÉMENT PRINCIPAL D'UN ÉCRAN — le hero du jour (Galerie), le hero du
           dernier record (Records). Un seul par écran.

        C32 : l'ancienne version de ce commentaire annonçait « trois endroits :
        hero du jour, bouton primaire, FAB. Nulle part ailleurs. » Il y en avait
        quinze. Un commentaire qui ment sur la règle est pire que pas de commentaire :
        on le lit, on le croit, et on ne verifie plus. La règle ci-dessus est celle
        qui est réellement appliquée, vérifiée usage par usage.

        Ce qui a été RETIRÉ en C32, parce que ni action ni élément principal :
        la 1re pastille de stats du Profil (une stat parmi quatre, mise en avant sans
        raison) et la puce active du carrousel du jour (16x4px — un dégradé n'y a pas
        la place de se déployer, il s'y rend comme un aplat).

        Avant d'en ajouter un : est-ce une action, ou LE sujet de l'écran ? Si la
        réponse est « ça fait joli », c'est non. ── */
  /* C35 : les deux degrades approfondis de 7 points de clarte. Teintes et saturations
     INCHANGEES (232/253/272/262 degres) — c'est la meme marque, en plus dense.
     Pourquoi : sur l'ancien arret le plus clair (#4B63FF), le blanc PUR ne faisait que
     4.64:1, pour un seuil de 4.5 sur du petit texte. Marge : 0.14. Tout le texte du
     hero devait donc rester en blanc pur, sans aucune nuance possible — d'ou les six
     elements blancs de meme intensite que Chris a trouves trop nombreux, et d'ou le
     constat de C13 (« la hierarchie passe par la taille, pas par un alpha »).
     A -7%, le blanc pur monte a 6.26:1 et un blanc a 80% atteint 4.55:1. La marge
     existe enfin : le titre et les chiffres restent nets, le secondaire peut reculer.
     Approfondir ne rend donc pas le blanc plus present — c'est ce qui permet d'en
     mettre moins. */
  --grad-primary: linear-gradient(135deg, #2744FF 0%, #6619EA 100%);
  --grad-hero:    linear-gradient(140deg, #2744FF 0%, #532AE6 52%, #7925C5 100%);

  /* ── Voiles : dégradent vers le fond du thème, jamais vers #000. ── */
  --scrim-header: rgba(12,14,19,.72);
  --scrim-nav:    rgba(12,14,19,.88);
  --scrim-modal:  rgba(6,7,10,.66);
  --scrim-fade:   12,14,19;      /* RGB du fond, pour les fondus */

  /* ── Ombres : 3 niveaux, pas 17. ── */
  --sh-1: 0 1px 2px rgba(0,0,0,.35);
  --sh-2: 0 4px 16px rgba(0,0,0,.40);
  --sh-3: 0 18px 48px rgba(0,0,0,.55);

  --theme-color:     #5B8CFF;
  --theme-secondary: #7C3AED;
  --theme-rgb:       124, 60, 237;
  --accent-complement: #FBBF24;
  /* C5 — barres du graphe de progression. Jetons DEDIES, et pas
     `rgba(var(--theme-rgb), .5)` : --theme-rgb vaut 124,60,237 (violet) dans les
     DEUX themes alors que --theme-color est bleu — les deux sont desynchronises
     dans ce fichier depuis longtemps. S'appuyer dessus aurait donne des barres
     violettes surmontees d'une barre selectionnee bleue.
     Valeurs choisies par mesure sur le fond de section (--surface-2), pas a
     l'oeil : la barre vise ~2,6:1 (une masse doit se voir sans crier) et le
     trait des semaines vides ~1,6:1 (une absence se lit plus bas qu'une
     presence — c'etait l'inverse dans la premiere version). */
  --prog-barre:      #414D68;
  --prog-vide:       #2A3242;
  /* C64 — la hauteur du safe-area, passee par un JETON plutot qu'appelee
     directement partout. Deux raisons, et la seconde est la plus importante :
       - une seule source si la regle change ;
       - c'est TESTABLE. `env()` vaut toujours 0 dans un navigateur headless,
         donc le defaut « les cartes defilent dans la bande du safe-area » ne
         pouvait PAS exister au banc — il n'a ete vu que sur iPhone. En passant
         par une variable, un test peut poser --safe-top: 59px sur la racine et
         reproduire les conditions de l'appareil. */
  --safe-top:        env(safe-area-inset-top, 0px);
  /* C129 — le jumeau du bas, pour la meme raison exactement. Il n'existait pas
     parce qu'une seule regle de toute la feuille lisait `env(safe-area-inset-
     bottom)` en direct : `.bienv-wrap`. Des lors qu'on EPINGLE des boutons au
     bas de l'ecran, la bande du bas cesse d'etre un detail — un bouton pose
     dessous est un bouton qu'on ne peut pas toucher sur iPhone. Et comme pour
     --safe-top, l'interet decisif est qu'un test peut poser --safe-bottom: 34px
     et VERIFIER que les actions se deplacent d'autant. */
  --safe-bottom:     env(safe-area-inset-bottom, 0px);
  /* C63 — la meme couleur que --bg-base, en alpha 0. Sert au fondu sous
     l'en-tete colle de la galerie. Surtout PAS `transparent` : plusieurs
     moteurs interpolent vers du noir transparent, ce qui salit le degrade. */
  --bg-base-0:       rgba(12, 14, 19, 0);
  --gradient-accent:  var(--grad-primary);
  --gradient-shimmer: linear-gradient(90deg, transparent 0%, rgba(124,90,255,.30) 50%, transparent 100%);

  --fw-color-rep:              var(--theme-color);
  --fw-color-weight:           #ff6b78;
  --fw-color-emom:             #bf5fff;
  --fw-color-amrap:            #ff9f43;
  --fw-color-rest:             rgba(160, 160, 200, 0.4);
  --fw-color-rest-label:       rgba(160, 160, 200, 0.82);   /* C125 : le rgba en dur valait .6 = 3,43:1 sur la carte sombre */
  --fw-color-timecap:          #ffa502;
  --fw-color-unbroken-text:    rgba(46, 213, 115, 0.9);
  --fw-color-unbroken-bg:      rgba(46, 213, 115, 0.12);
  --fw-color-unbroken-border:  rgba(46, 213, 115, 0.3);
  --fw-color-deadstop:         rgba(255, 120, 120, 0.95);
  --fw-color-slow:             rgba(100, 200, 255, 0.9);
  --fw-color-explosive:        rgba(255, 165, 2, 0.9);
  --fw-color-distance:         var(--theme-color);
  --fw-color-title:            var(--theme-color);
  --fw-color-header:           var(--theme-color);
  --fw-color-comment:          rgba(160, 160, 200, 0.4);
  --fw-color-time:             var(--theme-color);
  --fw-color-mult:             #ffa502;
  --fw-color-sep:              rgba(160, 160, 200, 0.78);   /* C125 : .5 = 2,76:1 sur la carte sombre */
  --fw-color-group:            rgba(160, 160, 200, 0.4);
}

/* ══════════════ THÈME CLAIR ══════════════
   Mêmes noms de tokens, valeurs inversées. Aucune règle de composant
   n'a besoin d'être dupliquée ici. */
[data-theme="light"] {
  color-scheme: light;

  --bg-base:      #F1F4F9;
  --surface-1:    #FFFFFF;
  --surface-2:    #FFFFFF;
  --surface-3:    #EDF0F6;
  --surface-4:    #E2E8F2;

  --border-0:     rgba(13,16,23,.07);
  --border-1:     rgba(13,16,23,.12);
  --border-2:     rgba(13,16,23,.18);

  /* Ratios sur --bg-base : 17.3 / 7.2 / 5.3  (sur blanc : 19.0 / 8.0 / 5.8) */
  --text-1:       #0D1017;
  --text-2:       #48506A;
  --text-3:       #5D6579;
  --text-on-fill: #FFFFFF;

  /* En clair l'accent doit être assombri : #5B8CFF ne fait que 2.4 sur blanc.
     Ici 5.61 sur --bg-base, 6.19 sur blanc. */
  --accent:        #3D4FE0;
  --accent-strong: #3346D6;
  --accent-soft:   rgba(61,79,224,.10);
  --accent-ring:   rgba(61,79,224,.24);

  --ok:     #047857;  --ok-soft:     rgba(4,120,87,.10);
  --warn:   #A16207;  --warn-soft:   rgba(161,98,7,.10);
  --warn-ink: #8A5406;   /* 5.28:1 sur le voile dore de la carte compet */
  --danger: #BE123C;  --danger-soft: rgba(190,18,60,.09);
  /* En theme clair, --danger est deja assez profond pour porter du blanc (6.29:1) :
     le jeton de remplissage n'a donc rien a corriger ici. */
  --danger-fill: #BE123C;

  --scrim-header: rgba(241,244,249,.80);
  --scrim-nav:    rgba(255,255,255,.90);
  --scrim-modal:  rgba(20,26,40,.38);
  --scrim-fade:   241,244,249;

  /* Ombres bleutées plutôt que du noir pur : du noir sur du clair fait sale. */
  --sh-1: 0 1px 2px rgba(13,16,23,.06);
  --sh-2: 0 4px 16px rgba(13,16,23,.09);
  --sh-3: 0 18px 48px rgba(13,16,23,.15);

  --theme-color:     #3D4FE0;
  --theme-secondary: #7C3AED;
  --theme-rgb:       124, 60, 237;
  --accent-complement: #A16207;
  /* C5 — mêmes cibles de contraste qu'en sombre, calculées sur le blanc de
     --surface-2 : ~2,6:1 pour la barre, ~1,6:1 pour le trait des semaines
     vides. Ce sont les RATIOS qui sont repris d'un thème à l'autre, pas les
     couleurs — c'est la règle de test-parite-themes.js. */
  --prog-barre:      #A4B0C8;
  --prog-vide:       #D7DDE8;
  /* C63 — --bg-base (#F1F4F9) en alpha 0. En clair, passer par `transparent`
     donnerait un halo GRIS sous l'en-tete : le defaut s'y voit bien plus. */
  --bg-base-0:       rgba(241, 244, 249, 0);
  --gradient-accent:  var(--grad-primary);
  --gradient-shimmer: linear-gradient(90deg, transparent 0%, rgba(124,90,255,.20) 50%, transparent 100%);

  --fw-color-weight:           #c62740;   /* C125 : #e0405a = 4,2:1 sur sa propre teinte -> 4,9:1 */
  --fw-color-emom:             #9b3fe0;
  --fw-color-amrap:            #b16000;   /* C125 : #e07a00 = 3,05:1 sur blanc -> 4,62:1 */
  /* ══ C125 — CINQ TOKENS DU RENDERER ETAIENT ILLISIBLES EN CLAIR ══════════
     Mesure AU PIXEL (capture d'un vrai programme, encre la plus sombre contre
     fond le plus clair — pas le style calcule, qui m'a deja menti deux fois en
     lisant le conteneur au lieu du token) :

         fw-rest-label      1,68:1        fw-badge-slow       1,77:1
         fw-badge-explosive 1,87:1        fw-badge-deadstop   2,46:1
         fw-timecap-line    2,80:1        (fw-rep, lui : 6,19:1)

     Ma passe theme clair de C117 avait couvert les surfaces et les voiles ; les
     couleurs de tokens du renderer n'y etaient pas. `slow` a 1,77:1, c'est du
     gris sur blanc.

     METHODE, REPRISE DE C26 (`--fw-color-mult`, meme situation) : on garde la
     TEINTE et la SATURATION, on descend la clarte jusqu'a franchir 4,5:1 sur le
     fond REEL — pour un badge, sa propre teinte composee sur la carte, pas la
     carte nue. Les couleurs restent reconnaissables, elles se voient.

     ⚠ CES TROIS-LA N'AVAIENT AUCUN OVERRIDE CLAIR : deadstop, slow, explosive
     heritaient telles quelles des valeurs sombres. Le theme clair ne les avait
     jamais vues. */
  --fw-color-rest:             #69769b;   /* 2,48:1 -> 4,50:1  (l'alpha .55 etait le fautif, pas la teinte) */
  --fw-color-timecap:          #a56a00;   /* 2,80:1 -> 4,51:1 */
  --fw-color-deadstop:         #d80000;   /* 2,09:1 -> 4,53:1  sur sa propre teinte rose */
  --fw-color-slow:             #0077b8;   /* 1,64:1 -> 4,50:1 */
  --fw-color-explosive:        #9c6500;   /* 1,72:1 -> 4,51:1 */
  --fw-color-rest-label:       #6f6fac;   /* 1,68:1 -> 4,62:1 */
  --fw-color-unbroken-text:    #0a7a3f;   /* C125 : 4,2:1 sur sa teinte verte -> 5,1:1 */
  --fw-color-unbroken-bg:      rgba(18, 161, 80, 0.12);
  --fw-color-unbroken-border:  rgba(18, 161, 80, 0.3);
  --fw-color-comment:          rgba(80, 90, 120, 0.5);
  --fw-color-mult:             #A36A00 /* C26 : etait #cc8500 = 3.03:1 sur blanc. Meme teinte ambre, clarte descendue -> 4.55:1 */;
  --fw-color-sep:              rgba(80, 90, 120, 0.5);
  --fw-color-group:            rgba(80, 90, 120, 0.45);
}

/* ══════════════ INVARIANTS (tous thèmes) ══════════════ */
:root {
  --font-body:    'Inter', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  /* C41 : etait 'Inter', c'est-a-dire la MEME famille que --font-body. Les 35
     declarations d'affichage du projet etaient donc correctes et sans effet visible.
     Inter reste en repli : si la condensee ne charge pas, rien ne casse. */
  --font-display: 'Barlow Condensed', 'Inter', system-ui, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, monospace;

  /* ══ ÉCHELLE TYPO ═══════════════════════════════════════════════════════
     La règle écrite ici disait « plus aucun font-size en rem/em, uniquement
     var(--fs-*) ». MESURÉ AU 9 AOÛT : 45 déclarations sur 397 utilisent
     l'échelle — 11 %. Et cinq des huit pas n'avaient AUCUN usage. Par-dessus,
     neuf valeurs en rem inventées au fil de l'eau (0.72, 0.75, 0.78, 0.8, 0.82,
     0.85, 0.875, 0.88, 0.9) et six en px. Les quatorze tailles distinctes de
     l'app ne viennent donc pas d'une échelle absente : elles viennent d'une
     échelle ignorée. La règle ci-dessus est restée un vœu.

     C113 ne remet pas les 350 déclarations d'aplomb — ce serait un lot à part,
     invisible, et à capturer écran par écran. Il fait les trois choses qui
     comptent : l'échelle devient RELATIVE, elle gagne un plancher explicite, et
     les textes qui portent de l'information quittent le 11 px.

     ⚠ EN REM, ET C'EST LE POINT STRUCTUREL. Mesuré en passant la racine de 16 à
     20 px : 63 % des familles de texte ne bougeaient pas — et c'étaient les
     PETITES. Les grands titres suivaient, le petit texte restait figé, soit
     exactement l'inverse de ce qu'on veut. Déclarer l'échelle en rem rend
     relatif tout ce qui l'utilise, aujourd'hui et demain, en un seul endroit.
     Aucun changement visible au réglage par défaut : 0.6875 × 16 = 11.
     Vérifié : aucun `calc()` ne consomme ces jetons, la conversion est sûre.

     ⚠ DEUX PLANCHERS, ET LA DIFFÉRENCE EST DE SENS, PAS DE TAILLE.
     `--fs-micro` (11px) est réservé aux UNITÉS et aux repères : « REPS »,
     « KCAL », « #2 », « LUN ». `--fs-xs` passe de 11 à 12px et sert à tout ce
     qui porte de l'INFORMATION : la catégorie, « Fait il y a 5 j »,
     « Programmé », « Appuie pour démarrer ». 11 px est le plancher absolu des
     recommandations iOS, prévu pour du tertiaire ; l'app y mettait 97 textes,
     dont des instructions. Et le contexte plaide pour plus de marge, pas moins :
     téléphone posé par terre, bras tendu, entre deux séries. */
  --fs-display: 2.125rem; --fs-h1: 1.75rem;  --fs-h2: 1.375rem; --fs-h3: 1.125rem;
  --fs-body:    0.9375rem; --fs-sm: 0.8125rem; --fs-xs: 0.75rem;  --fs-num: 1.875rem;
  --fs-micro:   0.6875rem;

  /* Espacement — grille 4 pt, 7 pas. */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px;
  --gutter: 20px;

  /* Rayons — 3 + la pilule. */
  --r-sm: 10px;  --r-md: 16px;  --r-lg: 24px;  --r-full: 999px;

  /* Mouvement */
  --ease:        cubic-bezier(.2,.8,.3,1);
  --ease-spring: cubic-bezier(.34,1.56,.64,1);
  --dur-1: 120ms;  --dur-2: 200ms;  --dur-3: 320ms;

  --sidebar-w: 220px;
  --topbar-h:  60px;
  --view-top:  calc(var(--safe-top) + 14px);
  --nav-h:     calc(64px + env(safe-area-inset-bottom, 0px));
  --tap:       44px;
}

/* ══════════════ ALIAS DE COMPATIBILITÉ ══════════════
   Les anciens noms pointent vers le nouveau socle : l'app entière hérite
   du soclesans réécriture, et deux bugs connus tombent au passage —
     · --border n'est plus teinté   → fin du liseré cyan
     · --text-muted n'est plus #2e4a60 (2.05:1) → devient --text-3 (6.4:1) */
:root, [data-theme="blue"], [data-theme="purple"],
[data-theme="mixed"], [data-theme="light"] {
  --bg-surface:       var(--surface-1);
  --bg-card:          var(--surface-2);
  --bg-hover:         var(--surface-4);
  --bg-input:         var(--surface-3);

  --border:           var(--border-1);
  --border-bright:    var(--accent-ring);
  --surface-card-a:   var(--surface-2);
  --surface-card-b:   var(--surface-1);
  --surface-card-brd: var(--border-1);

  --text-primary:     var(--text-1);
  --text-secondary:   var(--text-2);
  --text-muted:       var(--text-3);
  /* C11 : --text-faint et --text-dim etaient utilises 7 fois mais JAMAIS declares.
     --text-faint se repliait sur un #6b7196 en dur (3.64:1 en sombre, et pas
     theme-aware) ; --text-dim n'avait meme pas de repli, donc la declaration etait
     invalide et le style voulu ne s'appliquait jamais. Declares ici en alias
     plutot que patches sur 7 sites : une seule source, aucun risque de specificite. */
  --text-faint:       var(--text-3);
  --text-dim:         var(--text-2);

  --accent-blue:      var(--accent);
  --accent-green:     var(--ok);
  --accent-gold:      var(--warn);
  --accent-red:       var(--danger);
  --accent-purple:    var(--theme-secondary);
  --theme-text-on-accent: var(--text-on-fill);

  --radius-sm: var(--r-sm);
  --radius-md: var(--r-md);
  --radius-lg: var(--r-lg);
  --radius-xl: var(--r-lg);

  --shadow-card:  var(--sh-2);
  --shadow-glow:  none;          /* les halos ambiants brouillaient la lecture */
  --glow-primary: transparent;
  --glow-card:    transparent;
  --glow-strong:  var(--accent-ring);

  --ease-v2:   var(--ease);
  --dur-v2:    var(--dur-3);
  --dur-micro: var(--dur-1);
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  height: 100%;
  scrollbar-width: none;
  -ms-overflow-style: none;
  background: var(--bg-base);            /* fond opaque pour la zone hors-body */
  overscroll-behavior-y: none;           /* bloque le bounce iOS vertical (sinon les orbs body::before/::after sont révélées au bounce) */
}
html::-webkit-scrollbar { display: none; }
body {
  font-family: var(--font-body);
  background: var(--bg-base);
  color: var(--text-primary);
  min-height: 100%;
  min-height: -webkit-fill-available;
  overflow-x: hidden;
  overscroll-behavior: none;
  overscroll-behavior-y: none;          /* explicite pour iOS Safari PWA */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
body::-webkit-scrollbar { display: none; }

/* Ambient background orbs */
body::before {
  content: '';
  position: fixed;
  top: -20%;
  left: -10%;
  width: 60vw;
  height: 60vw;
  background: radial-gradient(ellipse, var(--glow-primary) 0%, transparent 70%);
  pointer-events: none;
  z-index: 0;
  animation: ambientFloat 12s ease-in-out infinite alternate;
}
body::after {
  content: '';
  position: fixed;
  bottom: -20%;
  right: -10%;
  width: 50vw;
  height: 50vw;
  background: radial-gradient(ellipse, var(--glow-primary) 0%, transparent 70%);
  pointer-events: none;
  z-index: 0;
  opacity: 0.6;
  animation: ambientFloat 15s ease-in-out infinite alternate-reverse;
}
@keyframes ambientFloat {
  from { transform: translate(0,0) scale(1); }
  to   { transform: translate(3%,5%) scale(1.08); }
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   GLOBAL KEYFRAMES
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
/* C15 : ces frames portaient `filter: drop-shadow()` SANS ARGUMENT — une valeur
   invalide, donc la declaration entiere etait rejetee par le navigateur. Le « glow
   pulse » n'a jamais existe : seule l'opacity animait. Reliquat de la purge des
   halos de C5, ou la regex a retire les arguments en laissant les appels vides.
   On garde la pulsation d'opacity, qui est ce qui se voyait reellement. */
@keyframes pulse-glow {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.85; }
}
@keyframes slide-up {
  from { transform: translateY(18px); }
  to   { transform: translateY(0); }
}
@keyframes fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes logo-set-pulse {
  /* Refonte : plus de pulsation en boucle du logo — glow doux et statique.
     C15 : la seule declaration etait un drop-shadow() vide, donc invalide :
     l'animation ne faisait rien du tout. Rendue explicite. */
  0%, 100% { filter: none; }
}
a { text-decoration: none; color: inherit; }
button { cursor: pointer; border: none; background: none; font-family: inherit; }
input, textarea, select {
  font-family: var(--font-body);
  -webkit-appearance: none;
  appearance: none;
  transform: translateZ(0);
}
svg { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* Ã¢â€â‚¬Ã¢â€â‚¬ SCROLLBAR Ã¢â€â‚¬Ã¢â€â‚¬ */
html, body { scrollbar-width: none; -ms-overflow-style: none; }
*::-webkit-scrollbar { width: 0; height: 0; display: none; }

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   SIDEBAR
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.sidebar {
  position: fixed;
  left: 0; top: 0; bottom: 0;
  width: var(--sidebar-w);
  background: rgba(4,3,18,0.82);
  backdrop-filter: blur(32px) saturate(160%);
  -webkit-backdrop-filter: blur(32px) saturate(160%);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  padding: calc(var(--safe-top) + 28px) 16px 28px;
  z-index: 100;
  transition: transform 0.3s cubic-bezier(0.4,0,0.2,1);
}

.sidebar-logo {
  display: flex;
  align-items: center;
  padding: 0 8px 20px;
  cursor: pointer;
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 1.35rem;
  letter-spacing: -0.02em;
}
.logo-nex {
  color: var(--text-primary);
  filter: drop-shadow(0 0 6px rgba(255,255,255,0.2));
}
.logo-set {
  color: var(--theme-color);
  animation: logo-set-pulse 3s ease-in-out infinite;
}
/* Luminous separator under sidebar logo */
.nav-sep {
  height: 1px;
  margin: 8px 8px;
  background: linear-gradient(90deg, var(--theme-color), var(--theme-secondary, var(--theme-color)), transparent);
  opacity: 0.35;
  border-radius: 1px;
  pointer-events: none;
}
.nav-links > .nav-sep:last-child {
  margin-top: auto;
}

.nav-links {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1;
}
.nav-link {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border-radius: var(--radius-md);
  color: var(--text-secondary);
  font-weight: 500;
  font-size: 0.9rem;
  transition: all 0.22s cubic-bezier(0.4,0,0.2,1);
  position: relative;
  overflow: hidden;
}
.nav-link svg { width: 18px; height: 18px; }
@media (hover: hover) {
  .nav-link:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
    padding-left: 18px;
  }
}
.nav-link.active {
  background: linear-gradient(90deg, var(--glow-card), rgba(255,255,255,0.05));
  color: var(--theme-color);
  border: 1px solid var(--border-bright);
  box-shadow: 4px 0 20px var(--glow-primary) inset;
}
.nav-link.active svg { stroke: var(--theme-color); }
/* Left glow bar on active item */
.nav-link.active::before {
  content: '';
  position: absolute;
  left: 0; top: 20%; bottom: 20%;
  width: 3px;
  background: var(--theme-color);
  border-radius: 0 3px 3px 0;
  box-shadow: none;
}

.sidebar-bottom { padding-top: 16px; }
.timer-toggle-btn svg { width: 17px; height: 17px; }
@media (hover: hover) {
  .timer-toggle-btn:hover { background: var(--bg-hover); color: var(--text-primary); }
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   TOPBAR (mobile)
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.topbar {
  display: none;
  position: fixed;
  top: 0; left: 0; right: 0;
  height: calc(var(--topbar-h) + var(--safe-top));
  background: rgba(3,3,18,0.88);
  backdrop-filter: blur(28px) saturate(160%);
  -webkit-backdrop-filter: blur(28px) saturate(160%);
  border-bottom: 1px solid var(--border);
  align-items: center;
  justify-content: space-between;
  padding: var(--safe-top) 20px 0;
  z-index: 200;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1rem;
  letter-spacing: 0.04em;
  transition: opacity 0.15s ease;
}
/* Topbar logo Ã¢â‚¬â€ NEX white, SET pulsing */
.topbar-logo .logo-nex { color: var(--text-primary); }
.topbar-logo .logo-set {
  color: var(--theme-color);
  animation: logo-set-pulse 3s ease-in-out infinite;
}
.menu-btn span {
  display: block;
  width: 22px; height: 2px;
  background: var(--text-primary);
  border-radius: 1px;
  transition: all 0.3s;
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   TIMER WIDGET
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
/* Ã¢â€â‚¬Ã¢â€â‚¬ Hide topbar in detail (swipe still works) Ã¢â€â‚¬Ã¢â€â‚¬ */
body.in-detail .topbar {
  opacity: 0;
  pointer-events: none;
}
body.in-support .topbar {
  display: none !important;
}
@media (max-width: 768px) {
  /* C89 : cette valeur passait par env() en direct, alors que la butee de
     l'en-tete colle du Detail vaut var(--safe-top). Deux ecritures de la meme
     grandeur, donc deux valeurs differentes des qu'un test simule un iPhone en
     posant --safe-top : l'en-tete se serait declare « colle » a l'arret, et le
     fondu serait reste allume en haut de page — exactement le defaut que C64 a
     corrige dans la galerie. C'est la lecon de C64 : quand une valeur
     d'environnement rend un defaut invisible au banc, il faut l'indirectionner.
     Le repli change de 44px a 0px au passage : il ne sert que dans un moteur qui
     ignore env(), cas ou 44px de padding pour rien serait de toute facon faux.

     C104 : 12px -> 8px. Un des quatre paliers du vide entre l'encoche et la
     carte, qui totalisaient 92px pour 38px utiles. Le detail passe donc a
     `safe-top + 8` quand la galerie est a `--view-top` = `safe-top + 14` :
     l'ecart de 6px est ASSUME. Ce sont deux ecrans differents relies par un
     fondu, pas un element qui se deplace — et l'alignement commun de C89
     visait le logo et les titres de vue, pas la barre du Detail qui n'a ni
     l'un ni l'autre au repos. */
  body.in-detail .main-content {
    padding-top: calc(var(--safe-top) + 8px) !important;
  }
}

.main-content {
  margin-left: 0;
  padding: 40px 40px 80px;
  min-height: 100vh;
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ VIEWS Ã¢â€â‚¬Ã¢â€â‚¬ */
.view { display: none; }
/* Fondu simple par défaut (hors v2-ambient : écran auth notamment) */
.view.active { display: block; animation: fade-in 0.2s ease; }
/* C106 — retour à un écran déjà vu : pas de ré-animation.
   S'appelait `leaving-detail` et ne s'appliquait JAMAIS : la classe était posée
   puis retirée dans un requestAnimationFrame, or les callbacks rAF tournent
   AVANT le calcul de style de leur frame. Mesuré : la galerie rejouait fade-in
   depuis l'opacité 0 à chaque retour de training. Le mécanisme est passé à un
   drapeau consommé par showView (voir script.js) ; le nom dit maintenant ce que
   la règle fait, et non d'où l'on vient. */
body.vue-sans-anim .view.active { animation: none; }

/* Vues V2 : entrée avec léger slide-fade (opacity + translateY 8px), durée + easing standard */
@keyframes view-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
body.v2-ambient .view.active {
  animation: view-in var(--dur-v2) var(--ease-v2);
}
/* Exclusions transform — dropdown détail et .form-actions fixed de create créent des stacking
   contexts qui se cassent si la vue elle-même est transformée pendant l'anim. En détail/create
   on garde donc un fondu pur (fade-in), sans translateY.

   C106 — `body.on-timer` RETIRÉ de cette liste. Le Timer y figurait par
   association : la raison ci-dessus nomme le dropdown du Détail et le
   `.form-actions` de Création, et ni l'un ni l'autre n'est dans le Timer.
   Mesuré, vue par vue, sur les éléments `fixed` ou `sticky` réellement rendus :

       create   div.form-actions[fixed]     <- la raison, bien réelle
       timer    (aucun)
       pr       (aucun)

   Le Timer était donc le SEUL des cinq onglets du bas à arriver à plat —
   galerie compensée par la cascade `gal-in` de ses sections, pr / prepa /
   profile en `view-in`. Il glisse maintenant comme eux.
   ⚠ Si un élément `fixed` apparaît un jour dans le Timer pendant l'arrivée,
   c'est cette ligne qu'il faudra rouvrir. Le banc mesure la condition. */
body.in-detail .view.active,
body.in-create .view.active {
  animation-name: fade-in;
}
/* Galerie : gal-in porte déjà l'entrée par sections. La vue globale se contente d'un fondu pur
   pour éviter le double-emploi avec le stagger des cat-section. */
body.in-gallery .view.active {
  animation-name: fade-in;
}
/* vue-sans-anim garde sa priorité (animation:none). Réaffirmé pour battre v2-ambient (même spécificité,
   déclarée plus tard). */
body.vue-sans-anim.v2-ambient .view.active { animation: none; }
/* prefers-reduced-motion : plus rien ne bouge.
   ⚠ REDONDANT depuis longtemps — le plancher global vit tout en bas du fichier
   (`*, *::before, *::after`), et il couvre déjà cette règle. Laissé parce qu'il
   ne coûte rien ; à ne pas prendre pour la seule protection. Voir C112, tout en
   bas, pour l'histoire complète et le piège de lecture qui va avec. */
@media (prefers-reduced-motion: reduce) {
  .view.active { animation: none !important; }
}

@keyframes slide-fade-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ VIEW HEADER Ã¢â€â‚¬Ã¢â€â‚¬ */
.view-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 32px;
  flex-wrap: wrap;
  gap: 16px;
}
.gallery-header {
  flex-wrap: nowrap;
}
/* === GALLERY HEADER — sticky, 3 zones (topbar / search / chips) === */
.gallery-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--bg-base);
  margin: 0 -40px;
  padding: var(--view-top) 40px 0; /* aligné sur --view-top (safe + 14) — mêmes hauteur que titres autres vues */
}
/* Étend le fond opaque au-dessus du header pour couvrir l'overscroll (bounce iOS) */
.gallery-header::before {
  content: '';
  position: absolute;
  left: 0; right: 0;
  bottom: 100%;
  height: 100vh;
  background: var(--bg-base);
  pointer-events: none;
}
/* Liseré néon subtil en bas du header sticky (adoucit la coupure header/contenu) */
.gallery-header::after {
  content: '';
  position: absolute;
  left: 15%;
  right: 15%;
  bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--theme-color), transparent);
  opacity: 0.35;
  box-shadow: 0 2px 10px 0 var(--theme-color);
  pointer-events: none;
}
@media (max-width: 768px) {
  .gallery-header {
    margin: 0 -20px;
    padding: var(--view-top) 20px 0;
  }
}
/* Neutralise le padding-top de .main-content en mode galerie pour éviter le doublon safe-area */
body.in-gallery .main-content { padding-top: 0; }

.gallery-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 0 14px; /* padding-top absorbé par .gallery-header (var(--view-top)) — logo à la même hauteur que titres autres vues */
}
.topbar-icon-btn {
  width: 38px; height: 38px;
  border-radius: 10px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  color: var(--text-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  flex-shrink: 0;
  cursor: pointer;
  transition: all 0.2s;
  -webkit-tap-highlight-color: transparent;
}
.topbar-icon-btn svg { width: 18px; height: 18px; }
@media (hover: hover) {
  .topbar-icon-btn:hover {
    background: var(--surface-4);
    border-color: var(--border-bright);
    color: var(--text-primary);
  }
}
.topbar-icon-btn:active { transform: scale(0.92); }

/* Zone 2 : recherche posée en permanence */
.gallery-search-row {
  display: flex;
  margin-bottom: 14px;
}
.gallery-search-row .search-wrap { flex: 1; }

/* Zone 3 : chips de filtre (scroll horizontal) */
.gallery-chips-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding-bottom: 14px;
}
.gallery-chips-row .filter-chips { flex: 1; min-width: 0; }
.gallery-logo {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.6rem;
  letter-spacing: -0.02em;
  display: flex;
  align-items: center;
}
.gallery-logo .logo-nex { color: var(--text-primary); }
.gallery-logo .logo-set { color: var(--theme-color); }
/* .btn-view-toggle : ancien bouton remplacé par .topbar-icon-btn dans le nouveau header */

.view-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.6rem, 4vw, 2.2rem);
  color: var(--text-primary);
  line-height: 1.15;
  letter-spacing: -0.02em;
}

/* ══════════════════════════════════════════
   HEADER BUTTONS — classe commune (dimensions STRICTEMENT identiques toutes vues).
   Toute action de header (icon-only ou pill texte) porte .hdr-btn.
   Variantes : sans modifier → carré 36 icône ; .hdr-btn-text → pill à hauteur 36 avec texte.
   Le dégradé thème reste réservé au FAB galerie et aux boutons de formulaires.
   ══════════════════════════════════════════ */
.hdr-btn {
  width: 36px; height: 36px;
  padding: 0;
  border-radius: 12px;
  background: var(--surface-3);
  border: 1px solid var(--border-1);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background var(--dur-micro) ease, border-color var(--dur-micro) ease, transform var(--dur-micro) ease;
  -webkit-tap-highlight-color: transparent;
  flex-shrink: 0;
  box-sizing: border-box;
}
.hdr-btn svg { width: 18px; height: 18px; stroke-width: 2; }
.hdr-btn:active { transform: scale(.94); }
@media (hover: hover) {
  .hdr-btn:hover { background: var(--surface-4); border-color: var(--border-2); }
}
/* Variante texte (ex. "Aide" profil) : garde hauteur 36 + radius 12, ajoute padding H + gap SVG/texte */
.hdr-btn-text {
  width: auto;
  padding: 0 14px;
  gap: 6px;
  font-size: 0.875rem;
  font-weight: 600;
}
.view-title em {
  font-style: normal;
  color: var(--accent-blue);
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   BUTTONS
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 22px;
  background: var(--gradient-accent);
  background-size: 200% auto;
  color: var(--theme-text-on-accent, #fff);
  border-radius: var(--radius-md);
  font-weight: 600;
  font-size: 0.88rem;
  letter-spacing: 0.02em;
  transition: all 0.25s;
  white-space: nowrap;
  position: relative;
  overflow: hidden;
  box-shadow: 0 4px 20px var(--glow-primary);
}
.btn-primary:active         { transform: scale(0.95); }
.btn-primary svg { width: 16px; height: 16px; position: relative; z-index: 1; }
.btn-primary span { position: relative; z-index: 1; }
@media (hover: hover) {
  .btn-primary:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 28px var(--glow-strong);
    background-position: right center;
  }
}

.btn-secondary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 22px;
  background: var(--bg-hover);
  color: var(--text-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-weight: 500;
  font-size: 0.88rem;
  transition: all 0.2s;
}
@media (hover: hover) {
  .btn-secondary:hover { border-color: var(--border-bright); color: var(--text-primary); }
}

/* C82 — LE `.btn-danger` D'ORIGINE A ETE SUPPRIME ICI : IL NE S'AFFICHAIT PLUS.
   Il declarait onze proprietes, et la passe V2 (l.~5920) redeclare exactement
   les memes, a specificite egale et plus bas dans le fichier. Verifie dans le
   navigateur plutot que deduit : un `.btn-danger` calcule `padding 12px 16px`,
   `rgba(255,71,87,.1)` et `font-weight 600` — les valeurs de V2. Celles d'ici
   (22px de padding, un autre rouge, graisse 500) n'atteignaient aucun ecran.
   Sont partis avec : `.btn-danger svg` et le `:hover`, tous deux redeclares par
   V2 ; et `.btn-danger:active { scale(.95) }`, battu par la regle globale
   `button:active:not(.btn-primary)` — `:not()` compte son argument, donc
   (0,2,1) contre (0,2,0), quel que soit l'ordre des fichiers.
   ⚠ CE QUI RESTE CI-DESSOUS N'EST PAS UN OUBLI : `.modal-footer .btn-danger`
   a une specificite superieure (0,2,0) et reste la seule regle qui donne au
   bouton rouge sa largeur dans un pied de modale. */
.modal-footer .btn-danger { flex: 1; justify-content: center; }

.format-btn-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.btn-format {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 14px 20px;
  background: linear-gradient(135deg, rgba(59,139,255,0.15), rgba(167,139,250,0.15));
  border: 1px solid rgba(59,139,255,0.3);
  color: var(--accent-blue);
  border-radius: var(--radius-md);
  font-weight: 600;
  font-size: 0.92rem;
  letter-spacing: 0.05em;
  transition: all 0.25s;
  flex: 1;
  justify-content: center;
  touch-action: manipulation;
}
.btn-format svg { width: 16px; height: 16px; }
@media (hover: hover) {
  .btn-format:hover {
    background: rgba(59,139,255,0.2);
    border-color: rgba(59,139,255,0.6);
    box-shadow: 0 0 24px rgba(59,139,255,0.2);
  }
}
.btn-format-undo svg { width: 15px; height: 15px; }
@media (hover: hover) {
  .btn-format-undo:hover {
    background: rgba(255, 100, 100, 0.18);
    border-color: rgba(255, 100, 100, 0.55);
  }
}
.btn-format-undo.is-redo {
  border-color: rgba(80, 200, 120, 0.35);
  background: rgba(80, 200, 120, 0.08);
  color: rgba(100, 220, 140, 0.9);
}
@media (hover: hover) {
  .btn-format-undo.is-redo:hover {
    background: rgba(80, 200, 120, 0.18);
    border-color: rgba(80, 200, 120, 0.55);
  }
}

.btn-format-ai {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 14px 18px;
  background: linear-gradient(135deg, var(--glow-primary), rgba(255,255,255,0.05));
  border: 1px solid var(--border-bright);
  color: var(--text-primary);
  border-radius: var(--radius-md);
  font-weight: 700;
  font-size: 0.88rem;
  letter-spacing: 0.08em;
  transition: all 0.25s;
  white-space: nowrap;
  cursor: pointer;
  touch-action: manipulation;
  flex: 1;
  justify-content: center;
}
.btn-format-ai svg { width: 15px; height: 15px; }
@media (hover: hover) {
  .btn-format-ai:hover {
    background: var(--glow-primary);
    box-shadow: none;
    border-color: var(--border-bright);
  }
}
.btn-format-ai:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.btn-format-photo {
  flex: 1;
  height: auto;
  min-height: 48px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-card);
  touch-action: manipulation;
}
.btn-format-photo svg { width: 18px; height: 18px; }
.btn-format-photo:disabled { opacity: 0.5; cursor: not-allowed; }

.btn-icon {
  width: 38px; height: 38px;
  border-radius: var(--radius-sm);
  background: var(--bg-hover);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s;
  -webkit-tap-highlight-color: transparent;
}
/* Quand un .btn-icon porte AUSSI .hdr-btn : le standard header prime.
   .btn-icon (source ligne 852) surchargeait .hdr-btn (ligne 642, spé égale, source plus tardif) —
   d'où liseré cyan résiduel (var(--border) tinté). Compound selector spé 0-2-0 tranche. */
.btn-icon.hdr-btn {
  width: 36px; height: 36px;
  border-radius: 12px;
  background: var(--surface-3);
  border: 1px solid var(--border-1);
  color: #fff;
}
.btn-icon svg { width: 16px; height: 16px; }
@media (hover: hover) {
  .btn-icon:hover { border-color: var(--border-bright); color: var(--text-primary); }
}
.btn-icon:focus { outline: none; }
.btn-icon:active { opacity: 0.7; }
@media (hover: hover) {
  .btn-icon.btn-danger:hover { border-color: var(--accent-red); color: var(--accent-red); background: rgba(255,71,87,0.1); }
}

.btn-back {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 18px;
  background: var(--bg-hover);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-secondary);
  font-size: 0.88rem;
  font-weight: 500;
  transition: all 0.2s;
}
.btn-back svg { width: 16px; height: 16px; }
@media (hover: hover) {
  .btn-back:hover { color: var(--text-primary); border-color: var(--border-bright); }
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   SEARCH + FILTERS
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
/* Legacy .search-filter-bar / .search-row : remplacés par .gallery-search-row + .gallery-chips-row dans le header sticky */
.gallery-search-row .search-wrap { flex: 1; }
.search-wrap {
  position: relative;
}
.search-icon {
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  width: 16px; height: 16px;
  stroke: var(--text-muted);
  pointer-events: none;
  z-index: 1;
  flex-shrink: 0;
}
.search-input {
  width: 100%;
  padding: 13px 16px 13px 42px;
  background: var(--bg-input);
  backdrop-filter: blur(8px);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-size: 0.9rem;
  transition: all 0.2s;
  outline: none;
}
.search-input::placeholder { color: var(--text-muted); }
.search-input:focus {
  border-color: var(--theme-color);
  box-shadow: none;   /* C15 : etait `0`, invalide donc ignore */
}
.search-clear {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--text-muted);
  color: var(--bg-base);
  font-size: var(--fs-xs);
  font-weight: 700;
  line-height: 1;
  display: none;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border: none;
  padding: 0;
  flex-shrink: 0;
  touch-action: manipulation;
  transition: background 0.15s;
}
@media (hover: hover) {
  .search-clear:hover { background: var(--text-secondary); }
}
.search-clear.visible { display: flex; }
.search-input.has-value { padding-right: 40px; }

.filter-chips {
  display: flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  gap: 8px;
  padding-bottom: 4px;
}
.filter-chips::-webkit-scrollbar {
  display: none;
}
.chip {
  padding: 7px 16px;
  border-radius: 100px;
  background: var(--bg-input);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  font-size: 0.82rem;
  font-weight: 500;
  transition: all 0.2s;
  white-space: nowrap;
  letter-spacing: 0;
  flex-shrink: 0;
}
@media (hover: hover) {
  .chip:hover { border-color: var(--border-bright); color: var(--text-primary); }
}
.chip.active {
  background: linear-gradient(135deg, var(--glow-primary), rgba(255,255,255,0.06));
  border-color: var(--theme-color);
  color: var(--theme-color);
  box-shadow: none;
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   CARDS GRID
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.cards-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 20px;
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ LIST VIEW Ã¢â€â‚¬Ã¢â€â‚¬ */
.cards-grid.list-view {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.list-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 12px;
  height: 56px;
  padding: 0 16px;
  background: var(--bg-card);
  border-radius: 12px;
  border: 1px solid var(--border);
  cursor: pointer;
  overflow: hidden;
  contain: layout style paint;
  -webkit-tap-highlight-color: transparent;
}
.list-row:active {
  background: var(--bg-hover);
  transform: scale(0.98);
}

.workout-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  padding: 13px 20px 14px;
  cursor: pointer;
  transition: all 0.28s cubic-bezier(0.4,0,0.2,1);
  position: relative;
  overflow: hidden;
  contain: layout style paint;
}
.workout-card:active { transform: scale(0.97); }
.workout-card::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--card-accent, var(--accent-blue)), transparent);
  opacity: 0.4;
}
.workout-card::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: var(--radius-xl);
  background: radial-gradient(ellipse at 50% 0%, var(--glow-card), transparent 70%);
  opacity: 0;
  transition: opacity 0.3s;
  pointer-events: none;
}
@media (hover: hover) {
  .workout-card:hover {
    border-color: var(--border-bright);
    transform: translateY(-4px);
    box-shadow: var(--shadow-card);
  }
  .workout-card:hover::after { opacity: 1; }
}

.card-diff {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.7;
}
/* ── CARD REDESIGN — line 1 (mini set + name + fav) + Apple-Fitness stat columns ── */
.card-top-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.card-stat-val.is-empty {
  color: var(--text-muted);
  opacity: 0.5;
}

/* Difficulty colors */
.diff-light { color: var(--accent-green); }
/* Difficulty colors Ã¢â‚¬â€ fixed, indÃƒÂ©pendants du thÃƒÂ¨me */
.diff-easy   { color: var(--ok); }
.diff-medium { color: var(--warn); }
.diff-hard   { color: var(--danger); }
.diff-max    { color: #a855f7; }

/* Left border + glow are now applied inline via JS using the category color */


/* Type accent colors */
.type-pull  { --card-accent: #3b8bff; }

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   EMPTY STATE
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
/* C17 : etat vide unifie sur le motif de Records (C14) — icone, titre, phrase,
   action. L'ancienne version etait un emoji de 3rem et une phrase constatant un
   manque. .empty-icon disparait avec l'emoji qu'il dimensionnait. */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 64px 28px;
  text-align: center;
  color: var(--text-2);
}
.empty-state-icone {
  width: 40px; height: 40px;
  fill: none;
  stroke: var(--text-3);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.empty-state-titre {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 800;
  color: var(--text-primary);
}
.empty-state-texte {
  font-size: var(--fs-sm);
  color: var(--text-2);
  max-width: 32ch;
  line-height: 1.5;
}
.empty-state .btn-primary { margin-top: 6px; }
.empty-state-icon {
  color: var(--theme-color);
  fill: var(--theme-color);
  stroke: none;
  filter: drop-shadow(0 0 8px rgba(47, 123, 255, 0.45));
  display: block;
  margin: 0 auto;
}
.empty-state p { font-size: 1rem; margin-bottom: 24px; }

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   CREATE FORM
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.create-form {
  max-width: 720px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
/* #formRowNameDate est un conteneur legacy (date de création) dont le seul enfant #formGroupDate
   est display:none. Le laisser visible ajoute un flex-gap parasite entre NOM et TYPE (18px doublé →
   36px). Hidden globalement : le JS peut toujours forcer display via style inline en édition,
   mais on retire le doublon dans le rythme vertical. */
#formRowNameDate { display: none; }
.form-row { display: flex; gap: 16px; }
.form-row.two-col > * { flex: 1; }

/* Type + Difficulté : catégorie large, difficulté étroite (juste pour "Medium"), même ligne en mobile */
#formRowTypeDiff { flex-direction: row; }
#formRowTypeDiff > .form-group:first-child { flex: 1; }
#formRowTypeDiff > .form-group:last-child  { flex: 0 0 110px; }
@media (max-width: 768px) {
  #formRowTypeDiff > .form-group:last-child { flex: 0 0 95px; }
}

.form-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.form-group label {
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.form-input {
  padding: 13px 15px;
  background: var(--bg-input);
  backdrop-filter: blur(8px);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-size: 0.92rem;
  outline: none;
  transition: all 0.2s;
  -webkit-appearance: none;
}
/* C25 : REGLE DE BASE DES PLACEHOLDERS, qui manquait.
   Le theme clair avait sa regle generique (l.9498) mais le theme sombre, aucune : douze
   regles ponctuelles patchaient des modales nommees une par une, et tout champ absent de
   cette liste retombait sur le gris par defaut de Chromium, #757575, soit 3.42:1 sur le
   fond de champ. C'etait le cas de la duree et des notes de seance, et du mot personnalise.
   Corriger les trois champs fautifs aurait laisse le piege intact pour le suivant : c'est
   le defaut de base qu'il faut poser. --text-3 donne 5.18:1, tout en restant nettement
   plus terne que la valeur saisie (14.56:1), pour qu'on distingue l'un de l'autre.
   Specificite (0,1,1) : l'override clair (0,2,1) et les regles ciblees passent devant. */
.form-input::placeholder,
.form-textarea::placeholder { color: var(--text-3); opacity: 1; }
.form-input:focus {
  border-color: var(--theme-color);
  box-shadow: none;   /* C15 : etait `0`, invalide donc ignore */
}
select.form-input option { background: var(--bg-surface); }

/* Champ mot de passe avec œil afficher/masquer (pattern miroir de .search-wrap/.search-clear).
   N'ajoute qu'un wrapper positionné + padding-right sur l'input — n'altère PAS le fix caret iOS
   (font-size 16px + backdrop-filter:none restent portés par .auth-form/.#modal .form-input). */
.pwd-field { position: relative; }
.pwd-field .form-input { width: 100%; box-sizing: border-box; padding-right: 44px; }
.pwd-toggle {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;
  background: none; border: none; padding: 0; cursor: pointer; border-radius: 8px;
  color: var(--text-3); touch-action: manipulation;
}
.pwd-toggle svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.pwd-toggle .eye-off { display: none; }
.pwd-toggle.revealed .eye-open { display: none; }
.pwd-toggle.revealed .eye-off { display: block; }
@media (hover: hover) { .pwd-toggle:hover { color: #fff; } }

.form-textarea {
  padding: 14px 15px;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-size: 0.88rem;
  font-family: var(--font-mono);
  line-height: 1.7;
  resize: vertical;
  outline: none;
  transition: all 0.2s;
}
.form-textarea:focus {
  border-color: rgba(59,139,255,0.4);
  box-shadow: 0 0 0 3px rgba(59,139,255,0.08);
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ FORMATTED PREVIEW Ã¢â€â‚¬Ã¢â€â‚¬ */
.program-preview-wrap {
  background: var(--bg-card);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  padding: 20px 24px;
  margin-bottom: 16px;
  animation: slide-fade-in 0.2s ease;
}
.preview-content .rep    { color: var(--accent-blue); font-weight: 700; }
.preview-content .weight { color: var(--accent-red);  font-weight: 700; }
.preview-content .set-line { display: block; padding: 2px 0; }
.preview-content .set-sep  { color: var(--text-muted); margin: 0 2px; }

.form-actions {
  display: flex;
  gap: 12px;
  justify-content: flex-end;
  padding-top: 8px;
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   DETAIL VIEW
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.detail-actions { display: flex; gap: 10px; }

.detail-content {
  max-width: 720px;
  /* C104 : `margin-top: 4px` retire. Il ne produisait RIEN — un element sticky
     fusionne ses marges avec ses freres, et la marge du bas de `.entete-colle`
     (24px alors, 14 aujourd'hui) l'absorbait entierement. Mesure avant retrait :
     bas de la barre 127 + 24 = haut du hero 151, au pixel. Le 4 n'apparaissait
     nulle part. Une valeur qui ne se voit pas est une valeur qui ment. */
  will-change: transform;
}
.detail-hero {
  background: var(--bg-card);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  padding: 18px 24px;
  margin-bottom: 16px;
  box-shadow: 0 4px 32px var(--glow-card);
  position: relative;
  overflow: hidden;
}
.detail-hero::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--border-bright), transparent);
}
.detail-badge {
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 5px 12px;
  border-radius: 100px;
  background: var(--bg-card);
  backdrop-filter: blur(8px);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  transition: border-color 0.2s;
}
.detail-badge:first-child {
  border-color: var(--border-bright);
  color: var(--theme-color);
  background: var(--glow-card);
}
/* === HERO DETAIL v2 — centered layout + stat columns === */
.detail-hero {
  padding: 16px 20px;
  text-align: center;
}
.detail-hero .card-fav-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 32px;
  height: 32px;
  margin: 0;
  z-index: 2;
}
.detail-hero .card-fav-btn svg { width: 18px; height: 18px; }
.detail-hero-cat {
  text-align: center;
  margin-bottom: 3px;
  min-width: 0;
}
.detail-hero-cat .detail-badge {
  padding: 0;
  border: none;
  background: none;
  border-radius: 0;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  max-width: 100%;
  display: inline-block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}
.detail-hero-meta2 {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 6px;
  font-size: 0.72rem;
  color: var(--text-muted);
  /* C28 : 12px ajoutes au-dessus. « Medium » et la date sont cliquables mais ne font
     que 14px de haut ; les porter a 44px demande d'etendre la zone de 15px de part et
     d'autre, ce qui recouvrait le centre du badge de categorie situe 3px plus haut —
     un appui sur « Endurance » aurait ouvert le choix de difficulte. Le test l'a
     attrape des la premiere tentative. Il faut plus de 22px entre deux centres pour
     que deux zones de 44px ne s'interceptent pas : on passe de 23px (marge de 1px,
     intenable) a 35px. Le hero grandit de 12px et respire. */
  margin-top: 12px;
  margin-bottom: 13px;
  flex-wrap: nowrap;
  min-width: 0;
}
.detail-hero-meta2 > * { white-space: nowrap; }
.detail-hero-meta2 .card-diff {
  margin-left: 0;
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  opacity: 1;
}
.detail-hero-meta2 .detail-date-span {
  font-size: 0.72rem;
  letter-spacing: 0.02em;
}
.detail-hero-sep {
  /* C11 : --text-muted EST deja le role atténué. Lui rajouter opacity .5 empilait
     deux atténuations et tombait a 2.4:1 — le séparateur devenait invisible, donc
     il ne séparait plus rien. La hiérarchie tient par le token, pas par l'opacity. */
  color: var(--text-muted);
}
.detail-stat-row {
  display: flex;
  margin-top: 4px;
  padding: 6px 0;
}
.detail-stat-col {
  flex: 1;
  text-align: center;
}
.detail-stat-val {
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 700;
  color: var(--text-secondary);
  line-height: 1;
}
.detail-stat-val.is-empty {
  color: var(--text-muted);
  opacity: 0.5;
}
.detail-stat-label {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  letter-spacing: 0.1em;
  margin-top: 4px;
}
.detail-stat-sep {
  width: 1px;
  background: var(--border);
  flex-shrink: 0;
}
.detail-objective-line {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 6px;
  margin-top: 8px;
}
.detail-hero .detail-objective {
  /* C40 : etait `center`. Corrige ICI plutot que par une regle en fin de fichier —
     celle-ci est scopee (0,2,0) et l'aurait emporte sur toute addition generique.
     C'etait le dernier bloc du hero a ne pas suivre l'axe gauche. */
  text-align: left;
  font-style: italic;
  min-height: calc(0.84rem * 1.4 * 2);
  color: var(--text-muted);
}
.detail-objective-line > .detail-objective-input {
  flex: 1;
  min-width: 0;
}
/* C11 : cette regle (0,2,0) battait le correctif porte par .detail-objective-empty
   seule (0,1,0) et remettait une opacity. Corrige a la source, comme le veut la
   regle du projet : on patche la regle scopee existante, pas par une nouvelle en
   fin de fichier qui perdrait. */
/* C55/C7 : remonte de --text-3 a --text-secondary, en meme temps que le « + ».
   Patche ICI et pas en fin de fichier — cette regle est scopee (0,2,0) et
   l'emporterait sur toute addition generique. C'est precisement ce que le
   commentaire de C11 ci-dessus annonce ; il aurait suffi de ne pas le lire pour
   refaire le bug. */
.detail-hero .detail-objective-empty { color: var(--text-secondary); }
.detail-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.4rem, 3vw, 1.9rem);
  line-height: 1.2;
  margin-bottom: 8px;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.detail-date-notes {
  font-size: 0.84rem;
  color: var(--text-muted);
  font-family: var(--font-body);
}
.detail-date-span {
  color: var(--text-muted);
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ INLINE EDITING Ã¢â€â‚¬Ã¢â€â‚¬ */
.detail-title--editable {
  cursor: pointer;
  border-radius: 6px;
  transition: opacity 0.15s;
}
@media (hover: hover) {
  .detail-title--editable:hover { opacity: 0.75; }
}
.detail-title--editable:active { opacity: 0.5; }

.detail-badge--editable {
  cursor: pointer;
  transition: opacity 0.15s;
}
@media (hover: hover) {
  .detail-badge--editable:hover { opacity: 0.75; }
}
.detail-badge--editable:active { opacity: 0.5; }

.diff-editable {
  cursor: pointer;
  transition: opacity 0.15s;
}
@media (hover: hover) {
  .diff-editable:hover { opacity: 0.65; }
}
.diff-editable:active { opacity: 0.4; }

.detail-date--editable {
  cursor: pointer;
  transition: opacity 0.15s;
}
@media (hover: hover) {
  .detail-date--editable:hover { opacity: 0.7; }
}

.detail-title-input {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.4rem, 3vw, 1.9rem);
  line-height: 1.2;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  background: transparent;
  border: none;
  border-bottom: 2px solid var(--theme-color);
  color: var(--text-primary);
  outline: none;
  width: 100%;
  margin-bottom: 8px;
  padding: 0 0 2px;
}

.detail-date-input {
  font-family: var(--font-body);
  font-size: 0.84rem;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--theme-color);
  color: var(--text-secondary);
  outline: none;
  padding: 0 0 1px;
  cursor: pointer;
  color-scheme: dark;
}

/* Objectif (t.notes) — éditable inline, 2 lignes réservées pour hauteur uniforme */
.detail-objective {
  font-size: 0.84rem;
  color: var(--text-secondary);
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  flex: 1;
  min-width: 0;
  min-height: calc(0.84rem * 1.4 * 2);
}
.detail-objective--editable { cursor: pointer; transition: opacity 0.15s; }
@media (hover: hover) {
  .detail-objective--editable:hover { opacity: 0.75; }
}
/* C11 : opacity .5 -> 2.89:1. Un placeholder doit rester lisible : c'est lui qui
   dit qu'on peut remplir le champ.
   C55/C7 : C11 concluait « l'italique porte l'affordance ». Sur un ecran tactile,
   non — il n'y a pas de curseur pour confirmer, et un texte italique gris se lit
   comme DESACTIVE. L'affordance passe donc a un « + » explicite, et la couleur
   remonte de --text-3 a --text-secondary : lisible sans devenir un titre.
   L'italique est conserve : il distingue le placeholder d'un objectif reel. */
.detail-objective-empty {
  color: var(--text-secondary);
  font-style: italic;
  display: flex;
  align-items: center;
  gap: 5px;
}
.obj-plus {
  width: 13px; height: 13px; flex-shrink: 0;
  fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round;
}
/* C11 : placeholder de date, meme famille que l'objectif vide. L'italique porte
   l'affordance ; on passe par un token plutot que par un opacity, qui ferait
   retomber le contraste sous le seuil comme le fait .detail-objective-empty. */
.detail-date-empty { color: var(--text-3); font-style: italic; }
.detail-objective-input {
  font-family: var(--font-body);
  font-size: 0.84rem;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--theme-color);
  color: var(--text-primary);
  outline: none;
  width: 100%;
  padding: 0 0 2px;
}

.inline-dropdown {
  background: rgba(24,24,32,0.96);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--border-bright);
  border-radius: 12px;
  padding: 6px;
  min-width: 160px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.5);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.inline-dropdown-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border-radius: 8px;
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background 0.15s;
  -webkit-tap-highlight-color: transparent;
}
@media (hover: hover) {
  .inline-dropdown-item:hover { background: var(--bg-hover); }
}
.inline-dropdown-item:active { background: var(--surface-4); transition: none; }
.inline-dropdown-item.selected { color: var(--text-primary); background: var(--bg-input); }
.inline-dd-swatch {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
}

.detail-workout-block {
  background: var(--bg-card);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  padding: 24px 20px;
  box-shadow: 0 4px 24px var(--glow-card);
}
.detail-workout-header {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--theme-color);
  opacity: 0.85;
  margin-bottom: 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.detail-workout-body {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  line-height: 1.9;
}
.detail-workout-body .rep    { color: var(--accent-blue); font-weight: 700; }
.detail-workout-body .weight { color: var(--accent-red);  font-weight: 700; }
.detail-workout-body .set-line { display: block; padding: 2px 0; }

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   HISTORY
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.history-list {
  max-width: 720px;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.history-item {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 20px;
  background: var(--bg-card);
  cursor: pointer;
  transition: all 0.2s;
  border-radius: 0;
  border-bottom: 1px solid var(--border);
}
.history-item:first-child { border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
.history-item:last-child  { border-radius: 0 0 var(--radius-lg) var(--radius-lg); border-bottom: none; }
.history-item:only-child  { border-radius: var(--radius-lg); }
@media (hover: hover) {
  .history-item:hover { background: var(--bg-card); }
}

.history-date {
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--text-muted);
  min-width: 90px;
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   FULLSCREEN OVERLAY
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.fullscreen-overlay {
  position: fixed;
  inset: 0;
  background: var(--bg-base);
  z-index: 999;
  display: none;
  overflow: hidden;
  min-height: 100vh;
}
.fullscreen-overlay.visible { display: block; }
.fullscreen-overlay::before { display: none; }
.fullscreen-close {
  position: fixed;
  top: calc(var(--safe-top) + 14px);
  right: 16px;
  width: 40px; height: 40px;
  border-radius: 50%;
  background: var(--surface-4);
  border: 1px solid var(--border-2);
  color: var(--text-secondary);
  font-size: 1rem;
  display: flex; align-items: center; justify-content: center;
  transition: all 0.2s;
  z-index: 1001;
  touch-action: manipulation;
  cursor: pointer;
}
@media (hover: hover) {
  .fullscreen-close:hover { background: rgba(255,255,255,0.18); color: var(--text-primary); }
}


.fs-pan-zone {
  position: absolute;
  inset: 0;
  touch-action: none;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.fs-pan-zone #fullscreenBody {
  width: 100%;
  will-change: transform;
  padding: 24px;
  box-sizing: border-box;
  flex-shrink: 0;
}
.fullscreen-content .rep    { color: var(--accent-blue); font-weight: 700; }
.fullscreen-content .weight { color: var(--accent-red);  font-weight: 700; }
.fullscreen-content .set-line { display: block; }

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   TOAST
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.toast {
  position: fixed;
  bottom: 28px;
  left: 50%;
  transform: translateX(-50%) translateY(20px);
  background: rgba(8,6,22,0.88);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius-md);
  padding: 13px 22px;
  font-size: 0.88rem;
  box-shadow: 0 8px 32px rgba(0,0,0,0.5);
  font-weight: 500;
  color: var(--text-primary);
  pointer-events: none;
  opacity: 0;
  transition: all 0.3s cubic-bezier(0.4,0,0.2,1);
  z-index: 1000;
  white-space: nowrap;
}
.toast.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   RESPONSIVE Ã¢â‚¬â€ MOBILE
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
@media (max-width: 768px) {
  .sidebar {
    transform: translateX(-100%);
    z-index: 300;
    box-shadow: 8px 0 40px rgba(0,0,0,0.6);
  }
  .sidebar.open { transform: translateX(0); }

  .topbar { display: flex; }

  .main-content {
    margin-left: 0;
    padding: var(--view-top) 20px 100px; /* aligné sur --view-top — titres à la même hauteur écran que gallery/settings */
  }

  .form-row.two-col { flex-direction: column; }
  .cards-grid { grid-template-columns: 1fr; }

  .view-title { font-size: 1.6rem; }

  .detail-actions .btn-icon { width: 34px; height: 34px; }

  /* Mobile nav overlay */
  .nav-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.6);
    z-index: 290;
  }
  .nav-overlay.visible { display: block; }
}

@media (max-width: 480px) {
  .filter-chips { gap: 6px; }
  .chip { padding: 6px 12px; font-size: 0.78rem; }
  .form-actions { flex-direction: column-reverse; }
  .form-actions .btn-primary,
  .form-actions .btn-secondary { width: 100%; justify-content: center; }
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   NEXSET v2 Ã¢â‚¬â€ Nouveaux ÃƒÂ©lÃƒÂ©ments
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */

/* Ã¢â€â‚¬Ã¢â€â‚¬ CARD SET NUMBER Ã¢â€â‚¬Ã¢â€â‚¬ */

/* Ã¢â€â‚¬Ã¢â€â‚¬ DETAIL SET NUMBER Ã¢â€â‚¬Ã¢â€â‚¬ */
.detail-set-number {
  font-family: var(--font-body);
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--theme-color);
  background: var(--glow-card);
  border: 1px solid var(--border-bright);
  padding: 4px 10px;
  border-radius: 8px;
  letter-spacing: 0.08em;
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ DETAIL SESSIONS BLOCK Ã¢â€â‚¬Ã¢â€â‚¬ */
.detail-sessions-block {
  background: var(--bg-card);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  padding: 24px 20px;
  margin-top: 16px;
  box-shadow: 0 4px 20px var(--glow-card);
}
.detail-sessions-block.empty-sessions .detail-workout-header {
  margin-bottom: 16px;
}
.no-sessions {
  font-size: 0.85rem;
  color: var(--text-muted);
  text-align: center;
  padding: 16px 0 4px;
}

.btn-add-session {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  background: rgba(59,139,255,0.12);
  border: 1px solid rgba(59,139,255,0.25);
  border-radius: var(--radius-md);
  color: var(--accent-blue);
  font-size: 0.8rem;
  font-weight: 600;
  transition: all 0.2s;
}
.btn-add-session svg { width: 13px; height: 13px; }
@media (hover: hover) {
  .btn-add-session:hover { background: rgba(59,139,255,0.22); }
}

.sessions-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 4px;
}
.session-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  gap: 12px;
}
.session-item-left {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1;
  min-width: 0;
}
.session-num {
  font-family: var(--font-body);
  font-size: 0.75rem;
  color: var(--text-muted);
  min-width: 24px;
}
.session-date {
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text-primary);
}
.session-text {
  min-width: 0;
  flex: 1;
}
.session-notes {
  font-size: 0.78rem;
  line-height: 1.4;
  color: var(--text-secondary);
  margin-top: 2px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: calc(0.78rem * 1.4 * 2);
}
.session-notes-empty {
  opacity: 0.5;
  font-style: italic;
}
.session-notes-textarea {
  font-family: var(--font-body);
  resize: vertical;
  min-height: 70px;
  line-height: 1.45;
}
.session-right {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.session-duration {
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--accent-green);
  background: rgba(46,213,115,0.1);
  padding: 3px 8px;
  border-radius: 6px;
}
@media (hover: hover) {
  .session-delete-btn:hover { opacity: 1; }
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ HISTORY UPDATES Ã¢â€â‚¬Ã¢â€â‚¬ */
.history-section-title {
  font-family: var(--font-display);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 0 4px 10px;
  margin-top: 8px;
}
.history-right {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ HISTORY MONTH GROUPS Ã¢â€â‚¬Ã¢â€â‚¬ */
.history-month-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 24px 0 10px;
}
.history-month-label {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--theme-color);
  opacity: 0.5;
  white-space: nowrap;
}
.history-month-line {
  flex: 1;
  height: 1px;
  background: var(--bg-card);
}
.history-month-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.history-session-card {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px 14px 20px;
  background: var(--bg-card);
  border: 1px solid var(--border-1);
  border-radius: 16px;
  cursor: pointer;
  transition: background 0.2s;
  animation: histSlideUp 0.4s ease both;
  overflow: hidden;
}
.history-session-card::before {
  content: '';
  position: absolute;
  left: 0;
  top: 10px;
  bottom: 10px;
  width: 3px;
  background: var(--theme-color);
  opacity: 0.4;
  border-radius: 0 2px 2px 0;
}
.history-session-card:active { background: var(--bg-hover); }
.hsc-date {
  font-size: 0.72rem;
  color: var(--text-muted);
  font-family: var(--font-body);
  white-space: nowrap;
  min-width: 72px;
}
.hsc-name {
  flex: 1;
  font-size: 0.88rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hsc-duration {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--theme-color);
  white-space: nowrap;
}
@keyframes histSlideUp {
  from { transform: translateY(14px); }
  to   { transform: translateY(0); }
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ SETTINGS / CATEGORIES Ã¢â€â‚¬Ã¢â€â‚¬ */
.settings-form {
  max-width: 600px;
}
body.in-settings .settings-form { min-height: 0; }
.settings-desc {
  font-size: 0.88rem;
  color: var(--text-secondary);
  margin-bottom: 24px;
  line-height: 1.6;
}
.categories-list {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin-bottom: 20px;
  background: var(--surface-2);
  backdrop-filter: blur(12px);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.category-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
  background: transparent;
  transition: background 0.15s;
}
.category-row:last-child { border-bottom: none; }
@media (hover: hover) {
  .category-row:hover { background: var(--bg-input); }
}
/* C55/C6 — la pastille faisait 14x14 : une cible tactile trois fois trop petite
   (44px est la regle, tenue partout ailleurs depuis C28). On garde le POINT a
   14px — c'est un reperage de couleur, le grossir en ferait un bouton — et on
   lui donne une zone d'appui de 44x44 autour, via un pseudo-element. Le rendu ne
   bouge pas d'un pixel, la cible triple. */
.category-color-dot {
  position: relative;
  width: 14px; height: 14px;
  border-radius: 50%;
  flex-shrink: 0;
  border: none;
  cursor: pointer;
  background: var(--dot, var(--theme-color));
  transition: transform 0.15s, box-shadow 0.15s;
}
.category-color-dot::after {
  content: '';
  position: absolute;
  /* -15px de chaque cote : 14 + 15 + 15 = 44. */
  inset: -15px;
  border-radius: 50%;
}
@media (hover: hover) {
  .category-color-dot:hover { transform: scale(1.2); box-shadow: 0 0 8px rgba(255,255,255,0.3); }
}

/* Le bouton de renommage. Meme gabarit que la croix, mais en couleur neutre :
   l'action courante ne doit pas crier plus fort que la destructrice, elle doit
   simplement EXISTER a l'oeil. */
.cat-rename-btn,
.cat-delete-btn {
  width: 34px; height: 34px;
  flex-shrink: 0;
  border-radius: 10px;
}
.cat-rename-btn { color: var(--text-secondary); }
.cat-rename-btn svg { width: 15px; height: 15px; }
.cat-delete-btn svg { width: 14px; height: 14px; }
@media (hover: hover) {
  .cat-rename-btn:hover { color: var(--text-primary); background: var(--bg-hover); }
}

/* Inline color picker (per existing category + new category) */
.cat-inline-picker,
.color-picker-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 10px 0 4px;
  flex-basis: 100%;
}
.color-swatch {
  width: 32px; height: 32px;
  border-radius: 50%;
  border: 2px solid transparent;
  cursor: pointer;
  flex-shrink: 0;
  transition: transform 0.15s, border-color 0.15s;
}
@media (hover: hover) {
  .color-swatch:hover { transform: scale(1.15); }
}
.color-swatch.selected { border-color: #fff; box-shadow: 0 0 0 1px rgba(255,255,255,0.4); }
.color-swatch-custom {
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-card);
  border: 1px solid var(--surface-card-brd);
  position: relative;
}
/* C120 — pictogramme en trait a la place de l'emoji : meme rendu sur toutes les
   plateformes, et il suit la couleur du texte au lieu d'imposer la sienne. */
.color-swatch-custom svg {
  width: 17px; height: 17px;
  fill: none; stroke: var(--text-2); stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
.color-swatch-custom svg circle { fill: var(--text-2); stroke: none; }
.hidden-color-input {
  position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;
}
.category-row { flex-wrap: wrap; }
.category-label {
  font-weight: 600;
  font-size: 0.9rem;
  flex: 1;
}
.category-count {
  font-size: 0.78rem;
  color: var(--text-muted);
}
.add-category-row .form-input { flex: 1; }

/* Ã¢â€â‚¬Ã¢â€â‚¬ MODAL Ã¢â€â‚¬Ã¢â€â‚¬ */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.72);
  z-index: 500;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 20px;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  overscroll-behavior: contain;
}
.modal-overlay.visible { display: flex; }
body:has(.modal-overlay.visible) { overflow: hidden; }
body.modal-open { overflow: hidden; }
#confirmModal { z-index: 1000; }
#reportModal  { z-index: 1000; }
/* ⚠ C82 — CE BLOC A L'AIR PERIME. IL NE L'EST PAS. NE PAS LE SUPPRIMER.
   Un audit l'a signale comme « mort, ~38 lignes trompeuses » parce qu'une
   seconde declaration de `.modal` existe plus bas (passe V2). Verification
   faite dans le navigateur : V2 ne redeclare que six proprietes — fond, les
   deux backdrop-filter, bordure, rayon, ombre. Les CINQ autres viennent d'ICI
   et sont bien celles qui s'appliquent :
       width: 100%   ·   max-width: 480px   ·   animation: slide-up
       position: relative   ·   overflow: hidden
   Le supprimer ferait perdre aux vingt modales leur largeur, leur animation
   d'entree, le rognage de leur contenu, et le reperage de leur `::before`.
   Ce qui rend ce bloc trompeur n'est pas d'etre mort, c'est d'etre a MOITIE
   vivant : on le lit en croyant y voir le style des modales, alors qu'il n'en
   donne que la geometrie. D'ou ce commentaire plutot qu'une suppression. */
.modal {
  background: rgba(6,5,20,0.82);
  backdrop-filter: blur(40px) saturate(160%);
  -webkit-backdrop-filter: blur(40px) saturate(160%);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius-xl);
  width: 100%;
  max-width: 480px;
  box-shadow: 0 24px 80px rgba(0,0,0,0.8);
  animation: slide-up var(--dur-v2) var(--ease-v2);
  position: relative;
  overflow: hidden;
}
/* Modal top luminous line */
.modal::before {
  content: '';
  position: absolute;
  top: 0; left: 8%; right: 8%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--theme-color), transparent);
  opacity: 0.7;
}
.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 24px 22px 12px;
}
.modal-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
}
/* ⚠ C100 — LA CROIX ETAIT UN CARACTERE, PAS UNE ICONE.
   Les quatorze fermetures de l'app affichaient le glyphe texte « ✕ » (U+2715)
   rendu dans Inter. Consequence : son epaisseur suivait la POLICE, pendant que
   toutes les autres icones de l'app sont des SVG a stroke 2 — d'ou un trait
   maigre au milieu d'un jeu d'icones franches, et un centrage optique qui
   depend du rendu de la fonte. Un glyphe dependait aussi de la police
   disponible : rien ne garantissait le meme dessin partout.
   Passe en SVG, comme le reste. La boite reste 30x30, donc la zone tactile
   etendue de C75 (::after 44px) est inchangee. */
.modal-close {
  width: 30px; height: 30px;
  border-radius: 50%;
  background: var(--bg-hover);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s;
}
.modal-close svg { width: 14px; height: 14px; }
/* Meme traitement pour les deux autres fermetures, qui portaient le meme
   glyphe : la croix du plein ecran et celle du champ de recherche. */
.fullscreen-close svg { width: 17px; height: 17px; }
.search-clear svg { width: 12px; height: 12px; }
@media (hover: hover) {
  .modal-close:hover { color: var(--text-primary); }
}
.modal-body {
  padding: 20px 22px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
/* Corps d'annonce : préserve les retours ligne saisis en admin */
.announcement-body {
  white-space: pre-line;
  line-height: 1.5;
  color: var(--text-secondary);
  font-size: 15px;
  margin: 0;
}
.modal-footer {
  display: flex;
  gap: 10px;
  padding: 8px 22px max(24px, env(safe-area-inset-bottom, 24px));
}
.modal-footer .btn-secondary,
.modal-footer .btn-primary {
  flex: 1;
  justify-content: center;
}

.confirm-message {
  font-size: 0.92rem;
  color: var(--text-secondary);
  line-height: 1.6;
  margin: 0;
}

.btn-confirm-ok {
  /* C25 : etait var(--accent-red), qui pointe sur --danger, calibre comme encre et non
     comme fond. Le libelle « Supprimer » y passait a 2.69:1. */
  background: var(--danger-fill) !important;
  box-shadow: none !important;
}
@media (hover: hover) {
  .btn-confirm-ok:hover {
    /* Le survol restait plus CLAIR que le repos, donc encore moins lisible. */
    background: #F91B3B !important;
    box-shadow: 0 4px 16px rgba(255,71,87,0.3) !important;
  }
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ DETAIL ACTIONS UPDATE Ã¢â€â‚¬Ã¢â€â‚¬ */
.detail-actions { display: flex; gap: 8px; flex-wrap: wrap; }

@media (max-width: 480px) {
  .session-item { flex-wrap: wrap; }
  .history-right { flex-direction: column; align-items: flex-end; gap: 4px; }
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   NEXSET v3 Ã¢â‚¬â€ Formatter + Compact Display
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */

/* Ã¢â€â‚¬Ã¢â€â‚¬ WORKOUT DISPLAY: compact, no wrap Ã¢â€â‚¬Ã¢â€â‚¬ */
.detail-workout-body,
.preview-content,
.fullscreen-content {
  font-family: var(--font-mono);
  font-size: 14px;
  line-height: 1.85;
  width: 100%;
  overflow-x: hidden;
}

/* Preview stays clipped */
.preview-content .set-line {
  overflow: hidden;
  text-overflow: ellipsis;
}

.set-line.block-sep {
  height: 10px;
  display: block;
}

/* Weight in brackets Ã¢â‚¬â€ red */
.rep    { color: var(--accent-blue); font-weight: 700; }
.weight { color: var(--accent-red);  font-weight: 700; }

/* EMOM lines */
.emom-line {
  color: var(--accent-purple);
  letter-spacing: 0.02em;
}

/* Fullscreen Ã¢â‚¬â€ lignes avec retour autorisÃƒÂ©, police auto-ajustÃƒÂ©e */
#fullscreenBody {
  font-family: var(--font-mono);
  font-size: clamp(13px, 3.5vw, 18px);
  line-height: 1.4;
  width: 100%;
}
#fullscreenBody .set-line {
  white-space: normal;
  word-break: break-word;
  overflow: visible;
  display: block;
  padding: 2px 0;
}

/* Preview block same rules */
.preview-content .set-line {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   SWIPE EDGE INDICATOR (mobile)
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
@media (max-width: 768px) {
  /* Subtle left-edge indicator so user knows swipe is available */
  .main-content::before {
    content: '';
    position: fixed;
    left: 0;
    top: 30%;
    bottom: 30%;
    width: 3px;
    background: linear-gradient(to bottom, transparent, rgba(59,139,255,0.4), transparent);
    border-radius: 0 3px 3px 0;
    pointer-events: none;
    z-index: 50;
    opacity: 0.5;
  }
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   INLINE DETAIL TIMER
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.inline-timer {
  background: var(--bg-card);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  padding: 14px 20px;
  margin-top: 16px;
  margin-bottom: 0;
  box-shadow: 0 4px 24px var(--glow-card);
}

/* Grid 3 colonnes : zones latérales 1fr symétriques + temps au centre auto.
   → le temps reste au centre absolu peu importe ce qui occupe les côtés. */
.itimer-grid {
  display: grid;
  grid-template-columns: minmax(50px, 1fr) auto minmax(50px, 1fr);
  align-items: center;
  width: 100%;
  /* C15 : 56px etait cale sur un etat ou les cotes sont vides. Avec le bouton de
     lecture au repos, un ecart plus serre tient mieux la carte ensemble. */
  gap: 20px;
}

/* Label haut symétrique au hint bas — équilibre visuel du bloc */
.itimer-toplabel {
  display: block;
  height: 18px;
  line-height: 18px;
  text-align: center;
  /* C15 : 0.8rem etait hors echelle. --text-muted existe, le repli etait inutile. */
  font-size: var(--fs-sm);
  letter-spacing: 0.04em;
  color: var(--text-muted);
  margin-bottom: 4px;
}

.itimer-side {
  display: flex;
  align-items: center;
  gap: 8px;
}
.itimer-side-left  { justify-content: flex-end; }
.itimer-side-right { justify-content: flex-start; }
.itimer-side > button { display: none; }
#inlineTimer[data-state="paused"] .itimer-save-icon  { display: flex; }
#inlineTimer[data-state="paused"] .itimer-reset-icon { display: flex; }
/* C52 : la regle de C15 est retiree avec le bouton. Elle n'affichait le rond de
   lecture qu'au repos, pour combler un repos « muet » — que le hint
   « Appuie pour démarrer » et le « Démarrer » du hero (C43) ne laissent plus
   muet du tout. La colonne gauche redevient vide au repos, ce qu'elle etait
   avant C15 : rien ne se deplace. */

/* Tap-zone centrale : display cliquable, contient uniquement le temps → centre absolu */
.itimer-tap-center {
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  box-shadow: none;
  outline: none;
  padding: 4px 0;
  cursor: pointer;
  color: inherit;
  -webkit-tap-highlight-color: transparent;
  -webkit-appearance: none;
  appearance: none;
  touch-action: manipulation;
}
.itimer-tap-center:focus,
.itimer-tap-center:focus-visible,
.itimer-tap-center:active,
.itimer-tap-center:hover {
  background: transparent !important;
  background-color: transparent !important;
  outline: none !important;
  box-shadow: none !important;
  border: none !important;
  -webkit-tap-highlight-color: transparent !important;
}

/* Hint adaptatif sous le grid — hauteur fixe pour ne pas faire sauter le bloc en countdown */
.itimer-hint {
  display: block;
  height: 18px;
  line-height: 18px;
  text-align: center;
  /* C15 : 0.8rem etait hors echelle. --text-muted existe, le repli etait inutile. */
  font-size: var(--fs-sm);
  letter-spacing: 0.04em;
  color: var(--text-muted);
  margin-top: 4px;
}
.itimer-hint > span { display: none; }
#inlineTimer[data-state="idle"]    .itimer-hint-idle    { display: inline; }
#inlineTimer[data-state="running"] .itimer-hint-running { display: inline; }
#inlineTimer[data-state="paused"]  .itimer-hint-paused  { display: inline; }

.inline-timer-display {
  /* C15 : etait 'Inter' en dur et --theme-color, alors que le Timer principal
     affiche var(--font-display) et var(--text-primary) depuis C10. Deux chronos
     dans la meme app n'ont pas de raison de ne pas se ressembler. */
  font-family: var(--font-display);
  font-size: 2.4rem;
  font-weight: 300;
  color: var(--text-primary);
  letter-spacing: -0.02em;
  transition: color 0.2s, opacity 0.2s;
  font-variant-numeric: tabular-nums;
  height: 52px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Paused : chrono grisé */
#inlineTimer[data-state="paused"] .inline-timer-display {
  color: var(--text-secondary);
  filter: none;
}

/* Running : glow pulsé */
#inlineTimer[data-state="running"] .inline-timer-display {
  animation: pulse-glow 2s ease-in-out infinite;
  color: var(--accent);
}

/* Boutons action (save, reset) — icônes seules, format 50x50 */
.itimer-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0;
  padding: 0;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  transition: all 0.2s;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.itimer-btn:focus { outline: none; }
.itimer-btn:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--theme-color) 50%, transparent);
  outline-offset: 2px;
}

/* C15 : meme langage que .sw-start-btn (C10) — aplat de marque, triangle blanc,
   retour au tap identique. Dimensionne a 44 px : c'est le plancher tactile HIG, et
   un cercle de 72 px ecraserait une carte de 14 px de padding. */
/* Les regles .itimer-play sont retirees avec le bouton (C52). */

.itimer-save {
  background: color-mix(in srgb, var(--theme-color) 15%, transparent);
  border-color: color-mix(in srgb, var(--theme-color) 30%, transparent);
  color: var(--theme-color);
}
@media (hover: hover) {
  .itimer-save:hover {
    background: color-mix(in srgb, var(--theme-color) 25%, transparent);
    box-shadow: 0 0 16px color-mix(in srgb, var(--theme-color) 20%, transparent);
  }
}
.itimer-save-icon {
  width: 50px;
  height: 50px;
  line-height: 0;
  flex-shrink: 0;
}
.itimer-save-icon svg { width: 22px; height: 22px; display: block; }

.itimer-reset {
  background: rgba(255,71,87,0.1);
  border-color: rgba(255,71,87,0.25);
  color: var(--accent-red);
}
@media (hover: hover) {
  .itimer-reset:hover { background: rgba(255,71,87,0.2); }
}
.itimer-reset-icon {
  width: 50px;
  height: 50px;
  line-height: 0;
  flex-shrink: 0;
}
.itimer-reset-icon svg { width: 22px; height: 22px; display: block; }

/* Countdown overlay */
.inline-timer-countdown { display: none !important; }
@keyframes countPulse {
  0%   { transform: scale(1);    opacity: 1; }
  50%  { transform: scale(1.15); opacity: 0.7; }
  100% { transform: scale(1);    opacity: 1; }
}

/* Running state Ã¢â‚¬â€ timer glows */
@media (max-width: 480px) {
  .inline-timer { padding: 12px 14px; }
  .inline-timer-display { font-size: 2rem; }
  .itimer-grid { gap: 32px; }
  .itimer-save-icon, .itimer-reset-icon { width: 44px; height: 44px; }
  .itimer-save-icon svg, .itimer-reset-icon svg { width: 20px; height: 20px; }
  .itimer-toplabel, .itimer-hint { height: 16px; line-height: 16px; font-size: 0.75rem; }
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   FAVORITES
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.card-fav-btn {
  width: 28px; height: 28px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  color: var(--text-muted);
  transition: all 0.2s;
  flex-shrink: 0;
}
.card-fav-btn svg { width: 15px; height: 15px; stroke: currentColor; stroke-width: 2; }
@media (hover: hover) {
  .card-fav-btn:hover { color: var(--accent-gold); }
}
.card-fav-btn.active { 
  color: var(--theme-color);
  filter: drop-shadow(0 0 6px var(--theme-color));
}
.card-fav-btn.active svg { 
  fill: var(--theme-color);
  stroke: var(--theme-color);
}
.detail-fav-btn svg { width: 13px; height: 13px; stroke: currentColor; stroke-width: 2; }
@media (hover: hover) {
  .detail-fav-btn:hover { color: var(--accent-gold); border-color: rgba(255,208,96,0.3); }
}
.detail-fav-btn.active {
  color: var(--theme-color);
  border-color: rgba(var(--theme-color), 0.35);
  background: var(--glow-card);
}
.detail-fav-btn.active svg { fill: var(--theme-color); stroke: var(--theme-color); }

/* Favorite : signalé par le rail gauche doré (inline-style JS), plus de bordure or ni ligne top dorée */

/* ── Désactivation des mises en forme par défaut (perso) ── */
body.fw-off-rep .fw-rep { color: inherit; font-weight: inherit; }
body.fw-off-distance .fw-distance { color: inherit; opacity: 1; font-style: normal; font-weight: inherit; }
body.fw-off-emom .fw-emom,
body.fw-off-emom .fw-emom-label,
body.fw-off-emom .fw-emom-dur { color: inherit; font-weight: inherit; opacity: 1; letter-spacing: normal; font-size: inherit; }
body.fw-off-amrap .fw-amrap,
body.fw-off-amrap .fw-amrap-dur { color: inherit; font-weight: inherit; opacity: 1; letter-spacing: normal; font-size: inherit; }
body.fw-off-rest .fw-rest,
body.fw-off-rest .fw-rest-line,
body.fw-off-rest .fw-rest-line .fw-rest-label { color: inherit; font-style: normal; opacity: 1; }
body.fw-off-timecap .fw-timecap,
body.fw-off-timecap .fw-timecap-num,
body.fw-off-timecap .fw-timecap-line { color: inherit; font-style: normal; }
body.fw-off-weight .fw-weight { background: none; border: none; color: inherit; padding: 0; margin: 0; border-radius: 0; font-size: inherit; font-weight: inherit; letter-spacing: normal; display: inline; }
body.fw-off-time .fw-time-min,
body.fw-off-time .fw-time-sec,
body.fw-off-time .fw-time-prime { color: inherit; opacity: 1; font-weight: inherit; vertical-align: baseline; font-size: inherit; font-family: inherit; letter-spacing: normal; }
body.fw-off-mult .fw-repeat { color: inherit; font-weight: inherit; font-size: inherit; }
body.fw-off-unbroken .fw-unbroken,
body.fw-off-unbroken .fw-unbroken-seg { background: none; border: none; color: inherit; padding: 0; margin: 0; border-radius: 0; font-size: inherit; font-weight: inherit; letter-spacing: normal; text-transform: none; position: static; transform: none; display: inline; }
body.fw-off-unbroken .fw-unbroken::before,
body.fw-off-unbroken .fw-unbroken-seg::before {
  content: " ";
  white-space: pre;
}
body.fw-off-deadstop .fw-badge-deadstop,
body.fw-off-deadstop .fw-badge-deadstop-seg { background: none; border: none; color: inherit; padding: 0; margin: 0; border-radius: 0; font-size: inherit; font-weight: inherit; letter-spacing: normal; text-transform: none; position: static; transform: none; display: inline; }
body.fw-off-deadstop .fw-badge-deadstop::before,
body.fw-off-deadstop .fw-badge-deadstop-seg::before {
  content: " ";
  white-space: pre;
}
body.fw-off-slow .fw-badge-slow { background: none; border: none; color: inherit; padding: 0; margin: 0; border-radius: 0; font-size: inherit; font-weight: inherit; letter-spacing: normal; text-transform: none; position: static; transform: none; display: inline; }
body.fw-off-slow .fw-badge-slow::before {
  content: " ";
  white-space: pre;
}
body.fw-off-explosive .fw-badge-explosive { background: none; border: none; color: inherit; padding: 0; margin: 0; border-radius: 0; font-size: inherit; font-weight: inherit; letter-spacing: normal; text-transform: none; position: static; transform: none; display: inline; }
body.fw-off-explosive .fw-badge-explosive::before {
  content: " ";
  white-space: pre;
}

/* ── Default styles list (Personnalisation > Mises en forme) ── */
.default-styles-list {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin-bottom: 12px;
  background: var(--surface-2);
  backdrop-filter: blur(12px);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.default-style-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 18px;
  background: var(--surface-2);
  flex-wrap: wrap;
}
/* ⚠ C120 — L'ECHANTILLON EST LE CONTENU PRINCIPAL DE LA LIGNE, LE LIBELLE
   PASSE EN SECOND. Meme grammaire que `.custom-style-row` juste en dessous :
   la chose rendue prend la place (`flex: 1`), son nom la suit en gris. Les
   deux listes de Personnalisation se lisent donc a l'identique.
   Le contenu vient de `formatWorkout`, donc il porte les classes `.fw-*` du
   renderer reel : ON NE REDECRIT AUCUNE DE SES PROPRIETES ICI, sinon
   l'echantillon finirait par ne plus ressembler a l'ecran qu'il annonce.
   Seuls la taille et le desemboitement du bloc-programme sont neutralises. */
.default-style-row .dsr-ech {
  flex: 1;
  min-width: 0;
  font-size: 15px;
  line-height: 1.25;
}
.default-style-row .dsr-ech .fw-workout-inner,
.default-style-row .dsr-ech .fw-line,
.default-style-row .dsr-ech .fw-line-content {
  display: inline;
  text-align: left;
  margin: 0;
  padding: 0;
}
.default-style-row .style-label {
  flex: 0 0 auto;
  font-weight: 500;
  font-size: 0.78rem;
  color: var(--text-muted);
}
.default-style-row .style-color-dot {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  flex-shrink: 0;
  border: none;
  cursor: pointer;
  transition: transform 0.15s, box-shadow 0.15s;
}
@media (hover: hover) {
  .default-style-row .style-color-dot:hover {
    transform: scale(1.2);
    box-shadow: 0 0 8px rgba(255,255,255,0.3);
  }
}
.style-inline-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 10px 0 4px;
  flex-basis: 100%;
}

/* Generic toggle pill — reused for default styles toggles */
.fw-style-toggle {
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  user-select: none;
  flex-shrink: 0;
}
.fw-style-toggle-track {
  position: relative;
  width: 36px;
  height: 20px;
  border-radius: 10px;
  background: rgba(136, 136, 136, 0.35);
  transition: background 0.2s;
  flex-shrink: 0;
}
.fw-style-toggle-thumb {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,0.4);
  transition: transform 0.2s, background 0.2s;
}
.fw-style-toggle input:checked ~ .fw-style-toggle-track {
  background: var(--theme-color);
}
.fw-style-toggle input:checked ~ .fw-style-toggle-track .fw-style-toggle-thumb {
  transform: translateX(16px);
}

/* ── Custom styles (mots persos) ── */
.fw-cs-badge-fill   { font-size: var(--fs-xs); font-weight:700; padding:0.1em 0.45em; border-radius:0.4em; border:1px solid transparent; display:inline-block; line-height:1.2; }
.fw-cs-badge-outline{ font-size: var(--fs-xs); font-weight:700; padding:0.1em 0.45em; border-radius:0.4em; border:1px solid transparent; background:transparent; display:inline-block; line-height:1.2; }
.fw-cs-text-bold    { font-weight:700; }
.fw-cs-text-discreet{ font-style:italic; opacity:0.65; }
.custom-style-chips { display:flex; flex-wrap:wrap; gap:8px; }
.custom-style-chip { padding:6px 12px; border-radius:8px; border:1px solid var(--border); background:transparent; color:var(--text-secondary); font-size:0.82rem; cursor:pointer; }
.custom-style-chip.selected { border-color:var(--theme-color); color:var(--text-primary); background: var(--bg-card); }
.custom-style-row { display:flex; align-items:center; gap:12px; padding:12px 18px; flex-wrap: wrap; }
.custom-style-row .cs-preview { flex:1; }
.custom-style-row .cs-type { font-size:0.78rem; color:var(--text-muted); }
.cs-preview, .cs-type { cursor: pointer; }
.category-row.editing,
.custom-style-row.editing {
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
}

.settings-tab-bar {
  display: flex;
  gap: 4px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 4px;
  width: 100%;
  margin-bottom: 20px;
}
.settings-tab {
  flex: 1;
  padding: 9px 4px;
  border: none;
  border-radius: 8px;
  background: none;
  color: var(--text-secondary);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
  touch-action: manipulation;
  transition: background 0.15s, color 0.15s;
  white-space: nowrap;
}
.settings-tab.active {
  background: var(--theme-color);
  color: var(--bg-base);
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   THEME PICKER
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.settings-section-title {
  font-family: var(--font-display);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 16px;
}

.theme-picker {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin-bottom: 8px;
}

.theme-option {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 14px 10px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  cursor: pointer;
  transition: all 0.25s;
  position: relative;
}
@media (hover: hover) {
  .theme-option:hover {
    border-color: var(--border-bright);
    background: var(--bg-card);
  }
}
.theme-option.active {
  border-color: var(--theme-color);
  background: var(--bg-hover);
  box-shadow: none;
}

.theme-preview {
  width: 100%;
  height: 64px;
  border-radius: 10px;
  position: relative;
  overflow: hidden;
}
.theme-preview-blue   { background: #0e1016; }
.theme-preview-light  { background: #e9edf5; }
.theme-preview-light  .tp-orb { background: radial-gradient(circle, rgba(0,120,255,0.35), transparent 70%); }
.theme-preview-light  .tp-card { background: #ffffff; box-shadow: 0 2px 6px rgba(20,28,60,0.12); }

/* C44 — apercu de « Systeme ». Coupe en diagonale : sombre a gauche, clair a droite.
   Les deux vignettes voisines montrent chacune UN fond ; celle-ci doit montrer qu'elle
   n'en choisit pas, et une diagonale le dit sans texte. Les deux cartes tombent de part
   et d'autre de la coupe (tp-card a gauche, tp-card-2 a droite par construction), donc
   chacune prend le remplissage de SON cote — sinon la carte claire disparaitrait dans
   le blanc. Couleurs en dur comme pour .theme-preview-blue/-light : une vignette de
   theme ne suit pas le theme actif, elle le represente. */
.theme-preview-system { background: linear-gradient(115deg, #0e1016 0 49.5%, #e9edf5 50.5% 100%); }
.theme-preview-system .tp-orb  { background: radial-gradient(circle, rgba(124,80,255,0.6), rgba(59,130,246,0.38), transparent 70%); }
.theme-preview-system .tp-card { background: rgba(124,110,255,0.10); }
.theme-preview-system .tp-card-2 { background: #ffffff; border-color: rgba(20,28,60,0.14); opacity: 1; }

.tp-orb {
  position: absolute;
  width: 70%;
  height: 70%;
  border-radius: 50%;
  top: -20%;
  left: -10%;
  opacity: 0.6;
}
.theme-preview-blue   .tp-orb { background: radial-gradient(circle, rgba(124,80,255,0.6), rgba(59,130,246,0.38), transparent 70%); }
.theme-preview-purple .tp-orb { background: radial-gradient(circle, rgba(191,95,255,0.6), rgba(255,45,155,0.3), transparent 70%); }
.theme-preview-mixed  .tp-orb { background: radial-gradient(circle, rgba(0,212,255,0.4), rgba(191,95,255,0.4), transparent 70%); }

.tp-card {
  position: absolute;
  width: 65%;
  height: 28%;
  border-radius: 6px;
  bottom: 18%;
  left: 8%;
  border: 1px solid var(--border-1);
}
.theme-preview-blue   .tp-card { background: rgba(124,110,255,0.10); }
.theme-preview-purple .tp-card { background: rgba(180,100,255,0.08); }
.theme-preview-mixed  .tp-card { background: rgba(100,80,255,0.08); }

.tp-card-2 {
  width: 40%;
  height: 20%;
  bottom: 52%;
  left: 50%;
  opacity: 0.6;
}

.theme-label {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-secondary);
}
.theme-option.active .theme-label { color: var(--text-primary); }

.theme-check {
  position: absolute;
  top: 8px; right: 10px;
  font-size: 0.75rem;
  color: var(--theme-color);
  opacity: 0;
  transition: opacity 0.2s;
  font-weight: 700;
}

/* Chip glassmorphism */
.chip {
  padding: 7px 16px;
  border-radius: 100px;
  background: var(--bg-input);
  backdrop-filter: blur(8px);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  font-size: 0.82rem;
  font-weight: 500;
  transition: all 0.2s;
  white-space: nowrap;
  letter-spacing: 0;
  flex-shrink: 0;
}
@media (hover: hover) {
  .chip:hover { border-color: var(--border-bright); color: var(--text-primary); }
}
.chip.active {
  background: linear-gradient(135deg, var(--glow-primary), rgba(255,255,255,0.06));
  border-color: var(--theme-color);
  color: var(--theme-color);
  box-shadow: none;
}
/* Fix 2: no tap-highlight bleed on filter chips */
.filter-chips .chip {
  -webkit-tap-highlight-color: transparent;
}
.filter-chips .chip:active {
  transition: none;
}

/* Btn secondary glass */
.btn-secondary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 22px;
  background: var(--bg-input);
  backdrop-filter: blur(8px);
  color: var(--text-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-weight: 500;
  font-size: 0.88rem;
  transition: all 0.2s;
}
@media (hover: hover) {
  .btn-secondary:hover { border-color: var(--border-bright); color: var(--text-primary); background: var(--bg-card); }
}

/* btn-format glass */
.btn-format {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 28px;
  background: var(--bg-card);
  backdrop-filter: blur(8px);
  border: 1px solid var(--border-bright);
  color: var(--accent-blue);
  border-radius: var(--radius-md);
  font-weight: 600;
  font-size: 0.92rem;
  letter-spacing: 0.03em;
  transition: all 0.25s;
  width: 100%;
  justify-content: center;
}
.btn-format svg { width: 18px; height: 18px; }
@media (hover: hover) {
  .btn-format:hover {
    background: var(--bg-card);
    box-shadow: none;
  }
}

/* Textarea wrapper + autocomplete toggle */
.textarea-wrap {
  position: relative;
}
.textarea-wrap .form-textarea {
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding-top: 48px;
}
.textarea-wrap .btn-format-photo {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 2;
  flex: none;
  width: 32px;
  height: 32px;
  min-height: 0;
  border-radius: var(--radius-sm);
  background: var(--bg-hover);
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
.textarea-wrap .btn-format-photo svg { width: 18px; height: 18px; }
.autocomplete-toggle {
  position: absolute;
  top: 8px;
  right: 8px;
  display: flex;
  align-items: center;
  gap: 5px;
  cursor: pointer;
  user-select: none;
  z-index: 2;
  font-size: 13px;
  line-height: 1;
  color: var(--theme-color);
  opacity: 0.85;
}
.autocomplete-toggle-label {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.autocomplete-toggle-track {
  position: relative;
  width: 36px;
  height: 20px;
  border-radius: 10px;
  background: rgba(136, 136, 136, 0.35);
  transition: background 0.2s;
  flex-shrink: 0;
}
.autocomplete-toggle-thumb {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(136, 136, 136, 0.6);
  transition: transform 0.2s, color 0.2s;
}
.autocomplete-toggle-thumb svg {
  width: 8px;
  height: 8px;
  fill: currentColor;
}
#autocompleteToggle:checked ~ .autocomplete-toggle-track {
  background: var(--theme-color);
}
#autocompleteToggle:checked ~ .autocomplete-toggle-track .autocomplete-toggle-thumb {
  transform: translateX(16px);
  color: var(--theme-color);
}

/* Rangées Notifications (Réglages du compte) — switch au visuel identique à .autocomplete-toggle. */
.notif-row { display:flex; align-items:center; justify-content:space-between; gap:14px; padding:12px 0; cursor:pointer; }
.notif-row + .notif-row { border-top:1px solid var(--border); }
.notif-row-text { display:flex; flex-direction:column; gap:3px; min-width:0; }
.notif-row-label { font-size:0.9rem; color:var(--text-primary); font-weight:500; }
.notif-row-sub { font-size:0.78rem; color:var(--text-secondary); line-height:1.35; }
.notif-toggle-track { position:relative; flex-shrink:0; width:36px; height:20px; border-radius:10px; background:rgba(136,136,136,.35); transition:background .2s; }
.notif-toggle-thumb { position:absolute; top:3px; left:3px; width:14px; height:14px; border-radius:50%; background:#fff; transition:transform .2s; }
.notif-check:checked ~ .notif-toggle-track { background:var(--theme-color); }
.notif-check:checked ~ .notif-toggle-track .notif-toggle-thumb { transform:translateX(16px); }

/* Textarea glass */
.form-textarea {
  padding: 14px 15px;
  background: var(--bg-input);
  backdrop-filter: blur(8px);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-size: 0.88rem;
  font-family: var(--font-mono);
  line-height: 1.7;
  resize: vertical;
  outline: none;
  transition: all 0.2s;
}
.form-textarea:focus {
  border-color: var(--border-bright);
  box-shadow: none;   /* C15 : etait `0`, invalide donc ignore */
}

/* Gallery nouveau button Ã¢â‚¬â€ slow shimmer every 3s */
.view-header .btn-primary {
  animation: nouveauShimmer 3s ease-in-out infinite;
}
@keyframes nouveauShimmer {
  0%, 65%  { background-position: left center; }
  75%      { background-position: right center; }
  100%     { background-position: left center; }
}

/* Scrollbar themed */
::-webkit-scrollbar-thumb { background: var(--border-bright); }

@media (max-width: 480px) {
  .theme-picker { grid-template-columns: 1fr; }
  .theme-preview { height: 50px; }
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   AUTH SCREEN
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.auth-screen {
  position: fixed;
  inset: 0;
  background: var(--bg-base);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 20px;
}
.auth-screen::before {
  content: '';
  position: fixed;
  top: -20%; left: -10%;
  width: 70vw; height: 70vw;
  background: radial-gradient(ellipse, var(--glow-primary) 0%, transparent 70%);
  pointer-events: none;
  animation: ambientFloat 12s ease-in-out infinite alternate;
}
.auth-screen::after {
  content: '';
  position: fixed;
  bottom: -20%; right: -10%;
  width: 50vw; height: 50vw;
  background: radial-gradient(ellipse, var(--glow-primary) 0%, transparent 70%);
  pointer-events: none;
  opacity: 0.5;
  animation: ambientFloat 15s ease-in-out infinite alternate-reverse;
}

.auth-container {
  width: 100%;
  max-width: 400px;
  background: rgba(5,4,18,0.72);
  backdrop-filter: blur(40px) saturate(160%);
  -webkit-backdrop-filter: blur(40px) saturate(160%);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius-xl);
  padding: 40px 34px;
  box-shadow: 0 24px 80px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.08);
  position: relative;
  z-index: 1;
  /* C112 : c'etait `0.45s cubic-bezier(0.4,0,0.2,1)` — la seule surface de
     l'app a entrer sur une AUTRE duree ET une AUTRE courbe. Aucune raison
     ecrite nulle part. C'est le premier ecran qu'on voit au lancement : qu'il
     ait sa propre cadence donne le ton d'une app moins tenue que les vingt
     surfaces qui suivent. Alignee sur les jetons.
     Si tu tenais a cette entree plus lente, c'est ici et nulle part ailleurs. */
  animation: slide-up var(--dur-v2) var(--ease-v2);
}
/* Top luminous border */
.auth-container::before {
  content: '';
  position: absolute;
  top: 0; left: 10%; right: 10%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--theme-color), var(--theme-secondary, var(--theme-color)), transparent);
  border-radius: 1px;
}

.auth-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 6px;
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 2.4rem;
  letter-spacing: 0.06em;
}
.auth-logo .logo-nex { color: var(--text-primary); }
.auth-logo .logo-set {
  color: var(--theme-color);
  animation: logo-set-pulse 3s ease-in-out infinite;
}

.auth-tagline {
  text-align: center;
  font-size: 0.85rem;
  color: var(--text-muted);
  margin-bottom: 28px;
}

.auth-tabs {
  display: flex;
  gap: 4px;
  background: var(--bg-input);
  border-radius: var(--radius-md);
  padding: 4px;
  margin-bottom: 24px;
}
.auth-tab {
  flex: 1;
  padding: 9px;
  border-radius: 10px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-muted);
  transition: all 0.2s;
}
.auth-tab.active {
  background: var(--surface-4);
  color: var(--text-primary);
  box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}

.auth-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Inputs auth alignés sur le pattern V2 sain (create / modales PR-edit) :
   backdrop-filter neutralisé sur l'input (bug caret WebKit iOS) + font-size 16px (anti-zoom).
   Le .auth-container garde son blur(40px) — on ne tue le blur QUE sur les inputs. */
.auth-form .form-input {
  font-size: 16px;
  box-sizing: border-box;
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  border-radius: 14px;
  color: #fff;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.auth-form .form-input::placeholder { color: var(--text-3); }
.auth-form .form-input:focus {
  border-color: var(--theme-color);
  box-shadow: 0 0 0 3px rgba(255,255,255,.03);
}

.auth-error {
  font-size: 0.82rem;
  color: var(--accent-red);
  text-align: center;
  min-height: 18px;
  line-height: 1.4;
}
/* C85 — la regle annoncee sous le champ de confirmation, a l'inscription.
   Calee sur `.auth-error` juste au-dessus (meme corps, meme interligne) mais en
   gris : c'est une consigne, pas un reproche. Elle ne doit pas se lire comme une
   erreur avant meme qu'on ait tape quoi que ce soit. */
.auth-hint {
  font-size: 0.82rem;
  color: var(--text-3, var(--text-secondary));
  line-height: 1.4;
  margin: 6px 2px 0;
}

.auth-submit {
  width: 100%;
  justify-content: center;
  padding: 15px;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  margin-top: 4px;
  box-shadow: none;
}

.auth-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: 20px 0;
  color: var(--text-secondary);
  font-size: 0.88rem;
}
.auth-spinner {
  width: 32px; height: 32px;
  border: 2px solid var(--border);
  border-top-color: var(--accent-blue);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Sidebar user + logout */
.sidebar-user {
  font-size: 0.75rem;
  color: var(--text-muted);
  padding: 8px 14px 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.logout-btn svg { width: 17px; height: 17px; }
@media (hover: hover) {
  .logout-btn:hover { background: rgba(255,71,87,0.1); opacity: 1; }
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   PROFILE VIEW
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.profile-form {
  max-width: 560px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.profile-card {
  background: var(--bg-card);
  backdrop-filter: blur(16px);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius-xl);
  padding: 24px;
  display: flex;
  align-items: center;
  gap: 20px;
  box-shadow: 0 4px 32px var(--glow-card);
  position: relative;
  overflow: hidden;
}
.profile-card::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--border-bright), transparent);
}
.profile-avatar {
  width: 62px; height: 62px;
  border-radius: 50%;
  background: var(--gradient-accent);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 800;
  color: var(--theme-text-on-accent, #fff);
  flex-shrink: 0;
  box-shadow: none;
  animation: pulse-glow 4s ease-in-out infinite;
}
.profile-info { display: flex; flex-direction: column; gap: 4px; }
.profile-display-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.35rem;
  color: var(--text-primary);
}
.profile-email { font-size: 0.82rem; color: var(--text-secondary); }
.profile-gear-btn {
  position: absolute; top: 14px; right: 14px;
  background: none; border: none; padding: 4px; cursor: pointer;
  color: var(--text-3);           /* discret au repos, sans halo thème */
  display: inline-flex; align-items: center;
  transition: color 0.15s;
}
@media (hover: hover) { .profile-gear-btn:hover { color: var(--text-2); } }

/* Stats perso — grille 2×2, surfaces alignées sur .profile-card (rgba .04 + border). */
.profile-stats { display:grid; grid-template-columns:1fr 1fr; gap:10px; }
.stat-tile {
  cursor: pointer;
  background: var(--surface-card-a);
  border: 1px solid var(--surface-card-brd);
  border-radius: var(--radius-lg);
  padding: 18px;
  display:flex; flex-direction:column; gap:4px; align-items:flex-start;
  box-shadow: 0 8px 20px rgba(0,0,0,.45);   /* Refonte : carte stat flottante */
}
[data-theme="light"] .stat-tile { background:#fff; border-color:rgba(12,22,52,.06); box-shadow:0 8px 18px rgba(20,28,60,.10); }
.stat-tile.active {
  border-color: rgba(var(--theme-rgb), .35);
  box-shadow: 0 0 0 1px rgba(var(--theme-rgb), .35), 0 0 18px rgba(var(--theme-rgb), .15);
}
/* C32 : la premiere pastille portait le degrade de marque, et elle seule. Sur l'ecran
   Profil, « Seances faites » etait donc mise en avant face a « Kcal totales »,
   « Ce mois-ci » et « Jours d'affilee » — alors que rien ne la rend plus importante.
   C'etait une hierarchie decorative, qui affirmait quelque chose de faux.
   Le degrade est la signature de l'app : il doit dire « ceci est une action » ou
   « ceci est l'element principal de l'ecran ». Une stat parmi quatre n'est ni l'un ni
   l'autre. Les quatre pastilles se valent, elles se ressemblent.
   Les deux regles d'encre qui suivaient sont retirees AVEC : elles forcaient du blanc,
   qui n'avait de sens que sur l'aplat colore. Les laisser aurait donne du blanc sur
   blanc en theme clair — exactement la famille de bug corrigee en C25. */
/* C44 : etait la SEULE famille de gros chiffres restee en Inter (mesure : Inter 800
   31,2px, quand le nom du hero, le nom de carte et le chiffre de carte sont tous en
   Barlow Condensed 800). Les quatre tuiles du Profil sont pourtant les chiffres les
   plus regardes de l'app. Aligne sur --font-display comme les 35 autres declarations
   d'affichage posees en C41. */
.stat-value { font-family: var(--font-display); font-size:1.95rem; font-weight:800; color:var(--text-primary); line-height:1.1; letter-spacing:-.02em; }
/* C30 : le tiret d'une serie a zero. Attenue, mais toujours au-dessus du seuil de
   lisibilite — on le met en retrait, on ne le cache pas. */
.stat-value.stat-value-vide { color: var(--text-3); font-weight: 700; }
.stat-label { font-size:0.8rem; color:var(--text-secondary); }
.stat-tip {
  margin-top: 10px;
  padding: 12px 14px;
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-1);
  border-radius: 14px;
  font-size: .82rem; color: var(--text-secondary); line-height: 1.4;
  animation: statTipIn .18s var(--ease-v2);
}
@keyframes statTipIn { from { opacity:0; transform: translateY(-8px); } to { opacity:1; transform:none; } }
.profile-name-line { display: flex; align-items: baseline; gap: 1px; }
/* C19 : .profile-display-name et .profile-id se suivent sans espace dans le
   markup, ce qui donnait « Invité#0000 » collé. On separe en CSS plutot que
   d'injecter une espace dans le texte — le pseudo reste manipulable tel quel. */
.profile-id {
  margin-left: 5px;
  color: var(--accent);
  font-family: var(--font-body);
  font-size: 0.95rem;
  letter-spacing: 0.04em;
}
/* Input inline du pseudo (édition sur place dans la carte profil). */
.profile-name-edit {
  font-size: inherit; font-weight: inherit;
  color: var(--text-primary);
  background: var(--surface-3);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 1px 8px;
  max-width: 160px;
  outline: none;
}
.profile-name-edit:focus { border-color: var(--theme-color); }
.profile-edit-btn {
  background: none;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
  padding: 2px;
  margin-left: 6px;
  display: inline-flex;
  align-items: center;
  transition: color 0.15s;
}
@media (hover: hover) {
  .profile-edit-btn:hover { color: var(--theme-color); }
}
.profile-edit-pwd-link {
  background: none;
  border: none;
  color: var(--theme-color);
  font-size: 0.8rem;
  cursor: pointer;
  padding: 4px 0 0;
  text-align: left;
}

/* Feature card (Personnalisation) */
.profile-feature-card {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 16px;
  /* margin-bottom retiré : l'espacement entre blocs de #view-profile est géré par .profile-form gap:16px */
  background: var(--bg-card);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius-lg);
  box-shadow: 0 4px 24px var(--glow-card);
  cursor: pointer;
  text-align: left;
  transition: transform 0.15s, box-shadow 0.2s;
}
.profile-feature-card:active { transform: scale(0.98); }
.pfc-icon {
  width: 42px; height: 42px;
  border-radius: 12px;
  background: var(--theme-color);
  color: var(--bg-base);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.pfc-text { flex: 1; }
.pfc-title { font-size: 1.1rem; font-weight: 700; color: var(--text-primary); }
.pfc-sub { font-size: 0.8rem; color: var(--text-secondary); margin-top: 2px; }
.pfc-chevron { color: var(--text-secondary); flex-shrink: 0; }


.profile-section {
  background: var(--bg-card);
  backdrop-filter: blur(12px);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  padding: 20px 22px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.profile-section-title {
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 4px;
}
.profile-msg {
  font-size: 0.82rem;
  min-height: 18px;
  text-align: center;
}

.profile-danger-section {
  border-color: rgba(255,71,87,0.15);
  gap: 10px;
}
.danger-title { color: rgba(255,71,87,0.6); }

/* ── Profil physique ── */
.profile-physio-title {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--text-primary);
  margin: 0 0 4px;
}
.profile-physio-note {
  font-size: 0.78rem;
  color: var(--text-muted);
  line-height: 1.4;
  margin-top: -4px;
}
.profile-physio-fields {
  display: flex;
  gap: 10px;
}
.physio-field {
  flex: 1 1 0;
  min-width: 0;
}
/* C40 : les trois champs se partageaient la rangee en tiers EGAUX, alors qu'ils n'ont
   pas les memes besoins. Poids et Age accueillent deux chiffres ; Sexe doit afficher
   « Non précisé », qui demande 91px pour 70px utiles — d'ou le « Non p⌄ » tronque, vu
   sur l'ecran Profil. Des parts egales ne sont equitables que si les contenus le sont.
   1.4 donne 122px au select, et laisse 88px a chacun des deux autres : largement de
   quoi ecrire un poids et un age. */
.physio-field:nth-child(2) { flex: 1.4 1 0; }
.physio-field label {
  display: block;
  text-align: center;
  /* C115 : `0.72rem` = 11,5 px — sous le plancher d'information pose par C113,
     et attrape par sa garde sur MES propres champs neufs. Le libelle d'un champ
     de formulaire dit ce qu'on doit y saisir : ca se lit, donc 12. */
  font-size: var(--fs-xs);
  color: var(--text-muted);
  letter-spacing: 0.04em;
  margin-bottom: 4px;
}
.physio-field .form-input {
  width: 100%;
  text-align: center;
  text-align-last: center;
}

.btn-logout-full {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 18px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-secondary);
  font-size: 0.88rem;
  font-weight: 500;
  transition: all 0.2s;
  width: 100%;
}
.btn-logout-full svg { width: 16px; height: 16px; }
@media (hover: hover) {
  .btn-logout-full:hover { border-color: var(--border-bright); color: var(--text-primary); }
}

.btn-delete-account {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 18px;
  background: rgba(255,71,87,0.08);
  border: 1px solid rgba(255,71,87,0.2);
  border-radius: var(--radius-md);
  color: var(--accent-red);
  font-size: 0.88rem;
  font-weight: 500;
  transition: all 0.2s;
  width: 100%;
  opacity: 0.7;
}
.btn-delete-account svg { width: 16px; height: 16px; }
@media (hover: hover) {
  .btn-delete-account:hover { background: rgba(255,71,87,0.15); opacity: 1; }
}


/* Ã¢â€â‚¬Ã¢â€â‚¬ Forgot password Ã¢â€â‚¬Ã¢â€â‚¬ */
.auth-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--text-faint, #6b7196);
  font-size: 0.78rem;
}
.auth-divider::before,
.auth-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}

.btn-google {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  padding: 12px 16px;
  background: #fff;
  color: #1f1f1f;
  border: 1px solid #dadce0;
  border-radius: var(--radius-md, 12px);
  font-size: 0.9rem;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.2s, box-shadow 0.2s;
  position: relative;
}
.btn-apple {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 12px 16px;
  margin-bottom: 10px;
  background: #000;
  color: #fff;
  border: 1px solid #000;
  border-radius: var(--radius-md, 12px);
  font-size: 0.9rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: opacity 0.2s;
  position: relative;
}
.btn-apple svg { margin-top: -2px; }
.btn-apple:active { opacity: 0.85; }
/* En thème clair, le bouton Apple noir reste noir (standard Apple) mais le Google devient bordé plus doux */
[data-theme="light"] .btn-apple { background: #000; color: #fff; border-color: #000; }
@media (hover: hover) {
  .btn-google:hover {
    background: #f8f9fa;
    box-shadow: 0 2px 8px rgba(0,0,0,0.22);
  }
}
.btn-google:active { transform: scale(0.97); }
.btn-google svg { stroke: none; }

.auth-forgot-link {
  text-align: center;
  font-size: 0.8rem;
  color: var(--text-muted);
  padding: 6px;
  transition: color 0.2s;
  width: 100%;
}
@media (hover: hover) {
  .auth-forgot-link:hover { color: var(--accent-blue); }
}

.forgot-panel {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  animation: slide-fade-in 0.2s ease;
}

.forgot-desc {
  font-size: 0.82rem;
  color: var(--text-secondary);
  line-height: 1.5;
  margin-bottom: 4px;
}
@media (hover: hover) {
  .err-link:hover { opacity: 0.8; }
}

/* ══ C109 — LA BARRE DE SÉANCE EN COURS ═════════════════════════════════════
   Remplace `.persistent-timer-bar { display: none !important }`. La barre était
   codée, branchée sur le tick d'une seconde, et n'avait AUCUNE règle : elle a
   été éteinte parce qu'elle était brute. Le raisonnement de forme (mini-lecteur,
   une seule commande, deux variantes) est dans `updatePersistentBar`.

   ⚠ ELLE SE PLACE PAR RAPPORT À LA NAV, ET LA NAV N'EST PAS TOUJOURS LÀ.
   `.bottom-nav` est en `bottom: 16px` et masquée en Réglages, Compte, Support
   et Création. Le jeton `--bec-bas` porte les deux cas : au-dessus de la nav
   quand elle existe, sur le safe-area sinon. Une valeur en dur aurait laissé la
   barre flotter au milieu de rien sur les quatre vues sans nav. */
.persistent-timer-bar {
  position: fixed;
  left: 12px; right: 12px;
  bottom: calc(var(--bec-bas, 74px) + env(safe-area-inset-bottom, 0px));
  z-index: 60;                    /* sous la nav (400) : elle ne la couvre jamais */
  display: flex; align-items: center; gap: 12px;
  /* ⚠ JETONS, PAS DE COULEURS EN DUR. La maquette utilisait
     `linear-gradient(165deg,#1b1b21,#121216)` et sortait une carte SOMBRE sur
     fond clair — vu à la capture en thème clair, pas en relisant le CSS. Ces
     jetons sont exactement ceux que la session du 19 juillet a posés pour les
     34 surfaces de cartes, précisément pour ce cas. */
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--surface-card-brd);
  border-radius: 18px;
  padding: 10px 10px 10px 14px;
  box-shadow: 0 10px 30px rgba(0,0,0,.45);
  cursor: pointer;
  /* C112 : `.28s` -> le jeton. C'etait ma propre valeur en dur, et elle faisait
     de cette barre la troisieme duree d'entree de l'app. */
  animation: bec-in var(--dur-v2) var(--ease-v2);
}
/* Nav visible (bottom 16 + hauteur ~58) → la barre se pose au-dessus.
   Nav masquée → elle descend sur le safe-area. */
body.seance-en-cours { --bec-bas: 88px; }
body.seance-en-cours.in-settings,
body.seance-en-cours.in-account,
body.seance-en-cours.in-support,
body.seance-en-cours.in-create { --bec-bas: 14px; }

@keyframes bec-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .persistent-timer-bar { animation: none; } }

.persistent-timer-bar .bec-pt {
  width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;
  background: var(--theme-color);
  box-shadow: 0 0 0 5px rgba(var(--theme-rgb), .18);
}
/* En pause, la pastille cesse d'affirmer que quelque chose avance. */
.persistent-timer-bar.bec-pause .bec-pt { background: var(--text-secondary); box-shadow: none; }

.persistent-timer-bar .bec-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.persistent-timer-bar .bec-nom {
  font-family: var(--font-display); font-weight: 800; font-size: 15px;
  /* `--text-primary` et non `#fff` : en thème clair le blanc disparaîtrait. */
  letter-spacing: .02em; text-transform: uppercase; color: var(--text-primary);
  /* Un nom fait jusqu'à 18 caractères (maxlength) : il doit céder avant le
     chrono et la commande, jamais l'inverse. */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.persistent-timer-bar .bec-sub { font-size: 11.5px; color: var(--text-secondary); letter-spacing: .04em; }
.persistent-timer-bar .bec-tps {
  font-family: var(--font-display); font-weight: 800; font-size: 20px; color: var(--text-primary);
  /* Chiffres à chasse tabulaire : sans ça le chrono tressaute d'un pixel à
     chaque seconde, et c'est le seul élément de l'app qui change tout seul. */
  font-variant-numeric: tabular-nums; flex-shrink: 0;
}
.persistent-timer-bar.bec-pause .bec-tps { color: var(--text-secondary); }

.persistent-timer-bar .bec-btn {
  width: 44px; height: 44px; border-radius: 50%; border: none; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--gradient-accent); color: var(--theme-text-on-accent, #fff);
  cursor: pointer; padding: 0;
  transition: transform 80ms ease;
}
.persistent-timer-bar .bec-btn:active { transform: scale(.94); }
/* C111 — les deux glyphes cohabitent, on n'en montre qu'un. Ils ne quittent
   JAMAIS le document : c'est ce qui empêche le tap sur l'icône d'être pris pour
   un tap sur la barre (raisonnement complet dans script.js). */
.persistent-timer-bar .bec-g { display: none; align-items: center; justify-content: center; }
.persistent-timer-bar .bec-btn[data-etat="run"]   .bec-g-run   { display: flex; }
.persistent-timer-bar .bec-btn[data-etat="pause"] .bec-g-pause { display: flex; }
.persistent-timer-bar .bec-btn svg { width: 24px; height: 24px; fill: currentColor; stroke: none; display: block; }
/* Mêmes glyphes et même rondeur que le bouton du hero (C107). */
.persistent-timer-bar .bec-btn svg polygon {
  stroke: currentColor; stroke-width: 2.5; stroke-linejoin: round;
}
/* `.bec-chev` a été SUPPRIMÉE avec la forme « sobre » de l'onglet Timer (C110).
   Une règle qui ne s'applique plus à rien est un piège pour la prochaine
   lecture — on croit qu'une variante existe encore. */

/* ⚠ LE FAB ET LA COMMANDE SONT DEUX RONDS AU MÊME DÉGRADÉ, À 8px L'UN DE
   L'AUTRE. Vu en maquette, pas en raisonnant. Pendant un entraînement, l'action
   principale de l'écran n'est pas d'en créer un autre. */
/* ⚠ LE COMPOSÉ N'EST PAS DÉCORATIF. `body.in-gallery .fab-new { display: flex }`
   a la MÊME spécificité (0-2-0) et est déclarée ~1800 lignes plus bas : elle
   gagnait, et le FAB restait affiché en galerie — la seule vue où il existe,
   donc la seule où le conflit comptait. Mesuré : `fab: flex` alors que la barre
   était là. Même piège que `.btn-icon` contre `.hdr-btn` en C92. */
body.seance-en-cours .fab-new,
body.seance-en-cours.in-gallery .fab-new { display: none; }

/* ⚠ IL N'Y A PAS DE COMPENSATION DE PADDING, ET C'EST DÉLIBÉRÉ.
   J'en avais écrit une, en annonçant que la barre rendait le dimanche de la
   Prépa inatteignable. C'ÉTAIT FAUX : la mesure d'origine comptait des
   conteneurs hauts qui chevauchent la barre à mi-défilement, pas le bas réel du
   contenu. Reprise correctement — défilé jusqu'en bout de course, bord
   inférieur le plus bas de la vue — l'écart avec la barre reste POSITIF partout,
   avec ou sans compensation :

       vue        avec compensation    sans
       gallery         202 px          136 px
       prepa           114 px           48 px
       pr              129 px          129 px
       profile         114 px           48 px
       settings        126 px           60 px

   48px de marge au pire, et cette valeur ne dépend pas de la quantité de
   contenu : elle vient du `padding-bottom` du conteneur, pas de la liste.
   Trois règles de plus qui composent mal avec les cinq paddings existants, pour
   un défaut qui n'existe pas — c'est du poids, pas une protection.
   `test-barre-seance.js` garde l'invariant qui compte : rien ne descend sous la
   barre. Il rougira le jour où une vue arrivera avec un bas plus serré. */

/* Inline timer countdown Ã¢â‚¬â€ pulse like sidebar, no size change */
.itimer-counting-down {
  color: var(--accent-blue) !important;
  /* C15 : `filter: drop-shadow() !important` etait invalide, donc ignore. Retire
     plutot que corrige : C5 a supprime les halos volontairement. */
  animation: countPulse 0.9s ease infinite !important;
}

/* Launch fade Ã¢â‚¬â€ smooth handoff to running state */
.itimer-launch {
  animation: itimerLaunch 0.38s ease forwards;
}
@keyframes itimerLaunch {
  0%   { opacity: 1; }
  100% { opacity: 0; }
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   FORMATTER v4 Ã¢â‚¬â€ Workout Display Classes
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */

/* Base workout font Ã¢â‚¬â€ cleaner, more readable */
/* ══ C88 — LE PROGRAMME QUITTE LA CHASSE FIXE ═══════════════════════════════
   DEMANDE : « un truc compact », « moins vieux », « plus rien dans les 0 », et
   surtout « super agreable a lire pendant un entrainement ».

   POURQUOI PAS UNE AUTRE MONOSPACE. J'en ai propose quatre — Chris ne voyait
   aucune difference entre elles, et il avait raison : a 13px elles se
   ressemblent toutes. Si aucune ne paraissait « moins vieille », c'est que
   c'est la CHASSE FIXE qui fait vieux. Changer de monospace, c'est changer de
   terminal.

   ⚠ ET ON LA PAYAIT POUR RIEN. Le seul vrai argument d'une monospace est
   d'aligner les chiffres en colonnes. Or le programme est rendu CENTRE —
   test-renderer-build5 le mesure et l'ecrit : « alignement du conteneur :
   center ». Il n'y a aucune colonne. On supportait l'allure terminal pour une
   propriete que ce bloc n'utilise pas.

   LE COMPROMIS N'EXISTE PAS, MESURE A L'APPUI (tools/ui-bench/police-lisibilite.html) :
     JetBrains Mono 13      264px de large   hauteur de x 8,8
     Barlow Condensed 13    161px            hauteur de x 8,3
     Barlow Condensed 16    183px            hauteur de x 9,9   <- retenu
   A 16px, la condensee est 31 % PLUS ETROITE que l'ancienne tout en ayant des
   lettres 13 % PLUS HAUTES. On ne compacte pas au detriment de la lecture : on
   depense la largeur liberee en TAILLE. Et ce qui se lit de loin, entre deux
   series, c'est la hauteur des lettres — pas leur finesse.

   LE ZERO. `font-feature-settings: "zero" 0` etait deja la : une tentative
   passee pour retirer le point du zero. Elle ne pouvait pas marcher — chez
   JetBrains Mono le zero pointe est le glyphe PAR DEFAUT, pas une variante
   optionnelle qu'on desactive. Barlow l'a nu, sans reglage.

   GRATUIT PAR AILLEURS : Barlow Condensed est deja dans www/fonts/ (c'est la
   police des noms de training). Aucun fichier ajoute, aucune requete externe —
   l'auto-hebergement reste intact — et le programme prend la meme voix que les
   noms : une seule famille d'affichage au lieu de deux.

   ⚠ CE QU'ON PERD : un programme contenant un tableau aligne a la main avec des
   espaces se decalera. Aucun des trainings en base n'en contient (verifie), mais
   c'est le seul cas ou l'ancienne police faisait un vrai travail.

   `.quick-apercu-corps` REJOINT CETTE REGLE. C'est le defaut d'origine signale
   par Chris : l'apercu de l'appui long affichait le meme programme en Inter
   13,3px pendant que le Detail le rendait en JetBrains Mono 13px. Meme contenu,
   deux typographies — parce que l'apercu n'heritait simplement d'aucune des
   trois classes ci-dessous. */
/* ⚠ C98 — LA GRAISSE DU CORPS, ET POURQUOI C'ETAIT ELLE LE PROBLEME.
   Signale a l'usage : « les chiffres sont trop gras ». Mesure : les chiffres
   (.fw-rep) etaient a 700, mais le CORPS etait deja a 600 — un programme entier
   en semi-gras. Rendu cote a cote, alleger les seuls chiffres ne se voyait
   presque pas : c'est le bloc de texte qui pesait, pas les nombres.
   Corps 500, chiffres 600 : l'ecart de graisse entre les deux est CONSERVE
   (100), donc la hierarchie ne bouge pas, et la distinction reposait de toute
   facon d'abord sur la couleur. Les pastilles (.fw-weight, .fw-unbroken) restent
   a 700 : a 11,5px, moins les rendrait pateuses.
   La graisse est du cote LIBRE de test-renderer-build5 depuis C88 — verifie
   avant de toucher, la structure seule est contractuelle. */
.detail-workout-body,
.preview-content,
.quick-apercu-corps,
#fullscreenBody {
  font-family: 'Barlow Condensed', 'Inter', sans-serif;
  font-weight: 500;
  font-size: 16px;
  line-height: 1.6;
  letter-spacing: 0.2px;
}

.fw-line {
  display: block;
  position: relative;
  white-space: nowrap;
  padding: 1px 0;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--text-primary);
}
.fw-line-content {
  display: inline-block;
}

/* Reps Ã¢â‚¬â€ cyan ÃƒÂ©lectrique thÃƒÂ¨me */
.fw-rep {
  color: var(--fw-color-rep);
  /* C98 : 700 -> 600. Reste un cran au-dessus du corps (500), donc detache
     toujours ; et c'est la couleur qui portait deja la distinction. */
  font-weight: 600;
}

/* Distance / cardio rep counts */
/* C125 — meme cas que `.fw-time-min` : 0.55 donnait 2,50:1 en clair et 2,60:1
   en sombre. 0.9 rend 4,2 / 5,6 et l'ecart avec les noms d'exercices reste
   parfaitement lisible a l'oeil. */
.fw-distance {
  color: var(--fw-color-distance);
  opacity: 0.9;
  font-style: italic;
  font-weight: 600;
}

/* Exercices Ã¢â‚¬â€ blanc doux */
.fw-ex {
  color: var(--text-primary);
}

/* SÃƒÂ©parateur / Ã¢â‚¬â€ gris-bleu semi-transparent */
.fw-sep {
  color: var(--fw-color-sep);
  margin: 0 6px;
}

/* Poids Ã¢â‚¬â€ badge rouge transparent */
/* REVERT BUILD 5 : le plancher de lisibilite 11px (C3) avait remplace les tailles
   RELATIVES du programme par un jeton absolu. Or 0.72em, 0.78em, 0.8em, 0.85em,
   0.88em et 0.7em ne sont pas six approximations du meme petit : c'est la
   hierarchie typographique du renderer, et elle suit la taille de la ligne
   parente. Les aplatir sur une valeur fixe mettait badges, titres, commentaires
   et repos exactement au meme corps. Valeurs d'origine remises. */
.fw-weight {
  color: var(--fw-color-weight);
  background: color-mix(in srgb, var(--fw-color-weight) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--fw-color-weight) 40%, transparent);
  font-size: 0.72em;
  font-weight: 700;
  padding: 0.15em 0.5em;
  line-height: 1;
  border-radius: 0.4em;
  margin-left: 0.3em;
  vertical-align: middle;
  letter-spacing: 0.05em;
  display: inline-block;
}

/* Dead Stop Ã¢â‚¬â€ inline per-segment badge (flows with segment text) */
.fw-badge-deadstop-seg {
  background: rgba(255, 90, 90, 0.15);
  color: var(--fw-color-deadstop);
  border: 1px solid rgba(255, 90, 90, 0.3);
  font-size: 0.72em;
  font-weight: 700;
  padding: 0.15em 0.5em;
  line-height: 1;
  border-radius: 0.4em;
  letter-spacing: 0.05em;
  margin-left: 0.3em;
  vertical-align: middle;
  display: inline-block;
}

/* Unbroken Ã¢â‚¬â€ badge vert absolu (positionnÃƒÂ© par JS) */
.fw-unbroken {
  background: var(--fw-color-unbroken-bg);
  color: var(--fw-color-unbroken-text);
  border: 1px solid var(--fw-color-unbroken-border);
  font-size: 0.72em;
  font-weight: 700;
  padding: 0.15em 0.5em;
  line-height: 1;
  border-radius: 0.4em;
  letter-spacing: 0.05em;
  display: inline-block;
  vertical-align: middle;
  white-space: nowrap;
}

/* Unbroken Ã¢â‚¬â€ inline per-segment badge (flows with segment text) */
.fw-unbroken-seg {
  background: var(--fw-color-unbroken-bg);
  color: var(--fw-color-unbroken-text);
  border: 1px solid var(--fw-color-unbroken-border);
  font-size: 0.72em;
  font-weight: 700;
  padding: 0.15em 0.5em;
  line-height: 1;
  border-radius: 0.4em;
  letter-spacing: 0.05em;
  white-space: nowrap;
  display: inline-block;
  vertical-align: middle;
}

/* EMOM lines Ã¢â‚¬â€ violet */
.fw-emom {
  color: var(--fw-color-emom);
  font-weight: 600;
  letter-spacing: 0.03em;
}

/* AMRAP lines Ã¢â‚¬â€ orange-amber */
.fw-amrap {
  color: var(--fw-color-amrap);
  font-weight: 600;
  letter-spacing: 0.03em;
}
.fw-amrap-dur {
  color: var(--fw-color-amrap);
  opacity: 0.75;
  font-weight: 400;
}

/* Title lines Ã¢â‚¬â€ WOD A, Circuit 1, Round 1, etc. */
/* C125 — quatrieme cas de la meme famille, et celui que ma premiere sonde
   avait rate : mon programme d'echantillon n'avait pas de ligne de titre. La
   garde en contient une desormais. */
.fw-title {
  color: var(--fw-color-title);
  opacity: 0.9;
  font-size: 0.78em;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* Header lines Ã¢â‚¬â€ noms d'exercices sans reps */
.fw-header {
  color: var(--fw-color-header);
  font-weight: 600;
  font-size: 0.9em;
  letter-spacing: 0.05em;
  opacity: 0.85;
}

/* Comment lines Ã¢â‚¬â€ grisÃƒÂ© petit italique */
.fw-comment {
  color: var(--fw-color-comment);
  font-style: italic;
  font-size: 0.88em;
}

/* Block separator */
.fw-sep-block {
  height: 8px;
  display: block;
  opacity: 0;
}

/* Grouped lines: content Ãƒâ€”3 */
.fw-grouped-count {
  color: #ffa502;
  font-weight: 700;
  font-size: 0.8em;
}

/* Fullscreen Ã¢â‚¬â€ bigger */
#fullscreenBody .fw-line {
  font-size: inherit;
  white-space: nowrap;
  overflow: visible;
  display: block;
  text-align: inherit;   /* C102 : c'est le conteneur qui decide */
}

/* Preview cards Ã¢â‚¬â€ clipped */
.preview-content .fw-line {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Workout block Ã¢â‚¬â€ centered like Word */
#workoutBodyEl {
  width: 100%;
  overflow-x: hidden;
}
/* ══════════════════════════════════════════════════════════════════════════
   C102 — L'ALIGNEMENT DESCEND DU CONTENEUR

   Il etait pose `center` en dur a TROIS endroits : `.fw-workout-inner`,
   `#workoutBodyEl .fw-line` et `#fullscreenBody .fw-line`. Un reglage qui n'en
   aurait couvert qu'un aurait donne un training centre au Detail et aligne a
   gauche en plein ecran — l'incoherence exacte que C66 a rencontree quand
   l'apercu d'appui long rendait le meme programme dans une autre typographie
   que le Detail.

   Les trois passent donc en `inherit`, et la valeur vit sur le CONTENEUR via
   une classe `al-*`. Un seul endroit decide, quatre surfaces suivent : Detail,
   plein ecran, apercu d'appui long, apercu de la creation.

   ⚠ PIEGE DE SPECIFICITE, verifie et non suppose. `#fullscreenBody` est un ID
   (1,0,0) : une regle `.al-center` (0,1,0) y perdrait. On ne pose donc AUCUN
   text-align sur l'id — la classe est appliquee par le JS a l'ouverture du
   plein ecran, comme aux trois autres surfaces. */
/* FILET : la valeur d'avant, au cas ou le JS n'aurait pas pose de classe
   `al-*` — training charge par un chemin oublie, rendu partiel. Sans elle le
   defaut retomberait sur `start`, et le programme basculerait a gauche sans que
   personne l'ait demande.

   ⚠ `#fullscreenBody` EST VOLONTAIREMENT ABSENT DE CETTE LISTE, et c'est le
   piege que ce lot a rencontre pour de vrai. Un id pese (1,0,0) et bat une
   classe (0,1,0) : mis ici, il aurait fige le plein ecran sur `center` quoi que
   dise `.al-right`. Mesure a l'appui — la classe etait bien posee, le rendu
   restait centre. Le plein ecran n'a donc AUCUN text-align par id ; il depend
   entierement de la classe posee par `openFullscreen`. */
.detail-workout-body, .preview-content, .quick-apercu-corps { text-align: center; }

.al-left   { text-align: left; }
.al-center { text-align: center; }
.al-right  { text-align: right; }

/* ── Le selecteur d'alignement ────────────────────────────────────────────
   Trois icones fantomes, sans conteneur : rendues encadrees, elles pesaient
   autant que le titre « Programme » pour un reglage qu'on touche une fois par
   training. Elles prennent la place du raccourci timer, masque plus bas. */
/* ⚠ 44px ET AUCUN ECART, mesure avant d'ecrire. Le premier jet faisait 32x32
   avec 1px de gap : test-cibles-tactiles l'a attrape. Et une zone ::after
   n'etait PAS la reponse — trois boutons a 33px entre centres, des zones de 44
   se seraient chevauchees de 11px, exactement le refus formule en C81 pour les
   palettes de couleur. C'est donc la taille reelle qui monte. Sans ecart, les
   centres tombent a 44 : les cibles se touchent sans jamais se mordre.
   L'icone reste a 15px, donc le groupe ne pese pas plus a l'oeil. */
.align-seg { display: inline-flex; gap: 0; margin-left: auto; flex-shrink: 0; }
.align-seg-btn {
  width: 44px; height: 44px;
  border: none; background: transparent; border-radius: 9px;
  color: var(--text-muted); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: color .15s, background .15s;
}
.align-seg-btn svg { width: 15px; height: 15px; }
.align-seg-btn.on { color: var(--theme-color); background: rgba(var(--theme-rgb), .12); }
@media (hover: hover) { .align-seg-btn:not(.on):hover { color: var(--text-primary); } }
.align-seg-btn:active { opacity: .6; }
/* L'en-tete « Apercu » de la creation est un simple <div> texte : il faut le
   passer en rangee pour que le selecteur se pose a droite. */
.entete-avec-align { display: flex; align-items: center; }

/* ⚠ C102 — LE RACCOURCI TIMER EST MASQUE, PAS SUPPRIME.
   Chris a une autre idee pour cet emplacement ; le code reste en place pour
   qu'elle puisse s'y brancher. Verifie avant de masquer : le timer reste
   atteignable par la barre du bas ET par le chrono en ligne, plus bas dans
   cette meme vue. Rien n'est isole. */
.raccourci-timer { display: none; }

.fw-workout-inner {
  display: block;
  width: 100%;
  text-align: inherit;
}
#workoutBodyEl .fw-line {
  display: block;
  text-align: inherit;
  white-space: normal;
  word-break: break-word;
}
#workoutBodyEl .fw-sep-block {
  display: block;
  height: 10px;
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ Rest lines Ã¢â€â‚¬Ã¢â€â‚¬ */
.fw-rest {
  color: var(--fw-color-rest);
  font-style: italic;
  font-size: 0.85em;
  letter-spacing: 0.05em;
  font-weight: 300;
}
.fw-rest * {
  font-weight: 300 !important;
  font-style: italic;
  color: rgba(160, 160, 200, 0.6);
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ Time Cap lines Ã¢â€â‚¬Ã¢â€â‚¬ */
.fw-timecap {
  color: var(--fw-color-timecap);
  font-weight: 400;
  font-style: italic;
  letter-spacing: 0.02em;
  font-size: 0.92em;
}
.fw-timecap-num {
  color: var(--fw-color-timecap);
  font-weight: 400;
  font-size: 1em;
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ Bracket wrap [ ... ] Ã¢â€â‚¬Ã¢â€â‚¬ */
.fw-bracket {
  color: rgba(120, 160, 220, 0.5);
  font-weight: 400;
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ Group wrap ( ... ) xN Ã¢â‚¬â€ brackets, x separator Ã¢â€â‚¬Ã¢â€â‚¬ */
.fw-group-bracket {
  color: var(--fw-color-group);
  font-weight: 400;
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ Time coloring in all line types Ã¢â€â‚¬Ã¢â€â‚¬ */
/* ⚠ L'opacity .55 fait tomber les unites de temps a 2,5:1 — sous le seuil WCAG,
   sur l'ecran qu'on lit a bout de bras en salle. C11 l'avait retiree ; Chris a
   demande que le rendu du programme reste EXACTEMENT comme avant, donc elle est
   remise. Ecart connu et assume, inscrit dans la baseline du banc. */
.fw-time-sec {
  color: var(--fw-color-time);
  /* Etait 0.55, ce qui diluait l'accent vers le fond et donnait 2.55:1 en sombre,
     2.50:1 en clair — les secondes d'un chrono, sur l'ecran qu'on lit a bout de bras
     entre deux series. 0.9 est la premiere valeur qui passe le seuil (4.68 / 5.05) et
     l'ecart avec 0.55 se voit a peine : le temps reste visuellement en retrait des
     noms d'exercices. Aucune taille ni position modifiee. */
  opacity: 0.9;
  font-weight: 400;
}
/* ⚠ C125 — LA JUMELLE OUBLIEE. Un lot precedent a monte `.fw-time-sec` de 0.55
   a 0.9 « la premiere valeur qui passe le seuil », et `.fw-time-prime` porte le
   commentaire « alignee sur la meme opacite, pour que deux regles jumelles ne se
   remettent pas a diverger ». `.fw-time-min` etait la TROISIEME du groupe et est
   restee a 0.55 : 2,41:1 en clair, 2,60:1 en sombre — mesure au pixel. La regle
   etait ecrite, elle n'avait ete appliquee qu'a deux des trois. */
.fw-time-min {
  color: var(--fw-color-time);
  opacity: 0.9;
  font-weight: 400;
}
.fw-time-prime {
  color: var(--fw-color-time);
  /* Meme encre et meme role que .fw-time-sec : alignee sur la meme opacite, pour que
     deux regles jumelles ne se remettent pas a diverger. */
  opacity: 0.9;
  font-size: 0.7em;
  font-family: 'Courier New', monospace;
  letter-spacing: -0.05em;
  vertical-align: 0.45em;
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ Syntax highlighter Ã¢â‚¬â€ line types Ã¢â€â‚¬Ã¢â€â‚¬ */
.fw-rest-line {
  color: var(--fw-color-rest);
  font-style: italic;
  font-size: 0.85em;
  letter-spacing: 0.05em;
}
/* ⚠ C125 — CE BLOC PORTAIT UN rgba EN DUR, AVEUGLE AU THEME.
   Meme defaut qu'en C117 pour le verre des en-tetes : une seule valeur pour les
   deux fonds. Sur le noir c'est un gris doux, sur le blanc c'est 1,68:1 —
   mesure au pixel, quasiment invisible. Le jeton, lui, a une valeur par theme.
   ⚠ ET C'EST CE BLOC QUI PEINT, PAS `.fw-rest-label` (spe 0-2-0 contre 0-1-0) :
   corriger `--fw-color-rest` seul ne changeait rien, verifie au banc. */
.fw-rest-line .fw-rest-label,
.fw-rest-line .fw-time-min,
.fw-rest-line .fw-time-sec,
.fw-rest-line .fw-time-prime,
.fw-rest-line .fw-rep {
  color: var(--fw-color-rest-label);
  opacity: 1;
  font-weight: 500;
  font-size: inherit;
  font-style: inherit;
  font-family: inherit;
}
.fw-timecap-line {
  color: var(--fw-color-timecap);
  font-weight: 400;
  font-style: italic;
  letter-spacing: 0.02em;
}
/* On TC lines, all tokens stay gold and thin */
.fw-timecap-line .fw-time-min,
.fw-timecap-line .fw-time-prime,
.fw-timecap-line .fw-rep,
.fw-timecap-line .fw-repeat {
  color: var(--fw-color-timecap);
  opacity: 1;
  font-weight: 400;
}
/* EMOM line: only the label/dur are purple Ã¢â‚¬â€ exercise stays default white */
.fw-emom-line {
  /* no line-level color override Ã¢â‚¬â€ label/dur handle their own color */
}

/* AMRAP line: only the label/dur are orange Ã¢â‚¬â€ exercise stays default white */
.fw-amrap-line {
  /* no line-level color override Ã¢â‚¬â€ label/dur handle their own color */
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ Syntax highlighter Ã¢â‚¬â€ keyword badges & labels Ã¢â€â‚¬Ã¢â€â‚¬ */
.fw-emom-label {
  color: var(--fw-color-emom);
  /* C98 : 700 -> 600, comme .fw-rep. Un mot en capitales a deja plus de presence
     qu'un mot en bas-de-casse a graisse egale — le laisser a 700 pendant que le
     corps descend a 500 en aurait fait le seul element lourd de la ligne. */
  font-weight: 600;
  letter-spacing: 0.04em;
}
.fw-emom-dur {
  color: var(--fw-color-emom);
  opacity: 0.8;
  font-size: 1.1em;
  font-weight: 500;
}
.fw-emom-sep {
  color: rgba(160, 160, 200, 0.45);
  margin: 0 0.2em;
}
.fw-badge-deadstop {
  background: rgba(255, 90, 90, 0.15);
  color: var(--fw-color-deadstop);
  border: 1px solid rgba(255, 90, 90, 0.3);
  font-size: 0.72em;
  font-weight: 700;
  padding: 0.15em 0.5em;
  line-height: 1;
  border-radius: 0.4em;
  letter-spacing: 0.05em;
  display: inline-block;
  vertical-align: middle;
  white-space: nowrap;
}

.fw-badge-slow {
  background: rgba(100, 200, 255, 0.12);
  color: var(--fw-color-slow);
  border: 1px solid rgba(100, 200, 255, 0.3);
  font-size: 0.72em;
  font-weight: 700;
  padding: 0.15em 0.5em;
  line-height: 1;
  border-radius: 0.4em;
  letter-spacing: 0.05em;
  display: inline-block;
  vertical-align: middle;
  white-space: nowrap;
}
.fw-badge-explosive {
  background: rgba(255, 165, 2, 0.12);
  color: var(--fw-color-explosive);
  border: 1px solid rgba(255, 165, 2, 0.3);
  font-size: 0.72em;
  font-weight: 700;
  padding: 0.15em 0.5em;
  line-height: 1;
  border-radius: 0.4em;
  letter-spacing: 0.05em;
  display: inline-block;
  vertical-align: middle;
  white-space: nowrap;
}

/* Badges (Unbroken / Dead Stop / Slow / Explosive) restent inline dans le flux du texte —
   pill inline-block, suivent le wrap naturellement, aucun positionnement absolu.
   La classe .badges-hidden masque le pill au boot le temps que le layout se stabilise
   (retirée par repositionBadges au prochain frame). */
.badges-hidden .fw-unbroken,
.badges-hidden .fw-badge-deadstop,
.badges-hidden .fw-badge-slow,
.badges-hidden .fw-badge-explosive { opacity: 0; }

/* Ã¢â€â‚¬Ã¢â€â‚¬ Syntax highlighter Ã¢â‚¬â€ repeat multipliers Ã¢â€â‚¬Ã¢â€â‚¬ */
.fw-repeat {
  color: var(--fw-color-mult, #ffa502);
  font-size: 0.8em;
  font-weight: 500;
}
.fw-rep-x {
  /* Le « × » entre le nombre et les reps. La valeur du build 5 etait AVEUGLE AU THEME :
     un seul rgba pour les deux fonds, donc 2.18:1 en sombre et 1.40:1 en clair — un
     caractere qui porte du sens (5 × 10) et qu'on ne voyait quasiment pas. Chris a
     demande que l'ECRITURE, les espaces et la position des mots soient identiques au
     build 5, et laisse les couleurs a mon jugement : taille, marge et graisse sont
     donc intactes, seule la clarte bouge. Teinte d'origine conservee (H=240), clarte
     resolue par theme pour atteindre 4.5:1 — il reste nettement plus terne que les
     chiffres qu'il separe, ce qui etait tout son role. */
  color: #80839D;
  font-size: 0.9em;
  margin: 0;
  font-weight: 400;
}
[data-theme="light"] .fw-rep-x { color: #6E6EAB; }

/* Ã¢â€â‚¬Ã¢â€â‚¬ Preview Ã¢â‚¬â€ bigger font Ã¢â€â‚¬Ã¢â€â‚¬ */
#programPreview .fw-line {
  font-size: clamp(14px, 3vw, 17px);
  line-height: 1.8;
  display: block;
  white-space: normal;
}
#programPreview .fw-sep-block {
  height: 10px;
}


/* Ã¢â€â‚¬Ã¢â€â‚¬ Detail date row with timer shortcut Ã¢â€â‚¬Ã¢â€â‚¬ */
.qr-wrapper {
  display: flex;
  justify-content: center;
}
.qr-container {
  background: var(--bg-base);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius-lg);
  padding: 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: none;
}
/* C83 — L'ATTENTE OCCUPE LA PLACE DU QR, ELLE NE LA CREUSE PAS.
   Le lien ne se fabrique plus qu'au moment ou on demande l'onglet QR : il y a
   donc desormais un instant sans code a afficher. 180x180, c'est exactement la
   taille du QR — sans ca, la modale se contracterait puis se redilaterait quand
   l'image arrive, et un saut de mise en page se lit comme un bug meme quand
   tout marche. */
.qr-attente {
  width: 180px;
  height: 180px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 12px;
  font-size: var(--fs-xs);
  color: var(--text-2);
  line-height: 1.4;
}
.qr-attente.qr-erreur { color: var(--accent-red); }
.qr-container img,
.qr-container canvas {
  display: block;
  border-radius: 4px;
}
.share-expiry {
  font-size: 0.75rem;
  color: var(--text-muted);
  text-align: center;
  margin: 0;
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   SHARE MODE Ã¢â‚¬â€ cache l'app avant que JS tourne
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.share-mode #authScreen,
.share-mode .sidebar,
.share-mode .topbar,
.share-mode .main-content { display: none !important; }

.share-mode #shareView { display: flex !important; }

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   SHARE VIEW (page publique)
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
#shareView {
  display: none;
  position: fixed;
  inset: 0;
  background: var(--bg-base);
  background-image:
    radial-gradient(ellipse 70% 50% at 15% 10%, var(--glow-primary) 0%, transparent 55%),
    radial-gradient(ellipse 50% 40% at 85% 90%, var(--glow-primary) 0%, transparent 55%);
  z-index: 9999;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  flex-direction: column;
  align-items: center;
}
.share-page {
  width: 100%;
  max-width: 640px;
  margin: 0 auto;
  padding: 40px 20px 60px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  min-height: 100vh;
}
.share-page-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding-bottom: 8px;
}
.share-page-logo {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 1.8rem;
  letter-spacing: -0.02em;
}
.share-page-tagline {
  font-size: 0.78rem;
  color: var(--text-muted);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 600;
}
.share-page-content {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.share-training-hero {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  padding: 24px;
  position: relative;
  overflow: hidden;
}
.share-training-hero::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--border-bright), transparent);
}
.share-training-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.share-training-name {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 800;
  /* C67 : la seule surface ou le nom etait en Barlow 800 mais en MINUSCULES.
     Corrige ici, dans sa propre regle, pour ne creer aucune egalite de
     specificite (cf. le grand commentaire de C67 en fin de fichier). */
  text-transform: uppercase;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  margin-bottom: 8px;
}
.share-workout-block {
  margin: 0;
}
.share-no-select {
  user-select: none;
  -webkit-user-select: none;
}
.share-import-action {
  padding: 20px 16px 36px;
  display: flex;
  justify-content: center;
}
.share-import-btn { min-width: 200px; }
.share-page-cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 32px 0 0;
  border-top: 1px solid var(--border);
  margin-top: auto;
}
.share-cta-text {
  font-size: 0.85rem;
  color: var(--text-muted);
  text-align: center;
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ Bouton inbox topbar mobile Ã¢â€â‚¬Ã¢â€â‚¬ */
/* .topbar-inbox-btn : hérite de .topbar-icon-btn (forme/taille), ajoute juste le positionnement du badge */
.topbar-inbox-btn .inbox-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  font-size: var(--fs-xs);
  min-width: 14px;
  height: 14px;
  line-height: 14px;
  padding: 0 3px;
}

/* .topbar-inbox-btn : hérite de .hdr-btn (blanc, verre, radius 12) — badge rouge sur le coin (voir bloc badge). */
.gallery-topbar .topbar-inbox-btn { position: relative; }
/* Pastille notif rouge avec nombre (override du badge) — visible uniquement quand JS la révèle (count > 0) */
.gallery-topbar .topbar-inbox-btn .inbox-badge {
  top: -3px;
  right: -5px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: #ff4757;
  color: #fff;
  border: 2px solid var(--bg-base);
  font-size: var(--fs-xs);
  font-weight: 700;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* .profile-help-pill : hérite de .hdr-btn.hdr-btn-text (36 haut, radius 12, padding 0 14px). */
.profile-help-pill svg { flex-shrink: 0; }

/* Ã¢â€â‚¬Ã¢â€â‚¬ Inbox item Ã¢â‚¬â€ infos ÃƒÂ©tendues Ã¢â€â‚¬Ã¢â€â‚¬ */
/* Inbox item redesign — compact (title line + meta + notes + stat cols) */
.inbox-item-title-line {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}
.inbox-item-setnum {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-muted);
  flex-shrink: 0;
}
.inbox-item-meta-line {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  flex-wrap: nowrap;
  min-width: 0;
}
.inbox-item-cat {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 55%;
}
.inbox-item-cat-unknown { color: var(--text-muted); }
.inbox-item-sep { color: var(--text-muted); opacity: 0.5; }
.inbox-item-meta-line .card-diff {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  opacity: 1;
  white-space: nowrap;
}
.inbox-item-notes {
  font-size: 0.82rem;
  color: var(--text-muted);
  font-style: italic;
  line-height: 1.4;
}
.inbox-item-stats { margin-top: 2px; }
.inbox-item-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 4px;
}
.inbox-item-footer .inbox-mod-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.inbox-item-footer .inbox-mod-link svg { flex-shrink: 0; }
.share-cta-btn {
  width: 100%;
  max-width: 320px;
  justify-content: center;
  font-size: 1rem;
  padding: 14px 24px;
  box-shadow: none;
}
.share-loading {
  display: flex;
  align-items: center;
  gap: 12px;
  justify-content: center;
  padding: 40px;
  color: var(--text-muted);
}
.share-expired {
  text-align: center;
  padding: 60px 20px;
  color: var(--text-secondary);
}


/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   SHARE MODAL Ã¢â‚¬â€ ONGLETS
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.modal-share { overflow: hidden; }
.share-tabs {
  display: flex;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.share-tab {
  flex: 1;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-muted);
  font-family: var(--font-display);
  font-size: .85rem;
  font-weight: 600;
  padding: 13px 8px;
  cursor: pointer;
  transition: color .18s, border-color .18s;
  letter-spacing: .02em;
}
.share-tab.active {
  color: var(--theme-color);
  border-bottom-color: var(--theme-color);
}
.share-tab-content { display: flex; flex-direction: column; }

/* Ã¢â€â‚¬Ã¢â€â‚¬ Onglet Envoyer ÃƒÂ  Ã¢â€â‚¬Ã¢â€â‚¬ */
.share-search-row {
  display: flex;
  gap: 8px;
  align-items: center;
}
.share-search-row .form-input { flex: 1; min-width: 0; }
.share-search-row .btn-primary { white-space: nowrap; flex-shrink: 0; }
/* ══════════════════════════════════════════════════════════════════════════
   C100 — LE RETOUR D'ENVOI DEVIENT UN BANDEAU

   L'ETAT MESURE : du texte vert nu (`color: var(--accent-green)`), sans fond ni
   forme, pose en STYLE INLINE depuis le JS — donc ni thematisable ni testable.
   Il flottait entre la liste des contacts et le bouton Fermer, sans rien qui le
   rattache a l'action qui vient d'avoir lieu.

   Un bandeau : fond teinte a 10 %, filet a 28 %, une icone. La couleur cesse
   d'etre le seul porteur de l'information — c'est la meme regle que C43 sur
   l'etoile favori, ou le contour distingue l'etat SANS la couleur. Ici l'icone
   fait ce travail : coche pour reussi, croix pour echoue, point pour en cours.

   `:empty { display: none }` : sans lui, le bandeau reserve sa hauteur en
   permanence et laisse un trou au repos. `min-height` faisait ca. */
.send-result {
  font-size: .82rem;
  margin-top: 12px;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 10px 12px;
  border-radius: 12px;
  border: 1px solid transparent;
  line-height: 1.35;
  transition: color .2s, background .2s, border-color .2s;
}
.send-result:empty { display: none; }
.send-result svg { width: 16px; height: 16px; flex-shrink: 0; }
.send-result .sr-nom { font-weight: 700; }

.send-result.sr-ok {
  color: var(--accent-green);
  background: rgba(52, 211, 153, .10);
  border-color: rgba(52, 211, 153, .28);
}
.send-result.sr-ko {
  color: var(--accent-red);
  background: rgba(255, 71, 87, .10);
  border-color: rgba(255, 71, 87, .26);
}
/* En cours : pas de fond. Une attente n'est pas un resultat, elle ne doit pas
   avoir le meme poids visuel qu'une confirmation. */
.send-result.sr-attente { color: var(--text-muted); padding: 8px 0; }
.recent-label {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: .1em;
  margin: 14px 0 6px;
}
/* ══════════════════════════════════════════════════════════════════════════
   C99 — LES CONTACTS RECENTS DEVIENNENT UNE LISTE

   L'ETAT MESURE : huit puces de largeurs inegales sur QUATRE lignes, 178px de
   haut, et des cibles de 32px — sous le plancher de 44 du projet.

   Une liste pleine largeur est a la fois plus compacte (136px contre 178) et
   plus lisible : une ligne par contact, une pastille d'initiales, un nom
   tronque proprement. Le contact le plus recent reste VISIBLE en tete, donc
   l'action frequente — renvoyer a la meme personne — coute toujours UN tap.
   C'est la seule chose qu'une liste pouvait faire perdre ; elle ne la perd pas.

   ⚠ LE PIEGE, ET IL EST DEJA DOCUMENTE TROIS FOIS DANS CE PROJET.
   `.modal-body` est en `display: flex; flex-direction: column`. Un enfant
   scrollable y herite de `flex-shrink: 1` et se COMPRESSE au lieu de defiler —
   bug de l'inbox (862fc88), rejoue sur `.prepa-cal-content` puis
   `.prepa-goals-content`. D'ou les deux precautions ci-dessous, ensemble :
     - `flex-shrink: 0` sur le bloc, pour qu'il ne soit pas ecrase par la
       colonne flex qui le contient ;
     - `display: block` sur la zone qui defile, pour qu'aucun de ses enfants
       n'herite d'un flex-shrink.
   Retirer l'une des deux ramene le defaut.
   ══════════════════════════════════════════════════════════════════════════ */
.recent-recipients { margin-top: 2px; display: block; flex-shrink: 0; }

/* La hauteur est bornee : deplier montre TOUT, mais la modale ne grandit pas.
   ⚠ 240px, PAS 216. Une ligne fait 48px et les ecarts 6px : 216 donne quatre
   lignes PLEINES, et rien a l'ecran ne dit alors qu'il y a une suite — mesure
   faite, la premiere version tombait pile sur ce compte. 240 laisse depasser la
   moitie de la cinquieme, et c'est cette demi-ligne coupee qui sert
   d'affordance, sans avoir besoin d'une barre de defilement visible. */
.contacts-liste {
  display: block;
  max-height: 240px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;   /* le defilement ne fuit pas vers la modale */
}
.contacts-ligne {
  display: flex; align-items: center; gap: 11px;
  width: 100%; min-height: 48px; padding: 0 12px;
  border-radius: 14px;
  background: var(--surface-2);
  border: 1px solid var(--border-0);
  color: var(--text-primary);
  font-size: .88rem; font-family: var(--font-body); text-align: left;
  cursor: pointer;
  transition: border-color .18s, background .18s;
}
.contacts-ligne + .contacts-ligne { margin-top: 6px; }
@media (hover: hover) {
  .contacts-ligne:hover { border-color: var(--theme-color); background: var(--surface-3); }
}
.contacts-ligne:active { opacity: .7; }
/* La pastille d'initiales : elle donne une identite a chaque ligne sans photo,
   et c'est ce qui fait qu'une liste se parcourt plus vite qu'une mosaique. */
.contacts-pastille {
  width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0;
  background: linear-gradient(140deg, var(--theme-color), var(--theme-secondary));
  display: flex; align-items: center; justify-content: center;
  font-size: .72rem; font-weight: 700; color: #fff; letter-spacing: .02em;
}
.contacts-nom {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.contacts-chev {
  width: 16px; height: 16px; flex-shrink: 0; opacity: .45;
  transition: transform .2s var(--ease-v2);
}
.contacts-ligne.deplie .contacts-chev { transform: rotate(180deg); }
/* La ligne « voir les N autres » : meme gabarit que les contacts pour que la
   colonne reste alignee, mais en retrait pour ne pas se faire passer pour un. */
.contacts-plus { background: transparent; border-style: dashed; color: var(--text-2); }
.contacts-plus .contacts-pastille {
  background: var(--surface-4); color: var(--text-2); font-size: .68rem;
}

.recipient-chip {
  display: inline-flex;
  align-items: center;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 5px 13px;
  font-size: .78rem;
  color: var(--text-primary);
  cursor: pointer;
  margin: 4px 6px 0 0;
  transition: border-color .18s, color .18s, background .18s;
  font-family: var(--font-body);
}
@media (hover: hover) {
  .recipient-chip:hover {
    border-color: var(--theme-color);
    color: var(--theme-color);
    background: var(--glow-primary);
  }
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   INBOX BADGE
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.inbox-badge {
  background: var(--accent-red);
  color: #fff;
  font-size: var(--fs-xs);
  font-weight: 800;
  border-radius: 10px;
  padding: 2px 6px;
  margin-left: auto;
  min-width: 18px;
  text-align: center;
  line-height: 1.4;
  font-family: var(--font-display);
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   INBOX MODAL
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.inbox-modal-body { padding: 0; }
.inbox-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-height: 58vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  min-height: 0; /* CRUCIAL en enfant flex : sinon les enfants (items) ne peuvent pas se laisser dépasser → compression */
  padding: 16px;
  overscroll-behavior: contain;
}
.inbox-empty {
  text-align: center;
  color: var(--text-muted);
  padding: 32px 16px;
  font-size: .9rem;
}
/* ══════════════════════════════════════════
   MODALE INBOX V2 — items minicard anthracite avec halo catégorie
   Handlers accept/decline/block/report intacts, seul le markup + skin change.
   ══════════════════════════════════════════ */
.inbox-item {
  position: relative;
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-0);
  border-radius: 16px;
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow: hidden;
  flex-shrink: 0; /* Interdiction de compression : ≥4 items dépassent max-height 58vh → la LISTE scroll,
                     pas les items qui se compressent (sinon boutons Refuser/Accepter clippés par overflow:hidden). */
}
/* Halo catégorie discret — s'affiche seulement si --cat-rgb posé sur l'item */
.inbox-item::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(120% 100% at 0% 0%, rgba(var(--cat-rgb, 255,255,255), .10), transparent 60%);
  pointer-events: none;
  opacity: 0;
  transition: opacity .2s;
}
.inbox-item[style*="--cat-rgb"]::before { opacity: 1; }
.inbox-item > * { position: relative; z-index: 1; }

/* Line 1 : De <sender> ← → date */
.inbox-item-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: 12px;
  color: var(--text-3);
}
.inbox-sender strong {
  color: var(--theme-color);
  font-weight: 700;
}
.inbox-date { font-family: var(--font-body); font-size: 11px; color: var(--text-3); }

/* Line 2 : #num (couleur catégorie) + NOM (16px, 800, uppercase, blanc) */
.inbox-item-title-line {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}
.inbox-item-setnum {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 13px;
  color: rgba(var(--cat-rgb, 255,255,255), .85);
  letter-spacing: 0;
  flex-shrink: 0;
}
.inbox-item-name {
  font-weight: 800;
  font-size: 16px;
  color: #fff;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: -0.01em;
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Line 3 : chip catégorie (point + label) + difficulté texte 11px coloré par niveau.
   Reset explicite : les anciennes règles inbox-item-meta-line V1 (font-size .72, font-weight 700,
   letter-spacing .04em, text-transform uppercase, flex-wrap nowrap) sont toujours au-dessus dans
   le fichier — on override ici. */
.inbox-item-meta-line {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
}
.inbox-item-cat {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  max-width: none;
  overflow: visible;
}
.inbox-item-cat-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: rgba(255,255,255,.4);
  flex-shrink: 0;
}
.inbox-item-diff {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-2);
}
.inbox-item-diff.diff-light  { color: #2ed573; }
.inbox-item-diff.diff-medium { color: var(--warn); }
.inbox-item-diff.diff-hard   { color: var(--danger); }
.inbox-item-diff.diff-max    { color: #a855f7; }

/* Line 4 optionnelle : notes en italique muted */
.inbox-item-notes {
  font-size: 12.5px;
  color: var(--text-2);
  font-style: italic;
  line-height: 1.4;
}

/* Line 5 : stats inline discrètes */
.inbox-item-stats-inline {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-2);
  font-family: var(--font-body);
}

/* Line 6 : Refuser + Accepter — 50/50, 42px */
.inbox-item-actions {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}
.inbox-item-actions .btn-secondary,
.inbox-item-actions .btn-primary {
  flex: 1;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  padding: 0;
  height: 42px;
}
.inbox-item-actions .btn-primary svg { width: 15px; height: 15px; }

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   SUPPORT VIEW
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
#view-support.active,
#view-admin-support.active {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  padding: 0 40px 0;
  background: var(--bg-base);
  z-index: 10;
  display: flex;
  flex-direction: column;
}
.support-chat-wrap {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.support-messages {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 4px 0 16px;
  min-height: 0;
}
.support-loading {
  text-align: center;
  color: var(--text-muted);
  padding: 32px 16px;
  font-size: .9rem;
}
.support-empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  color: var(--text-muted);
  text-align: center;
  padding: 32px 16px;
}
.support-empty svg {
  width: 44px; height: 44px;
  opacity: 0.3;
  stroke: var(--theme-color);
}
.support-empty p {
  font-size: .95rem;
  font-weight: 600;
  color: var(--text-secondary);
  margin: 0;
}
.support-empty span { font-size: .82rem; }
.support-msg {
  display: flex;
  flex-direction: column;
  gap: 3px;
  max-width: 78%;
}
.support-msg-user  { align-self: flex-end;  align-items: flex-end; }
.support-msg-admin { align-self: flex-start; align-items: flex-start; }
.support-msg-label {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--theme-color);
}
.support-bubble {
  padding: 10px 14px;
  border-radius: 16px;
  font-size: .88rem;
  line-height: 1.5;
  word-break: break-word;
  white-space: pre-wrap;   /* préserve les retours à la ligne des messages */
}
.support-msg-user  .support-bubble {
  background: var(--gradient-accent);
  color: var(--theme-text-on-accent, #fff);
  border-bottom-right-radius: 4px;
}
.support-msg-admin .support-bubble {
  background: var(--surface-3);
  border: 1px solid var(--border);
  color: var(--text-primary);
  border-bottom-left-radius: 4px;
}
.support-msg-time {
  font-size: var(--fs-xs);
  font-family: var(--font-body);
  color: var(--text-muted);
}
.support-input-bar {
  display: flex;
  gap: 10px;
  padding: 16px 0 max(16px, env(safe-area-inset-bottom, 16px));
  border-top: 1px solid var(--border);
  margin-top: 8px;
  flex-shrink: 0;
}
.support-input-bar .form-input { flex: 1; min-width: 0; }
.support-input-bar .btn-primary {
  flex-shrink: 0;
  padding: 0 20px;
  display: flex;
  align-items: center;
  align-self: stretch;
  gap: 6px;
}
.support-input-bar .btn-primary svg { width: 15px; height: 15px; }
@media (max-width: 768px) {
  #view-support.active,
  #view-admin-support.active {
    left: 0;
    padding: 0 20px 0;
  }
}

.support-chat-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: calc(var(--safe-top) + 16px) 0 16px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.support-back-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px; height: 36px;
  border-radius: 50%;
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--text-secondary);
  transition: background 0.15s, color 0.15s;
}
@media (hover: hover) {
  .support-back-btn:hover { background: var(--bg-card); color: var(--text-primary); }
}
.support-back-btn svg { width: 20px; height: 20px; }
.support-chat-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--text-primary);
  letter-spacing: 0.02em;
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   ADMIN SUPPORT
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
#adminThreadList {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
#adminThreadDetail {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.admin-detail-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 8px;
  flex-shrink: 0;
}
.admin-thread-item {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 14px 16px;
  cursor: pointer;
  transition: background .15s, border-color .15s;
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
@media (hover: hover) {
  .admin-thread-item:hover { background: var(--surface-3); border-color: var(--border-2); }
}
.admin-thread-closed { opacity: 0.45; }
.admin-thread-info { flex: 1; min-width: 0; }
.admin-thread-pseudo {
  font-weight: 600;
  font-size: .92rem;
  margin-bottom: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.admin-thread-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  flex-shrink: 0;
}
.admin-thread-time { font-size: var(--fs-xs); color: var(--text-muted); white-space: nowrap; }
.admin-unread-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--theme-color);
  visibility: hidden;
}
.admin-thread-unread .admin-unread-dot { visibility: visible; }
.admin-thread-unread {
  border-left: 4px solid var(--theme-color);
  background: var(--glow-primary);
  padding-left: 12px;
}
.admin-thread-unread .admin-thread-pseudo {
  color: var(--theme-color);
  font-weight: 700;
}
.status-badge {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: .04em;
  padding: 3px 8px;
  border-radius: 20px;
}
.status-open   { background: rgba(0,212,127,.15); color: #00d47f; border: 1px solid rgba(0,212,127,.3); }
.status-closed { background: var(--surface-3); color: var(--text-muted); border: 1px solid var(--border); }

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   iOS PWA Ã¢â‚¬â€ NATIVE APP FEEL
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */

/* Prevent double-tap zoom on all interactive elements */
button, a, label,
.card, .nav-link, .btn-icon, .btn-primary, .btn-secondary,
.card-fav-btn, .modal-close, .auth-tab, .share-tab,
[role="button"], [onclick] {
  touch-action: manipulation;
}
/* Global tap scale micro-interaction */
button:active:not(.btn-primary), .btn-icon:active, .auth-tab:active,
.chip:active, .nav-link:active, .itimer-btn:active,
.sw-pick-btn:active {
  transform: scale(0.94);
  transition: transform 0.08s ease;
}

/* iOS auto-zoom fix Ã¢â‚¬â€ minimum 16px on all inputs/textareas/selects */
/* iOS Safari: font-size < 16px triggers auto-zoom which misplaces the cursor.
   !important overrides any more-specific class (e.g. .form-input at 0.92rem).
   translateZ forces GPU layer Ã¢â‚¬â€ prevents cursor offset on PWA install. */
input, textarea, select {
  font-size: 16px !important;
  caret-color: auto;
  transform: translateZ(0);
  -webkit-transform: translateZ(0);
}

/* Scrollable areas Ã¢â‚¬â€ native momentum */
.gallery-grid, .history-list, .modal-body,
.inbox-list, .detail-content, .view {
  -webkit-overflow-scrolling: touch;
}

/* Full-height on iOS Ã¢â‚¬â€ prevents bottom bar gap */
html, body {
  height: -webkit-fill-available;
}

/* Prevent pull-to-refresh and elastic bounce */
body {
  overscroll-behavior-y: none;
}

/* Remove tap highlight everywhere */
* {
  -webkit-tap-highlight-color: transparent;
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   SPLASH SCREEN
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.splash-screen {
  position: fixed;
  inset: 0;
  background: var(--bg-base);
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.45s ease, visibility 0.45s ease;
}
.splash-screen.hidden {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
.splash-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}
.splash-logo {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 3.2rem;
  letter-spacing: 0.14em;
  line-height: 1;
  animation: splashLogoIn 0.7s cubic-bezier(0.4,0,0.2,1) both;
}
.splash-logo .logo-nex { color: var(--text-primary); }
.splash-logo .logo-set {
  color: var(--theme-color);
  animation: splashLogoIn 0.7s 0.1s cubic-bezier(0.4,0,0.2,1) both,
             logo-set-pulse 2s 0.8s ease-in-out infinite;
}
@keyframes splashLogoIn {
  from { opacity: 0; transform: scale(0.85) translateY(10px); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}
.splash-tagline {
  font-size: 0.72rem;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-family: var(--font-body);
  margin-top: -4px;
}
.splash-ring {
  width: 28px;
  height: 28px;
  margin-top: 12px;
  border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.08);
  border-top-color: var(--glow-primary);
  filter: brightness(3);
  animation: splashSpin 0.75s linear infinite;
}
@keyframes splashSpin {
  to { transform: rotate(360deg); }
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   AI LOADING OVERLAY
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.ai-loader {
  position: fixed;
  inset: 0;
  z-index: 9998;
  display: flex;
  align-items: center;
  justify-content: center;
  /* C117 : meme jeton que le voile des modales. C'est un voile plein ecran
     pendant un chargement — en clair il etait noir a 55%. */
  background: var(--scrim-modal);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.22s ease, visibility 0.22s ease;
}
.ai-loader.visible {
  opacity: 1;
  visibility: visible;
  pointer-events: all;
}
.ai-loader-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
}
.ai-loader-ring {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 2.5px solid var(--border-1);
  border-top-color: var(--glow-primary);
  filter: brightness(2.5);
  animation: splashSpin 0.75s linear infinite;
}
.ai-loader-label {
  font-family: var(--font-body);
  font-size: 0.72rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-muted);
  animation: aiLabelPulse 1.8s ease-in-out infinite;
}
@keyframes aiLabelPulse {
  0%, 100% { opacity: 0.5; }
  50%       { opacity: 1; }
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   DETAIL Ã¢â‚¬â€ DROPDOWN MENU "..."
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.detail-menu-wrap {
  position: relative;
  z-index: 501; /* wrap au-dessus des calques V2 — laissé en place, inoffensif */
}
.detail-dropdown {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 168px;
  background: var(--bg-surface);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius-md);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  box-shadow: 0 8px 32px rgba(0,0,0,0.5), 0 0 0 1px var(--border);
  z-index: 300;
  display: none;
  flex-direction: column;
  overflow: hidden;
  animation: dropdownIn var(--dur-micro) ease;
}
.detail-dropdown.open { display: flex; }
@keyframes dropdownIn {
  from { opacity: 0; transform: translateY(-6px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.detail-dropdown-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  font-size: 0.9rem;
  color: var(--text-primary);
  background: none;
  border: none;
  cursor: pointer;
  font-family: var(--font-body);
  transition: background 0.15s;
  text-align: left;
  width: 100%;
  touch-action: manipulation;
}
@media (hover: hover) {
  .detail-dropdown-item:hover { background: var(--bg-hover); }
}
.detail-dropdown-item:active { background: var(--bg-hover); }
.detail-dropdown-item svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.detail-dropdown-sep {
  height: 1px;
  background: var(--border);
  margin: 2px 0;
}
.detail-dropdown-danger { color: var(--accent-red); }

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   USER-SELECT Ã¢â‚¬â€ app non-sÃƒÂ©lectionnable
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
body, .sidebar, .topbar, .nav-link, .workout-card,
.detail-hero, .detail-workout-block, .detail-badge,
.btn-icon, .btn-primary, .btn-secondary, .btn-back,
.view-title, .card-name, .card-date,
.fw-line, .fw-rest, .fw-emom, .fw-amrap, .fw-amrap-dur, .fw-comment, .fw-unbroken, .fw-timecap,
.fw-rest-line, .fw-timecap-line, .fw-emom-line, .fw-amrap-line, .fw-emom-label, .fw-emom-dur,
.fw-badge-deadstop, .fw-badge-slow, .fw-badge-explosive, .fw-repeat, .fw-rep-x, .fw-title, .fw-header,
.chip, .modal-title, .detail-title, .auth-logo,
.splash-logo, .topbar-logo, .sidebar-logo,
.detail-set-number,
.history-item, .history-name {
  user-select: none;
  -webkit-user-select: none;
}
input, textarea, [contenteditable] {
  user-select: text;
  -webkit-user-select: text;
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   CARD HIGHLIGHT Ã¢â‚¬â€ retour galerie depuis dÃƒÂ©tail
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
@keyframes card-highlight {
  0%   { box-shadow: 0 0 0 0px var(--theme-color), 0 0  0px var(--glow-primary); }
  35%  { box-shadow: 0 0 0 2px var(--theme-color);  }
  100% { box-shadow: 0 0 0 0px var(--theme-color), 0 0  0px var(--glow-primary); }
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   GALLERY Ã¢â‚¬â€ TROPHY BUTTON (PR shortcut)
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.btn-pr-trophy {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  background: rgba(255,208,96,0.07);
  border: 1px solid rgba(255,208,96,0.18);
  border-radius: var(--radius-md);
  color: var(--accent-gold);
  transition: all 0.2s;
  flex-shrink: 0;
}
@media (hover: hover) {
  .btn-pr-trophy:hover {
    background: rgba(255,208,96,0.15);
    border-color: rgba(255,208,96,0.38);
    box-shadow: 0 0 16px rgba(255,208,96,0.18);
  }
}
.btn-pr-trophy:active { transform: scale(0.93); }
/* â”€â”€ FAB â€” Nouveau training â”€â”€ */
.fab-new {
  position: fixed;
  bottom: 96px;
  right: 20px;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--gradient-accent);
  border: none;
  cursor: pointer;
  display: none;
  align-items: center;
  justify-content: center;
  color: #fff;
  box-shadow: 0 4px 20px var(--glow-strong), 0 2px 8px rgba(0,0,0,0.4);
  z-index: 390;
  transition: transform 0.25s ease, opacity 0.25s ease;
  padding: 0;
  flex-shrink: 0;
  touch-action: manipulation;
}
body.in-gallery .fab-new {
  display: flex;
}
body.in-detail .fab-new { display: none; }
.fab-new svg {
  width: 24px;
  height: 24px;
}
.fab-new:active {
  transform: scale(0.92);
}
.fab-new.fab-hidden {
  transform: translateY(80px);
  opacity: 0;
  pointer-events: none;
}
body.platform-android .fab-new { bottom: calc(96px + env(safe-area-inset-bottom, 0px)); }

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   PR VIEW Ã¢â‚¬â€ LAYOUT & ADD BUTTON
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
/* .btn-add-pr : hérite de .hdr-btn (bg/border/color/hover neutres — pas de teinte thème au repos). */
.btn-add-pr svg { stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; }

/* Ã¢â€â‚¬Ã¢â€â‚¬ Tab bar Ã¢â€â‚¬Ã¢â€â‚¬ */
.pr-tab-bar {
  display: flex;
  gap: 4px;
  background: var(--bg-card);
  border-radius: var(--radius-md);
  padding: 4px;
  margin-bottom: 20px;
}
.pr-tab {
  flex: 1;
  padding: 10px;
  border-radius: 10px;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--text-muted);
  transition: all 0.2s;
}
.pr-tab.active {
  background: var(--surface-4);
  color: var(--text-primary);
  box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ Exercise list Ã¢â€â‚¬Ã¢â€â‚¬ */
.pr-exercise-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.pr-exercise-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 18px;
  background: var(--surface-card-a);
  border: 1px solid var(--surface-card-brd);
  border-radius: var(--radius-lg);
  cursor: pointer;
  transition: all 0.2s;
  box-shadow: 0 8px 20px rgba(0,0,0,.45);   /* Refonte : carte record flottante */
}
[data-theme="light"] .pr-exercise-card { background:#fff; border-color:rgba(12,22,52,.06); box-shadow:0 8px 18px rgba(20,28,60,.10); }
/* Slide-up UNIQUEMENT à l'entrée dans la vue PR (pattern gal-entering).
   Sans ce scope, chaque re-render (switch d'onglet Par exercice/Par date, ajout/suppression PR)
   rejouait l'anim → saccade. Maintenant : joué une fois à showView('pr'), plus jamais après. */
body.pr-entering .pr-exercise-card {
  animation: slide-up var(--dur-v2) var(--ease-v2) both;
}
@media (hover: hover) {
  .pr-exercise-card:hover {
    border-color: var(--border-bright);
    background: var(--bg-hover);
  }
}
.pr-exercise-card:active { transform: scale(0.98); transition: transform 0.08s; }
.pr-exercise-info { flex: 1; min-width: 0; }
.pr-exercise-name {
  font-weight: 700;
  font-size: 0.98rem;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pr-exercise-count {
  /* C113 : 0.72rem = 11,5px, une des neuf valeurs rem hors echelle — et elle
     portait « il y a 3 j », donc de l'information. Elle m'avait echappe :
     mon premier releve filtrait a 11px pile. */
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-top: 2px;
}
.pr-exercise-best {
  font-family: var(--font-body);
  font-size: 0.88rem;
  color: var(--theme-color);
  font-weight: 600;
  white-space: nowrap;
  flex-shrink: 0;
}
.pr-exercise-chevron {
  color: var(--text-muted);
  flex-shrink: 0;
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ Date rows Ã¢â€â‚¬Ã¢â€â‚¬ */
.pr-date-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: all 0.18s;
  margin-bottom: 6px;
}
@media (hover: hover) {
  .pr-date-row:hover { border-color: var(--border-bright); background: var(--bg-card); }
}
.pr-date-row-date {
  font-family: var(--font-body);
  font-size: 0.72rem;
  color: var(--text-muted);
  white-space: nowrap;
  flex-shrink: 0;
}
.pr-date-row-name {
  flex: 1;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-primary);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pr-date-row-perf {
  font-family: var(--font-body);
  font-size: 0.82rem;
  color: var(--theme-color);
  white-space: nowrap;
  flex-shrink: 0;
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ Empty state Ã¢â€â‚¬Ã¢â€â‚¬ */
.pr-empty {
  text-align: center;
  color: var(--text-muted);
  padding: 48px 24px;
  font-size: 0.9rem;
  line-height: 1.6;
}
.pr-empty strong { color: var(--text-secondary); }

/* Ã¢â€â‚¬Ã¢â€â‚¬ Detail panel Ã¢â‚¬â€ overlays prMain Ã¢â€â‚¬Ã¢â€â‚¬ */
.pr-detail-panel { display: none; }
.pr-detail-panel.visible { display: block; animation: slide-up var(--dur-v2) var(--ease-v2); }

.pr-detail-header {
  display: flex;
  align-items: center;
  margin-bottom: 20px;
}
.pr-detail-title { display: none; }

/* Ã¢â€â‚¬Ã¢â€â‚¬ Best PR card Ã¢â‚¬â€ glassmorphism premium Ã¢â€â‚¬Ã¢â€â‚¬ */
.pr-detail-best {
  background: var(--bg-input);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius-xl);
  padding: 22px 24px;
  margin-bottom: 16px;
  position: relative;
  overflow: hidden;
  box-shadow: 0 4px 32px var(--glow-card), inset;
}
.pr-detail-best:empty { display: none; }
.pr-detail-best::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--theme-color), transparent);
  opacity: 0.5;
}
.pr-detail-best::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, var(--glow-card) 0%, transparent 60%);
  pointer-events: none;
  border-radius: inherit;
}
.pr-detail-best-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  position: relative;
  z-index: 1;
}
.pr-detail-best-left { min-width: 0; flex: 1; }
.pr-detail-best-label {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 4px;
}
.pr-detail-best-value {
  font-family: var(--font-display);
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--accent);
  letter-spacing: -0.03em;
  line-height: 1;
  /* C15 : `filter: drop-shadow()` sans argument retire — invalide, donc ignore. */
}
.pr-detail-best-date {
  font-family: var(--font-body);
  font-size: 0.72rem;
  color: var(--text-muted);
  margin-top: 6px;
}
.pr-detail-best-exercise {
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-primary);
  text-align: right;
  flex-shrink: 0;
  align-self: center;
  max-width: 45%;
  word-break: break-word;
  line-height: 1.2;
  opacity: 0.9;
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ Chart Ã¢â‚¬â€ glassmorphism Ã¢â€â‚¬Ã¢â€â‚¬ */
.pr-detail-chart-wrap {
  background: var(--bg-card);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius-lg);
  padding: 16px 12px;
  margin-bottom: 20px;
  box-shadow: 0 2px 20px var(--glow-card);
}
.pr-chart-empty {
  text-align: center;
  color: var(--text-muted);
  font-size: 0.82rem;
  padding: 12px 0;
}

/* Ã¢â€â‚¬Ã¢â€â‚¬ History list Ã¢â€â‚¬Ã¢â€â‚¬ */
.pr-detail-history { display: flex; flex-direction: column; gap: 8px; }
.pr-detail-hist-title {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 10px;
}
.pr-history-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  min-height: 62px;
  background: var(--bg-card);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--border);
  border-left: 3px solid var(--border-bright);
  border-radius: var(--radius-md);
  transition: border-color 0.18s, background 0.18s;
  animation: slide-up var(--dur-v2) var(--ease-v2) both;
  box-sizing: border-box;
}
@media (hover: hover) {
  .pr-history-item:hover { background: var(--bg-card); border-color: var(--border-bright); border-left-color: var(--theme-color); }
}
.pr-history-left {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.pr-history-date {
  font-family: var(--font-body);
  font-size: 0.72rem;
  color: var(--text-muted);
}
.pr-history-notes {
  font-size: 0.75rem;
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pr-history-right { flex-shrink: 0; }
.pr-history-perf {
  font-family: var(--font-body);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-primary);
}
.pr-best-mark {
  color: #ffd700;
  filter: drop-shadow(0 0 8px rgba(255,215,0,0.45));
}
.pr-history-del {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  color: var(--text-muted);
  background: none;
  transition: all 0.18s;
  flex-shrink: 0;
}
@media (hover: hover) {
  .pr-history-del:hover { color: var(--accent-red); background: rgba(255,71,87,0.1); }
}

/* Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â
   PR MODAL Ã¢â‚¬â€ TYPE TOGGLE & SUGGESTIONS
Ã¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢ÂÃ¢â€¢Â */
.pr-type-toggle {
  display: flex;
  gap: 4px;
  background: var(--bg-input);
  border-radius: var(--radius-md);
  padding: 4px;
  margin-bottom: 16px;
}
.pr-type-btn {
  flex: 1;
  padding: 9px;
  border-radius: 10px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-muted);
  transition: all 0.2s;
}
.pr-type-btn.active {
  background: var(--surface-4);
  color: var(--text-primary);
  box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}

/* C45/A2 — le sens de progression d'un record en temps.
   Reutilise .pr-type-toggle : c'est le meme geste (deux choix exclusifs) et il
   doit se reconnaitre comme tel. Seule la marge change — celle du selecteur de
   type separe deux blocs, ici on est DANS un form-group et la phrase d'aide suit
   immediatement. */
/* ══ C53/A4 — une ESTIMATION ne se lit pas comme une MESURE ═══════════════
   Mesure avant correction : sur la carte, REPS et KCAL etaient tous deux en
   16px/800 blanc ; sur le Profil, « Seances faites » et « Kcal totales » tous
   deux en 31,2px/800. Une valeur calculee en LISANT DU TEXTE portait exactement
   le meme poids qu'une valeur enregistree par l'utilisateur.

   On ne touche PAS a la taille. Les colonnes de stats sont alignees sur une
   ligne de base commune, et les quatre tuiles du Profil forment une grille :
   en rapetisser une attirerait l'oeil au lieu de le detourner, et casserait
   l'alignement que C44 vient de mettre au propre. La couleur suffit — plus le
   tilde, qui dit « environ » en un caractere et se comprend sans legende.

   Ce qui n'est PAS fait ici : retirer les kcal. Le backlog demandait de les
   garder, en second plan. L'estimateur est bon (physio, par exercice) ; c'est
   son rang qui etait faux, pas son calcul.

   COMMENT LA REGLE EST POSEE, ET POURQUOI PAS AUTREMENT.
   Une simple `.cc-sv-est { color: ... }` (0,1,0) se fait battre par
   `body.in-gallery .cc-sv` (0,2,1) et par `[data-theme="light"] body.in-gallery
   .cc-sv` (0,3,1) — teste, ca ne marchait qu'au Profil en sombre.
   On pourrait egaler la specificite et compter sur l'ordre du fichier. C'est
   exactement le montage qui a produit le bug de l'etoile favori en C43 : a
   specificite egale, la derniere regle gagne, donc n'importe quel ajout plus bas
   casse le tout en silence. On RESTREINT donc les regles existantes avec
   `:not(...)` — il n'y a plus d'egalite a arbitrer. */
.cc-sv-est,
.dh-sv-est,
.detail-stat-val-est { color: var(--text-secondary); }
/* (0,2,0) : bat proprement la regle de base `.stat-value` (0,1,0), sans egalite. */
.stat-value.stat-value-est { color: var(--text-secondary); }

/* ══ C60 — la fiche de performance a ete RETIREE ══════════════════════════
   Les regles .perf-* ont ete supprimees avec elle (~60 lignes). Voir le grand
   commentaire au-dessus de prescriptionSeance() dans script.js pour le
   pourquoi : le pari « on ajuste une ligne pre-remplie » s'est revele faux a
   l'usage, et sans ajustement la donnee enregistree n'est qu'une copie du
   programme. Le champ Notes, devenu un textarea, prend le relais.
   Une lecon de mise en page vaut d'etre gardee, elle vient de .perf-in et elle
   ne concerne pas la fiche : un element de grille vaut min-width:auto par
   defaut, donc un <input> refuse de descendre sous sa largeur intrinseque
   (~170px). Trois colonnes 1fr debordaient de la modale, et seul le premier
   champ etait visible. `min-width: 0` sur les enfants de grille est la
   correction — vue sur capture d'ecran, JAMAIS par le banc, qui mesure le
   contraste et le corps du texte, pas le debordement. */

/* ══ C5 — section Progression du Profil ═══════════════════════════════════ */
.prog-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; }

/* Le selecteur de periode. Segmenté plutot que deux boutons separes : c'est un
   choix EXCLUSIF, et un segmenté le dit par sa forme avant de le dire par sa
   couleur — un utilisateur ne se demande pas s'il peut cocher les deux. */
.prog-periodes {
  display: flex; gap: 3px; flex-shrink: 0;
  padding: 3px; border-radius: var(--radius-md); background: var(--bg-hover);
}
.prog-per {
  padding: 5px 11px; min-height: 30px; border: none; border-radius: calc(var(--radius-md) - 3px);
  background: none; color: var(--text-muted);
  font-size: .74rem; font-weight: 600; letter-spacing: .01em; cursor: pointer;
}
.prog-per.on { background: var(--surface-3); color: var(--text-primary); }

.prog-total { display: flex; align-items: baseline; gap: 7px; margin-bottom: 14px; }
/* Barlow Condensed comme les autres gros chiffres de l'app (C44/C1) : ce total
   appartient a la meme famille que les tuiles de stats juste au-dessus. */
.prog-total-n { font-family: var(--font-display); font-size: 1.7rem; font-weight: 700; color: var(--text-primary); line-height: 1; }
.prog-total-txt { font-size: .78rem; color: var(--text-secondary); }

/* ⚠ LES BARRES SONT DES <button>, ET LEUR HAUTEUR EST SUR UN <i> INTERIEUR.
   Mettre la hauteur variable sur le bouton lui-meme donnerait des cibles
   tactiles de 3px de haut pour les semaines a zero — intouchables. Le bouton
   fait donc TOUTE la hauteur du graphe (44px et plus), il est alignes en bas,
   et c'est le <i> qui porte la barre visible. La cible ne depend plus de la
   valeur : une semaine vide se tape aussi facilement qu'une semaine a 7. */
.prog-barres {
  display: flex; align-items: stretch; gap: var(--gap, 5px);
  height: 92px;
}
.prog-barre {
  flex: 1; min-width: 0; padding: 0; border: none; background: none; cursor: pointer;
  display: flex; align-items: flex-end;
}
.prog-barre > i {
  display: block; width: 100%; height: var(--h, 0%);
  border-radius: 3px 3px 0 0;
  background: var(--prog-barre);
  transition: background .18s ease;
}
/* Une semaine SANS seance garde un trait, elle ne disparait pas. L'effacer
   donnerait une regularite qu'on n'a pas — meme regle qu'en A3 : l'app montre,
   elle ne flatte pas. Le trait est plus DISCRET que les barres pleines, pour se
   lire comme une absence et non comme une toute petite valeur. La premiere
   version utilisait --border-0, un blanc translucide a 6 % : mesure sur capture,
   il etait moins visible que le fond et l'absence disparaissait. */
.prog-barre.vide > i { height: 3px; border-radius: 2px; background: var(--prog-vide); }
.prog-barre.on > i { background: var(--theme-color); }
@media (hover: hover) { .prog-barre:hover > i { background: var(--text-3); } }
@media (hover: hover) { .prog-barre.on:hover > i { background: var(--theme-color); } }

.prog-axe {
  display: flex; justify-content: space-between;
  margin-top: 7px; font-size: .68rem; color: var(--text-muted);
}

/* Le detail de la semaine tapee : ce qui transforme un graphe decoratif en
   outil (c'est le geste que Strava a rendu standard). */
.prog-detail { margin-top: 12px; padding: 10px 12px; border-radius: var(--radius-md); background: var(--bg-hover); }
.prog-detail-tete { font-size: .76rem; font-weight: 600; color: var(--text-secondary); margin-bottom: 8px; }
.prog-seance {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; min-height: 34px; padding: 5px 0;
  border: none; background: none; text-align: left; cursor: pointer;
}
.prog-seance + .prog-seance { border-top: 1px solid var(--border-1); }
.prog-seance-nom {
  /* C67 : etait en 800 partout ailleurs, en 700 ici. La graisse fait partie de
     la regle au meme titre que la casse. */
  font-family: var(--font-display); font-size: .92rem; font-weight: 800;
  color: var(--text-primary); text-transform: uppercase;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.prog-seance-date { font-size: .72rem; color: var(--text-muted); flex-shrink: 0; }
.prog-reste { font-size: .72rem; color: var(--text-muted); padding-top: 7px; }

.prog-sep { height: 1px; background: var(--border-1); margin: 18px 0 14px; }
.prog-rep-titre { font-size: .8rem; font-weight: 600; color: var(--text-secondary); margin-bottom: 10px; }
/* 8px de haut : c'est une PROPORTION, pas une valeur a lire. Une barre epaisse
   attirerait l'oeil plus que la legende, qui porte les chiffres. */
.prog-rep-barre { display: flex; height: 8px; border-radius: 4px; overflow: hidden; margin-bottom: 12px; }
.prog-rep-barre > span { display: block; height: 100%; }
.prog-cats { display: flex; flex-direction: column; gap: 7px; }
.prog-cat { display: flex; align-items: center; gap: 8px; font-size: .78rem; }
.prog-cat-puce { width: 8px; height: 8px; border-radius: 2px; flex-shrink: 0; }
.prog-cat-nom { color: var(--text-primary); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Le compte reste en RETRAIT du nom : on parcourt la legende en cherchant une
   categorie, pas un pourcentage. Meme hierarchie que sur les cartes (C58). */
.prog-cat-val { color: var(--text-secondary); flex-shrink: 0; font-variant-numeric: tabular-nums; }

/* ══ C45/A3 — poids de corps ══════════════════════════════════════════════ */
.bw-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.bw-add-btn {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 7px 12px;
  /* 34px de haut : sous les 44px de la regle tactile, mais c'est une action
     SECONDAIRE posee a cote d'un titre — l'aligner sur un bouton primaire en
     ferait le point de gravite du bloc, devant la mesure elle-meme. */
  min-height: 34px;
  border-radius: 999px;
  background: var(--bg-hover);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  font-size: 0.78rem; font-weight: 600;
  transition: all .2s;
}
@media (hover: hover) { .bw-add-btn:hover { color: var(--text-primary); border-color: var(--border-bright); } }
.bw-ligne { display: flex; align-items: baseline; gap: 10px; margin-top: 10px; }
.bw-valeur {
  font-family: var(--font-display);   /* C44 : les gros chiffres parlent d'une seule police */
  font-size: 1.95rem; font-weight: 800; line-height: 1.1;
  letter-spacing: -.02em; color: var(--text-primary);
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
}
.bw-unite { font-size: .9rem; font-weight: 600; color: var(--text-secondary); margin-left: 3px; }
/* Neutre a dessein : ni vert ni rouge. Prendre du poids peut etre l'objectif. */
.bw-ecart { font-size: .8rem; font-weight: 600; color: var(--text-secondary); }
.bw-spark { margin-left: auto; align-self: center; overflow: visible; }
.bw-spark polyline { fill: none; stroke: var(--theme-color); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.bw-spark-tete { fill: var(--theme-color); }
.bw-date { font-size: .75rem; color: var(--text-muted); margin-top: 2px; }
.bw-vide { font-size: .82rem; color: var(--text-secondary); line-height: 1.4; margin-top: 8px; }

.pr-sens-toggle { margin-bottom: 6px; }
.pr-sens-aide {
  display: block;
  font-size: 0.76rem;
  line-height: 1.35;
  color: var(--text-muted);
}

.pr-suggest-wrap { position: relative; }
.pr-suggestions {
  position: absolute;
  top: calc(100% + 4px);
  left: 0; right: 0;
  background: var(--bg-surface);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius-md);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  box-shadow: 0 8px 32px rgba(0,0,0,0.5);
  z-index: 500;
  overflow: hidden;
  max-height: 220px;
  overflow-y: auto;
}
.pr-suggestion-item {
  padding: 11px 14px;
  font-size: 0.88rem;
  color: var(--text-primary);
  cursor: pointer;
  transition: background 0.15s;
}
@media (hover: hover) {
  .pr-suggestion-item:hover { background: var(--bg-hover); }
}
.pr-suggestion-item + .pr-suggestion-item { border-top: 1px solid var(--border); }

/* Ã¢â€â‚¬Ã¢â€â‚¬ History item Ã¢â‚¬â€ cliquable pour ÃƒÂ©dition Ã¢â€â‚¬Ã¢â€â‚¬ */
.pr-history-item { cursor: pointer; }

/* Ã¢â€â‚¬Ã¢â€â‚¬ Edit PR modal footer Ã¢â€â‚¬Ã¢â€â‚¬ */
.modal-footer--edit-pr {
  display: flex;
  gap: 8px;
  align-items: center;
}
.modal-footer--edit-pr .btn-pr-delete { margin-right: auto; }

.btn-danger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  background: rgba(255,71,87,0.1);
  border: 1px solid rgba(255,71,87,0.25);
  border-radius: var(--radius-md);
  color: var(--accent-red);
  font-weight: 600;
  font-size: 0.88rem;
  transition: all 0.2s;
}
.btn-danger svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }
@media (hover: hover) {
  .btn-danger:hover { background: rgba(255,71,87,0.2); border-color: rgba(255,71,87,0.45); }
}


.profile-legal-link {
  display: inline-flex;
  align-items: center;
  color: var(--text-dim);
  font-size: 0.875rem;
  text-decoration: none;
  padding: 8px 0;
  transition: color 0.15s;
}
@media (hover: hover) {
  .profile-legal-link:hover { color: var(--text-primary); }
}
@media (hover: hover) {
  .btn-block:hover { background: rgba(255,140,0,0.16); border-color: rgba(255,140,0,0.4); }
}

.blocked-list { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.blocked-empty { color: var(--text-faint, #6b7196); font-size: 0.875rem; margin: 0; }
.blocked-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--bg-card, #0a0b1a);
  border: 1px solid var(--border, #1c2036);
  border-radius: var(--radius-md, 10px);
  padding: 10px 14px;
}
.blocked-item-name { color: var(--text); font-size: 0.875rem; font-weight: 500; }
.btn-unblock {
  font-size: 0.8rem;
  padding: 5px 12px;
  border-radius: var(--radius-md, 10px);
  background: transparent;
  border: 1px solid var(--border, #1c2036);
  color: var(--text-dim);
  cursor: pointer;
  transition: all 0.15s;
}
@media (hover: hover) {
  .btn-unblock:hover { border-color: var(--accent-soft); color: var(--accent-soft); }
}

.inbox-mod-link {
  background: none; border: none; padding: 3px 0;
  font-size: 0.75rem; color: var(--text-faint, #6b7196);
  cursor: pointer; transition: color 0.15s; line-height: 1.4;
}
@media (hover: hover) {
  .inbox-block-link:hover { color: #c07800; }
}

/* â”€â”€ Report modal â”€â”€ */
.report-modal-subtitle { color: var(--text-dim); font-size: 0.875rem; margin-bottom: 12px; }
.report-reasons { display: flex; flex-direction: column; gap: 8px; }
.report-reason-btn {
  display: block; width: 100%;
  padding: 11px 14px;
  background: var(--bg-card, #0a0b1a);
  border: 1px solid var(--border, #1c2036);
  border-radius: var(--radius-md, 10px);
  color: var(--text); font-size: 0.9rem;
  text-align: left; cursor: pointer;
  transition: all 0.15s;
}
@media (hover: hover) {
  .report-reason-btn:hover { border-color: var(--accent-soft); color: var(--accent-soft); }
}

/* â”€â”€ Inbox report link â”€â”€ */
@media (hover: hover) {
  .inbox-report-link:hover { color: var(--accent-red, #ff4757); }
}

/* â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
   BOTTOM NAV
â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â• */
/* Clavier ouvert (plugin Keyboard, resize native) : masque la nav flottante — sinon elle
   remonterait au-dessus du clavier et chevaucherait le contenu. L'input du chat/sheet
   remonte seul via le resize natif. --kb-height dispo pour ajustements CSS futurs. */
body.keyboard-open .bottom-nav { display: none; }
.bottom-nav {
  position: fixed;
  bottom: 16px;
  left: 16px;
  right: 16px;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  background: rgba(10, 11, 26, 0.97);
  box-shadow: 0 0 0 1px rgba(255,255,255,0.04);
  border: 1px solid var(--border, #1c2036);
  border-radius: 20px;
  padding: 6px 4px;
  z-index: 400;
}
.bnav-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 8px 4px;
  background: none;
  border: none;
  color: var(--text-faint, #6b7196);
  cursor: pointer;
  border-radius: 14px;
  transition: color 0.18s;
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: 0.02em;
  touch-action: manipulation;
  position: relative;
}
.bnav-btn svg { width: 22px; height: 22px; }
.bnav-btn.active,
.bnav-btn.timer-active { color: var(--theme-color, #2f7bff); }
.bnav-btn.active::before,
.bnav-btn.timer-active::before {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 22px;
  height: 3px;
  border-radius: 2px;
  background: var(--theme-color);
  box-shadow: 0 0 8px var(--theme-color);
}
body.platform-android .bottom-nav { bottom: calc(16px + env(safe-area-inset-bottom, 0px)); }
.share-mode .bottom-nav { display: none; }
body.in-create .bottom-nav,
body.in-settings .bottom-nav,
body.in-support .bottom-nav,
body.in-account .bottom-nav,
body.in-detail .bottom-nav { display: none; }
/* â”€â”€ Bottom fade â€” dÃ©gradÃ© derriÃ¨re le menu flottant â”€â”€ */
.bottom-fade {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 130px;
  background: linear-gradient(to bottom, transparent, var(--bg-base));
  pointer-events: none;
  z-index: 380;
}
body.on-timer .bottom-fade,
body.in-create .bottom-fade,
body.in-settings .bottom-fade,
body.in-support .bottom-fade,
body.in-detail .bottom-fade { display: none; }

body.in-create .main-content,
body.in-settings .main-content { padding-bottom: max(24px, env(safe-area-inset-bottom, 24px)); }
.main-content { padding-bottom: 106px; }

/* Android gesture navigation : padding-bottom follows the bottom-nav elevated by safe-area.
   Excludes on-timer (padding:0), in-create/in-settings (dedicated safe-area rule above).
   env() = 0 in 3-button mode → identical to current behavior. iPhone/web unaffected (no platform-android). */
body.platform-android:not(.on-timer):not(.in-create):not(.in-settings) .main-content {
  padding-bottom: calc(106px + env(safe-area-inset-bottom, 0px));
}
@media (max-width: 768px) {
  body.platform-android:not(.on-timer):not(.in-create):not(.in-settings) .main-content {
    padding-bottom: calc(100px + env(safe-area-inset-bottom, 0px));
  }
}

/* === SETTINGS STICKY HEADER (view-header + tab-bar figés en haut) === */
.settings-sticky-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--bg-base);
  margin: 0 -40px;
  padding: var(--view-top) 40px 0; /* aligné sur --view-top — même hauteur écran que titres autres vues */
}
@media (max-width: 768px) {
  .settings-sticky-header {
    margin: 0 -20px;
    padding: var(--view-top) 20px 0;
  }
}
/* Neutralise le padding-top de .main-content en mode settings (évite le doublon safe-area) */
body.in-settings .main-content { padding-top: 0; }
body.in-account .main-content { padding-top: 0; }
/* ⚠ C122 — CETTE MARGE EST SUPPRIMEE, PAS DEPLACEE PAR HASARD.
   Elle datait de la barre d'onglets (« resserre titre ↔ onglets »), qui n'existe
   plus depuis C118. Une marge sur le dernier enfant d'un conteneur sans padding
   bas s'echappe par collapsing : elle ne fait pas grandir la boite, elle ouvre
   un couloir hors du fond ou le contenu defile a decouvert. L'espacement est
   repris a l'identique en `padding-bottom` sur l'en-tete lui-meme, plus bas. */

/* ⚠ C93 — LE TRAIT QUI DEPASSAIT DE L'EN-TETE COLLE, DANS « REGLAGES DU COMPTE ».
   MESURE : le trait sous le titre est en `bottom: -8px` sur 3px de haut, il
   descend donc a 100px ; l'en-tete colle s'arretait a 92. Onze pixels hors du
   fond opaque, et le contenu defilait dessous a decouvert — sur capture, le
   trait violet tranchait le mot « COMPTE ».

   POURQUOI SEULEMENT ICI. Reglages a une barre d'onglets APRES le titre : le
   trait y tombe au milieu de l'en-tete, par chance. Compte n'a rien apres le
   titre, donc l'en-tete s'arrete pile a sa base.

   ET POURQUOI CE N'ETAIT PAS UN SIMPLE OUBLI DE PADDING : `.view-header` porte
   `margin-bottom: 16px`, mais l'en-tete colle n'a aucun padding bas — la marge
   S'ECHAPPE par collapsing au lieu d'agrandir la boite. C'est exactement le
   defaut (a) de C63 : une marge est hors du fond opaque, donc le contenu s'y
   voit defiler. On convertit donc la marge en padding, ce qui referme la boite
   sans changer l'espacement visible. */
/* ⚠ C122 — LA MEME CORRECTION, MAIS SANS LE `body.in-account`.
   C93 avait ecrit noir sur blanc pourquoi Reglages s'en tirait : « Reglages a
   une barre d'onglets APRES le titre : le trait y tombe au milieu de l'en-tete,
   PAR CHANCE ». C118 a supprime la barre d'onglets. La chance a cesse, et ni
   C118 ni C119 n'ont relu l'hypothese que C93 avait pourtant nommee.

   Mesure dans Personnalisation, defilement en cours :

       fond de l'en-tete   0 .. 92 px
       trait sous le titre       ~92 px   (dernier pixel du fond)
       elementsFromPoint(200, 93..112)  ->  `.default-style-row` EN TETE DE PILE

   Les seize pixels sous le trait n'avaient aucun fond : le contenu y defilait a
   decouvert et venait TRANCHER le trait — sur capture, « 21 Pull Up » et
   « Repetitions » coupes en deux par la barre violette.

   La correction ne cible plus une vue : tant que la marge d'un `.view-header`
   s'echappe par collapsing, la boite reste ouverte, et la prochaine vue qui
   perdra son element apres le titre reproduira le defaut a l'identique. */
.settings-sticky-header { padding-bottom: 16px; }
.settings-sticky-header .view-header { margin-bottom: 0; }
/* Rangée 1 : bouton Retour seul, aligné à gauche */
.settings-sticky-header .settings-backrow {
  display: flex; align-items: center;
  margin-bottom: 10px;
}
/* La settings-tab-bar garde son margin-bottom: 20px pour séparer onglets ↔ panel */

.profile-nav-group { display: flex; flex-direction: column; gap: 6px; }
.profile-nav-row {
  display: flex; align-items: center; gap: 12px;
  width: 100%; padding: 13px 14px;
  background: var(--bg-card, #0a0b1a);
  border: 1px solid var(--border, #1c2036);
  border-radius: var(--radius-md, 10px);
  color: var(--text-primary, #e8f6ff); font-size: 0.9rem; font-weight: 500;
  cursor: pointer; text-align: left; transition: border-color 0.15s;
}
@media (hover: hover) {
  .profile-nav-row:hover { border-color: var(--border-bright); }
}
.profile-nav-row > svg:first-child { width: 18px; height: 18px; flex-shrink: 0; color: var(--text-secondary, #6a90b0); }
.profile-nav-row > span { flex: 1; }
/* C76 : le badge est un <span>, il heritait donc du `flex: 1` ci-dessus et
   prenait 115px de large pour afficher un chiffre — a egalite avec le libelle.
   Mesure faite par test-badges-notification, pas devinee. */
.profile-nav-row > span.inbox-badge { flex: 0 0 auto; }
.profile-nav-chevron { width: 14px; height: 14px; color: var(--text-secondary, #6a90b0); flex-shrink: 0; }

/* â”€â”€ 2B: sidebar hidden â€” revert these 3 lines to restore â”€â”€ */
#sidebar { display: none; }
.nav-overlay { display: none !important; }
#menuBtn { visibility: hidden; pointer-events: none; }

/* â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
   TIMER VIEW â€” #view-timer
â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â• */
#view-timer {
  min-height: calc(100vh - 106px);
  position: relative;
}
#view-timer.active {
  display: flex;
  align-items: stretch;
}

/* â”€â”€ Immersive Timer (body.on-timer) â”€â”€ */
body.on-timer .topbar          { display: none; }
body.on-timer .main-content    { padding: 0; overflow: hidden; }
body.on-timer #view-timer      { height: 100dvh; min-height: unset; overflow: hidden; }
body.on-timer                  { overflow: hidden; }

body.on-timer .sw-view-wrap {
  padding-top:    calc(var(--safe-top) + 28px);
  padding-bottom: calc(106px + env(safe-area-inset-bottom, 0px));
}
body.on-timer .sw-circle-area  { flex: 0 0 auto; width: min(360px, 88vw); height: min(360px, 88vw); }
body.on-timer .sw-mode-bar    { max-width: min(88vw, 340px); }
body.on-timer .sw-view-actions {
  position: absolute;
  left: 20px;
  right: 20px;
  bottom: calc(106px + env(safe-area-inset-bottom, 0px));
  max-width: 280px;
  margin: 0 auto;
  min-height: 0;
}
body.on-timer .sw-time-display { font-size: clamp(52px, 18vw, 88px); }
.sw-circle-grow                { display: none; }
body.on-timer .sw-circle-grow  { display: block; flex: 1; min-height: 8px; }

.sw-hint {
  position: absolute;
  top: calc(50% + 46px);
  left: 0; right: 0;
  text-align: center;
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  color: var(--text-2);
  opacity: 0;
  transition: opacity 0.25s;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}
#view-timer[data-state="idle"] .sw-hint { opacity: 1; }

.sw-watermark { display: none;
  position: absolute;
  top: calc(var(--safe-top) + 6px);
  left: 0; right: 0;
  text-align: center;
  font-size: var(--fs-xs);
  font-weight: 800;
  letter-spacing: 0.4em;
  color: var(--text-primary);
  opacity: 0.13;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}
body.on-timer #view-timer:not([data-state="idle"]) .sw-watermark { display: none; opacity: 0.45; }

/* Idle landscape overlay — base hidden */
.sw-landscape-idle-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 399;
  background: var(--bg-base);
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;
  color: var(--text-secondary);
  font-size: 0.82rem;
  text-align: center;
  padding: 24px;
}

.sw-view-wrap {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 8px 20px 16px;
  gap: 12px;
}

/* Mode bar */
.sw-mode-bar {
  display: flex;
  gap: 4px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 4px;
  width: 100%;
  max-width: 320px;
}
.sw-mode-btn {
  flex: 1;
  padding: 8px 4px;
  border: none;
  border-radius: 8px;
  background: none;
  color: var(--text-secondary);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  cursor: pointer;
  touch-action: manipulation;
  transition: background 0.15s, color 0.15s;
}
.sw-mode-btn.active { background: var(--theme-color); color: var(--bg-base); }
.sw-mode-btn:disabled              { cursor: default; }
.sw-mode-btn:disabled:not(.active) { opacity: 0.4; }

/* Circle */
.sw-circle-area {
  position: relative;
  width: min(320px, 88vw);
  height: min(320px, 88vw);
  flex-shrink: 0;
}
.sw-circle-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.sw-track {
  fill: none;
  /* C20 : etait rgba(255,255,255,.08) en dur — l'anneau etait totalement INVISIBLE
     en theme clair, il ne restait qu'un triangle flottant dans du vide. */
  stroke: var(--border-1);
  stroke-width: 10;
}
.sw-progress {
  fill: none;
  /* C20 : accent plat, theme-aware. */
  stroke: var(--accent);
  stroke-width: 12;
  stroke-linecap: round;
}
.sw-circle-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  touch-action: manipulation;
  user-select: none;
}
.sw-time-display {
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: clamp(52px, 16vw, 72px);
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1;
  letter-spacing: -0.02em;
  transition: font-size 0.25s, color 0.25s, transform 0.25s;
}
.sw-sub-display {
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 0.95rem;
  color: var(--text-secondary);
  letter-spacing: 0.02em;
  text-align: center;
}
.sw-rounds-display {
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', system-ui, sans-serif;
  font-size: 1.3rem;
  font-weight: 600;
  color: var(--theme-color);
  font-variant-numeric: tabular-nums;
  text-align: center;
}
.sw-timer-info {
  position: absolute;
  top: 100%;
  left: 0; right: 0;
  padding-top: 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  pointer-events: none;
}

/* Play icon (idle) */
.sw-play-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 60px;
  height: 68px;
  transform: translate(-22px, -34px);
  opacity: 0;
  transition: opacity 0.25s;
  pointer-events: none;
  overflow: visible;
}
.sw-play-icon polygon {
  fill: var(--theme-color);
  stroke: var(--theme-color);
  stroke-width: 12;
  stroke-linejoin: round;
}
#view-timer[data-state="idle"] .sw-play-icon { opacity: 1; }
#view-timer[data-state="idle"] .sw-time-display,
#view-timer[data-state="idle"] .sw-sub-display,
#view-timer[data-state="idle"] .sw-rounds-display { opacity: 0; }

/* === ÉTAT FINISHED (AMRAP fini, EMOM non-DeathBy fini) === */
/* Coche centrée dans le cercle via le flex centering du parent .sw-circle-overlay (comme .sw-time-display) */
.sw-finish-check {
  width: 96px; height: 96px;
  fill: none;
  stroke: #fff;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
  display: none;
}
/* Tracé animé du ✓ : stroke-dashoffset 30 → 0 (positif, masqué → tracé complet).
   Sens gauche→droite assuré par l'ordre des points du polyline ("4 12 9 17 20 6").
   forwards garantit que le tracé reste visible (dashoffset 0) après l'animation. */
.sw-finish-check polyline {
  stroke-dasharray: 30;
  stroke-dashoffset: 30;
}
#view-timer[data-state="finished"] .sw-finish-check polyline {
  animation: swCheckDraw 0.45s 0.05s ease-out forwards;
}
@keyframes swCheckDraw {
  from { stroke-dashoffset: 30; }
  to   { stroke-dashoffset: 0; }
}
/* Résumé + hint : dans .sw-timer-info (sous le cercle, naturellement empilés via flex column) */
.sw-finish-summary {
  display: none;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.sw-finish-time {
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 32px;
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1;
  letter-spacing: -0.02em;
}
.sw-finish-rounds {
  font-size: 15px;
  font-weight: 500;
  color: var(--text-secondary);
}
.sw-finish-label {
  margin-top: 4px;
  padding: 4px 12px;
  background: var(--theme-color);
  color: var(--bg-base);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.1em;
  border-radius: 100px;
}
.sw-finish-hint {
  margin-top: 10px;
  font-size: 12px;
  color: var(--text-muted);
  display: none;
}
/* En state finished : cacher play/pause/time/hint normal + rounds-display/sub-display */
#view-timer[data-state="finished"] .sw-play-icon,
#view-timer[data-state="finished"] .sw-pause-icon,
#view-timer[data-state="finished"] .sw-time-display,
#view-timer[data-state="finished"] .sw-hint,
#view-timer[data-state="finished"] .sw-sub-display,
#view-timer[data-state="finished"] .sw-rounds-display { display: none; }
/* Révéler coche (dans le cercle, centrée via flex parent) + summary + hint (sous le cercle dans .sw-timer-info) */
#view-timer[data-state="finished"] .sw-finish-check { display: block; }
#view-timer[data-state="finished"] .sw-finish-summary { display: flex; }
#view-timer[data-state="finished"] .sw-finish-hint { display: block; }
/* Ring plein en couleur thème (rempli via JS strokeDashoffset = 0) */
#view-timer[data-state="finished"] .sw-progress { stroke: var(--accent); }

/* Pause state (C) */
.sw-pause-icon {
  position: absolute;
  top: calc(50% - 58px);
  left: 0; right: 0;
  display: flex;
  gap: 12px;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.25s;
  pointer-events: none;
}
.sw-pause-icon::before,
.sw-pause-icon::after {
  content: '';
  width: 8px;
  height: 38px;
  border-radius: 3px;
  background: var(--theme-color);
}
#view-timer[data-state="paused"] .sw-pause-icon { opacity: 1; }
#view-timer[data-state="paused"] .sw-time-display {
  font-size: clamp(30px, 9vw, 42px);
  color: var(--text-secondary);
  transform: translateY(20px);
}

/* Config pickers */
.sw-view-config {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  max-width: 280px;
  height: 108px;
}
@media (hover: hover) {
  .sw-pick-btn:hover { border-color: var(--border-bright); }
}

/* â”€â”€ Drum picker â”€â”€ */
.sw-drum-wrap {
  position: relative;
  width: 100%;
}
.sw-drum-indicator {
  position: absolute;
  left: 12px;
  right: 12px;
  top: calc(50% - 18px);
  height: 36px;
  border-top: 1px solid var(--border-2);
  border-bottom: 1px solid var(--border-2);
  background: var(--surface-2);
  border-radius: 6px;
  pointer-events: none;
}
.sw-drum {
  width: 100%;
  height: 108px;
  /* C20 — CORRECTIF DE BUG, pas un changement de mise en page.
     .sw-drum-indicator suit la roue dans le DOM et est en absolu : il se peignait
     PAR-DESSUS. Avec --surface-2 opaque (#171B23), la bande masquait COMPLETEMENT
     la valeur selectionnee en theme sombre — on ne pouvait pas lire son propre
     reglage. Le theme clair y echappait par accident (son override repasse la
     bande en rgba translucide). Rien ne bouge : la bande redevient un fond. */
  position: relative;
  z-index: 1;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  mask-image: linear-gradient(
    to bottom, transparent 0%, black 28%, black 72%, transparent 100%
  );
  -webkit-mask-image: linear-gradient(
    to bottom, transparent 0%, black 28%, black 72%, transparent 100%
  );
}
.sw-drum::-webkit-scrollbar { display: none; }
.sw-drum-spacer { flex-shrink: 0; }
.sw-drum-item {
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  scroll-snap-align: center;
  font-size: 20px;
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
  opacity: 0.4;
  transition: font-size 0.15s ease, opacity 0.15s ease, color 0.15s ease;
  user-select: none;
  -webkit-user-select: none;
  cursor: default;
}
.sw-drum-item.sel {
  font-size: 28px;
  font-weight: 700;
  color: var(--theme-color);
  opacity: 1;
}
.sw-drum-unit {
  font-size: 0;
  font-weight: 400;
  margin-left: 0;
  white-space: nowrap;
  transition: font-size 0.15s ease, margin-left 0.15s ease;
}
.sw-drum-item.sel .sw-drum-unit {
  font-size: var(--fs-xs);
  margin-left: 5px;
}
.sw-emom-drums {
  display: flex;
  gap: 8px;
  width: 100%;
}
.sw-emom-drums .sw-drum-wrap { flex: 1; }
.sw-emom-drums .sw-drum { height: 80px; }
.sw-emom-config-wrap {
  width: 100%;
  display: flex;
  flex-direction: column;
}
.sw-emom-hint {
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  /* C20 : --text-faint n'existe pas, le repli #6b7196 en dur plafonnait a 3,64:1
     et n'etait pas theme-aware. */
  font-size: var(--fs-xs);
  color: var(--text-2);
  letter-spacing: 0.03em;
  opacity: 0;
  transition: opacity 0.2s;
}

/* Action buttons */
.sw-view-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 100%;
  max-width: 280px;
  min-height: 52px;
  justify-content: flex-start;
}
.sw-view-stop-btn {
  width: 100%;
  padding: 14px;
  border: 1px solid rgba(255,51,85,0.35);
  border-radius: 14px;
  background: transparent;
  color: var(--accent-red, #ff3355);
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  cursor: pointer;
  touch-action: manipulation;
  animation: sw-btn-in 0.2s ease;
}
@keyframes sw-btn-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.sw-view-round-btn {
  padding: 12px 28px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--bg-card);
  color: var(--text-primary);
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
  touch-action: manipulation;
}

/* ── Progress bar (portrait: hidden; landscape: visible) ── */
.sw-progress-bar-wrap {
  display: none;
  width: 100%;
  height: 4px;
  background: var(--surface-4);
  overflow: hidden;
  flex-shrink: 0;
}
.sw-progress-bar-fill {
  height: 100%;
  width: 0%;
  background: var(--theme-color);
}

/* ── Landscape timer layout ── */
@media (orientation: landscape) and (max-height: 500px) {

  /* Bottom nav masquée en cours — réapparaît en idle pour naviguer */
  body.on-timer:has(#view-timer:not([data-state="idle"])) .bottom-nav {
    display: none;
  }
  body.on-timer.timer-active .bottom-nav { display: none; }

  /* Toutes les règles paysage sont scoped à :not([data-state="idle"])
     → en idle, le layout portrait s'applique (blocage CSS, pas de lock natif) */

  /* .sw-view-wrap = contenant relatif pour les enfants absolus */
  body.on-timer #view-timer:not([data-state="idle"]) .sw-view-wrap {
    position: relative;
    padding: 0;
    gap: 0;
  }

  /* Masquer : cercle SVG, spacers flexbox, tabs, config drums */
  body.on-timer #view-timer:not([data-state="idle"]) .sw-circle-grow,
  body.on-timer #view-timer:not([data-state="idle"]) .sw-circle-svg,
  body.on-timer #view-timer:not([data-state="idle"]) .sw-mode-bar,
  body.on-timer #view-timer:not([data-state="idle"]) .sw-view-config {
    display: none;
  }

  /* .sw-circle-area couvre tout le viewport — devient la scène */
  body.on-timer #view-timer:not([data-state="idle"]) .sw-circle-area {
    position: absolute;
    inset: 0;
    width: auto;
    height: auto;
  }

  /* Grand chrono plein écran (running + countdown + paused)
     — paused conserve le grisé (color) + translateY(20px) hérités du portrait, seule la font-size augmente */
  body.on-timer #view-timer[data-state="running"] .sw-time-display,
  body.on-timer #view-timer[data-state="countdown"] .sw-time-display {
    font-size: clamp(120px, 48vh, 200px);
  }
  body.on-timer #view-timer[data-state="paused"] .sw-time-display {
    font-size: clamp(100px, 42vh, 170px);
  }

  /* Idle : overlay "tourne le téléphone" */
  #view-timer[data-state="idle"] .sw-landscape-idle-overlay {
    display: flex;
  }

  /* Bandeau haut : rounds (haut) puis temps total (bas), empilés sous le watermark NEXSET */
  body.on-timer #view-timer:not([data-state="idle"]) .sw-timer-info {
    top: calc(var(--safe-top) + 28px);
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    padding-top: 0;
  }
  body.on-timer #view-timer:not([data-state="idle"]) .sw-rounds-display { font-size: clamp(28px, 6vh, 50px); }
  body.on-timer #view-timer:not([data-state="idle"]) .sw-sub-display    { font-size: clamp(22px, 5vh, 40px); }

  /* STOP / +ROUND : absolu sur les côtés, pointer-events passthrough */
  body.on-timer #view-timer:not([data-state="idle"]) .sw-view-actions {
    inset: 0;
    width: auto;
    max-width: none;
    min-height: 0;
    pointer-events: none;
  }
  body.on-timer #view-timer:not([data-state="idle"]) .sw-view-stop-btn {
    position: absolute;
    left: 24px;
    top: 50%;
    transform: translateY(-50%);
    width: auto;
    pointer-events: all;
    animation: none;
  }
  body.on-timer #view-timer:not([data-state="idle"]) .sw-view-round-btn {
    position: absolute;
    right: 24px;
    top: 50%;
    transform: translateY(-50%);
    width: auto;
    pointer-events: all;
    animation: none;
  }

  /* Barre de progression en bas */
  body.on-timer #view-timer:not([data-state="idle"]) .sw-progress-bar-wrap {
    display: block;
    position: absolute;
    left: 0; right: 0;
    bottom: calc(env(safe-area-inset-bottom, 0px));
    height: 4px;
    border-radius: 0;
    z-index: 2;
  }

  /* ── ÉTAT FINISHED en paysage : layout horizontal cercle-coche à GAUCHE, résumé à DROITE ── */
  /* Cercle-overlay (contient la coche) : DOMINE la demi-scène gauche */
  body.on-timer #view-timer[data-state="finished"] .sw-circle-overlay {
    inset: auto;
    left: calc(env(safe-area-inset-left, 0px) + 4vw);
    top: 50%;
    transform: translateY(-50%);
    width: min(78vh, 340px);
    height: min(78vh, 340px);
  }
  /* Coche massive proportionnelle au cercle (~45% du diamètre) */
  body.on-timer #view-timer[data-state="finished"] .sw-finish-check {
    width: min(35vh, 155px);
    height: min(35vh, 155px);
  }
  /* Timer-info (résumé + hint) : demi-scène droite, gap 48px depuis le cercle */
  body.on-timer #view-timer[data-state="finished"] .sw-timer-info {
    top: 50%;
    left: calc(env(safe-area-inset-left, 0px) + 4vw + min(78vh, 340px) + 48px);
    right: calc(env(safe-area-inset-right, 0px) + 4vw);
    bottom: auto;
    transform: translateY(-50%);
    padding-top: 0;
    align-items: flex-start;
    gap: 12px;
    text-align: left;
  }
  /* Résumé : tailles généreuses — l'écran doit paraître PLEIN */
  body.on-timer #view-timer[data-state="finished"] .sw-finish-time    { font-size: clamp(48px, 9vh, 72px); font-weight: 700; }
  body.on-timer #view-timer[data-state="finished"] .sw-finish-rounds  { font-size: clamp(20px, 4vh, 28px); }
  body.on-timer #view-timer[data-state="finished"] .sw-finish-label   { font-size: 0.95rem; padding: 7px 20px; margin-top: 8px; letter-spacing: 0.12em; }
  body.on-timer #view-timer[data-state="finished"] .sw-finish-hint    { font-size: 0.8rem; margin-top: 18px; }
  /* STOP / +ROUND : masqués en finished (évite collision avec le nouveau layout latéral) */
  body.on-timer #view-timer[data-state="finished"] .sw-view-stop-btn,
  body.on-timer #view-timer[data-state="finished"] .sw-view-round-btn { display: none; }
}




/* ══════════════════════════════════════════
   Galerie — menu flottant transparent + fondus scroll + header non-fixe
   Appliqué en dur sur la galerie (body.in-gallery). Cards V1 inchangées.
   ══════════════════════════════════════════ */

/* Header galerie : non-fixe, transparent (le voile passe devant), sans liseré néon */
body.in-gallery .gallery-header { position: relative; z-index: 387; background: transparent; }
body.in-gallery .gallery-header::after { display: none; }
/* C61 — LA BARRE DE RECHERCHE REDEVIENT VISIBLE EN GALERIE.
   Elle etait masquee ici (« temporairement », disait le commentaire) ET ignoree
   par renderCards, qui partait de `trainings.slice()` sans lire `currentSearch`.
   Les deux moities du meme oubli : le champ existait dans index.html, son
   ecouteur marchait, getFilteredTrainings() savait filtrer — et rien de tout ca
   n'arrivait a l'ecran. Sur un compte a 40 trainings, chercher par nom est le
   chemin le plus court qui existe ; c'est aussi ce qui rend acceptable d'avoir
   retire la rangee de pastilles. Le filtrage cote JS est retabli dans
   renderCards(), donc la barre a de nouveau un effet. */
/* Neutralise le fond opaque V1 (::before height:100vh; bottom:100%) qui défilait avec le header
   depuis qu'il est en position:relative et masquait des cards */
body.in-gallery .gallery-header::before { display: none; }

/* Bottom-nav très transparente (le contenu défile derrière, blur garde la lisibilité).
   Factorisée sur .v2-ambient — les vues qui la masquent (create/settings/support/detail
   et timer-active) conservent leur display:none par cascade (specificité identique, ordre après). */
body.v2-ambient .bottom-nav {
  /* tokenise a la source : une regle scopee body.* bat toujours une regle
     de composant, c'est ce qui cassait le theme clair partout. */
  background: var(--scrim-nav);
  backdrop-filter: blur(24px) saturate(1.4);
  -webkit-backdrop-filter: blur(24px) saturate(1.4);
  border: 1px solid var(--border-0);
  box-shadow: var(--sh-2);
}

/* Voile de fondu haut — élément dédié .gallery-topfade DEVANT le header (z 388 > header 387).
   Factorisé sur .v2-ambient : toute vue V2 hérite du voile ; l'opacité passe à 1 au scroll (gal-scrolled). */
.gallery-topfade { display: none; }
body.v2-ambient .gallery-topfade {
  display: block;
  position: fixed;
  top: 0; left: 0; right: 0;
  height: calc(var(--safe-top) + 34px);
  z-index: 388;
  pointer-events: none;
  opacity: 0;
  transition: opacity .25s ease;
  /* C20 : ce voile fondait vers du #000 pur, sans aucun override de theme — en
     clair, une bande NOIRE en haut d'une page a #F1F4F9. L'audit de juillet l'avait
     signale (P0-3) et ca n'avait jamais ete corrige ; aucun scanner de contraste ne
     pouvait le voir, puisqu'il ne porte pas de texte.
     Ce que font les apps systeme iOS et Apple Music : le voile ne fond ni vers du
     noir ni vers du blanc, il fond vers LA COULEUR DE FOND DE LA PAGE. C'est ce qui
     le rend juste dans les deux themes sans le moindre override. */
  background: linear-gradient(to bottom,
    var(--bg-base) 0%,
    var(--bg-base) 45%,
    color-mix(in srgb, var(--bg-base) 50%, transparent) 68%,
    transparent 100%);
}
body.v2-ambient.gal-scrolled .gallery-topfade { opacity: 1; }

/* ── Pull-to-refresh — indicateur DERRIÈRE .main-content, révélé par le translate ── */
body.in-gallery .ptr-indicator {
  position: fixed;
  top: calc(var(--safe-top) + 14px);
  left: 50%; transform: translateX(-50%);
  z-index: 5;                     /* SOUS .main-content (z-index:10 en galerie) */
  opacity: 0; transition: opacity .2s ease;
  pointer-events: none;
}
body.in-gallery.ptr-armed .ptr-indicator,
body.in-gallery.gal-refreshing .ptr-indicator { opacity: 1; }
/* Spinner simple — même style que .splash-ring / .ai-loader-ring (cohérence globale) */
body.in-gallery .ptr-spin {
  width: 26px; height: 26px; border-radius: 50%;
  border: 2.5px solid rgba(255,255,255,0.08);
  border-top-color: var(--glow-primary);
  filter: brightness(2.5);
  animation: splashSpin 0.75s linear infinite;
}

/* Fond opaque cohérent pour toutes les vues V2 — position/z-index couvre PTR et ambient orbs.
   Factorisé sur .v2-ambient : plus jamais besoin d'ajouter une vue à cette rallonge. */
body.v2-ambient .main-content { background: var(--bg-base); position: relative; z-index: 10; }

/* Voile bas supprimé : cards restent visibles et se floutent dans la nav (verre dépoli pur) */

/* ══════════════════════════════════════════
   RANGÉES PAR CATÉGORIE — étape 1 (peek carrousels horizontaux)
   ══════════════════════════════════════════ */

/* Chips de filtre masquées en galerie (le regroupement par catégorie remplace le filtrage) */
body.in-gallery .gallery-chips-row { display: none; }

/* Masque le glow d'ambiance V1 (body::before ambientFloat) qui trainait comme une bande bleue à gauche sur le fond noir */
body.in-gallery::before { display: none; }
/* Masque l'indicateur de swipe sidebar (barre bleue verticale gauche, media <768px) sur TOUTES les vues V2.
   L'écran auth vit hors .main-content (index.html:33) donc n'est pas concerné. */
body.v2-ambient .main-content::before { display: none; }

body.in-gallery .cat-rows { display: block; }
body.in-gallery .cat-section { margin-bottom: 4px; }

/* Le titre de section, tel qu'il etait. Il sert AUSSI au bloc du jour
   (« Seance du jour », « Tes favoris »), qui n'est pas un bouton — d'ou le fait
   que rien de ce qui suit n'a bouge ici. */
body.in-gallery .cat-title {
  font-size: 16px; font-weight: 700; color: #fff; letter-spacing: -.01em;
  padding: 0; margin: 10px 0 9px;
}

/* ══ C61 — L'EN-TETE DE CATEGORIE EST DEVENU UN BOUTON ════════════════════
   C'etait un <div> decoratif ; c'est desormais la SEULE commande de filtrage de
   la galerie (la rangee de pastilles a ete supprimee, cf. index.html). D'ou :
     - 44px de hauteur minimale, la cible tactile ;
     - `position: sticky`, pour qu'on sache toujours dans quelle categorie on se
       trouve pendant le defilement — c'est ce qui remplace le repere que
       donnaient les rangees separees ;
     - un chevron, parce qu'un titre qui ne ressemble pas a un bouton ne se fait
       pas taper. C'est exactement l'erreur corrigee en C7 sur « Ajouter un
       objectif ».

   ⚠ POURQUOI UNE CLASSE A PART, ET NON `.cat-section > .cat-title`.
   `.cat-title` habille aussi le titre du bloc du jour, qui doit rester un titre
   simple : il ne faut donc toucher que l'en-tete de categorie. Le reflexe serait
   de scoper avec `body.in-gallery .cat-section > .cat-title` — c'est un piege.
   Cette forme pese (0,3,2) et passerait DEVANT
   `[data-theme="light"] body.in-gallery .cat-title` (0,3,1), donc elle
   imposerait `color: #fff` en theme clair : titre blanc sur fond blanc. C'est
   exactement le bug de l'etoile favori de C43, dans l'autre sens.
   La classe supplementaire ne declare donc QUE ce qui est nouveau et ne
   redeclare AUCUNE propriete deja portee par `.cat-title` — en particulier pas
   `color`. Aucune egalite a arbitrer, aucun ordre de fichier a respecter. */
body.in-gallery .cat-title-btn {
  display: flex; align-items: center; gap: 8px;
  /* ⚠ LE FOND DOIT ALLER D'UN BORD A L'AUTRE DE L'ECRAN.
     Mesure : l'en-tete allait de x=20 a x=382 sur un ecran de 402 — il s'arretait
     aux marges de .main-content. Les 20px de gouttiere de chaque cote n'etaient
     couverts par RIEN, et tout ce qui y est peint (ombres de cartes, debords)
     defilait a decouvert le long d'un en-tete cense masquer.
     On deborde donc des marges par une marge negative, et on les recompose en
     padding pour que le CONTENU reste aligne sur les cartes.
     ⚠ LA LARGEUR DOIT ETRE EXPLICITE, et c'est le piege de ce correctif.
     `width: auto` semble logique — et il donne 185px au lieu de 402 : un
     <button> se dimensionne en shrink-to-fit sur son contenu, meme en
     `display: flex`. `width: 100%` ne marche pas davantage : il resout sur la
     largeur de contenu du parent (362px), et les marges negatives ne font que
     DEPLACER cette boite, elles ne l'elargissent pas — l'en-tete couvrirait
     -20..342 au lieu de 0..402.
     Il faut donc redonner explicitement les deux marges de page :
     largeur = 100% + 2 x padding, decalage = -padding. */
  width: calc(100% + 80px); margin-left: -40px;
  /* ⚠ DU PADDING, PAS DES MARGES — sinon le contenu defile DANS la marge.
     La premiere version ecrivait `margin: 6px 0 8px`. Une marge est hors de la
     boite, donc hors du fond opaque : les 8px sous l'en-tete restaient
     transparents et on voyait le haut des cartes y passer, coupe net, pendant
     le defilement. Vu sur capture apres signalement — invisible a l'arret,
     puisque le probleme n'existe QUE pendant le scroll.
     En padding, le fond couvre toute la bande et rien ne traverse. */
  min-height: 44px; margin-top: 0; margin-bottom: 0; padding: 8px 40px 10px;
  /* --bg-base, PAS une valeur en dur ni un fond transparent : un en-tete colle
     et transparent laisse defiler les cartes DERRIERE le texte. Le jeton suit
     les deux themes (#0C0E13 sombre, #F1F4F9 clair), donc l'en-tete reste opaque
     dans les deux, sans regle de theme supplementaire. */
  background: var(--bg-base); border: none; text-align: left; cursor: pointer;
  /* Le header de la galerie n'est PAS colle (cf. `body.in-gallery
     .gallery-header { position: relative }` plus haut) : il defile et libere le
     haut. L'en-tete se colle donc a 0 — decale du safe-area, sinon il passe sous
     l'encoche en mode plein ecran Capacitor. */
  position: sticky; top: var(--safe-top); z-index: 3;
}
/* Le padding de .main-content vaut 40px en large et 20px sous 768px : le debord
   doit suivre, sinon l'en-tete deborde de l'ecran sur mobile. */
@media (max-width: 768px) {
  body.in-gallery .cat-title-btn {
    width: calc(100% + 40px); margin-left: -20px;
    padding-left: 20px; padding-right: 20px;
  }
}

/* ⚠ C64 — LA BANDE DU SAFE-AREA, INVISIBLE EN HEADLESS.
   L'en-tete se colle a `top: env(safe-area-inset-top)`. Sur iPhone cette valeur
   vaut ~59px : les 59 premiers pixels de l'ecran ne sont donc couverts par
   RIEN, et les cartes y defilent a decouvert, juste sous l'heure et la batterie.
   Vu sur capture d'appareil, pas au banc — en headless env() vaut 0, la bande
   a une hauteur nulle et le defaut n'existe tout simplement pas. C'est la meme
   lecon qu'en C20 sur les masques WebKit : certains defauts n'apparaissent que
   sur le vrai appareil.
   On pose donc un cache FIXE de la hauteur exacte du safe-area. z-index 4, soit
   juste au-dessus des en-tetes colles (3) : ils ne se chevauchent pas — l'un
   finit ou l'autre commence — mais si un jour ils se croisent, c'est le cache
   qui doit gagner, jamais un titre a moitie sous l'encoche.
   Non affiche quand la hauteur est nulle : inutile de peindre 0px.

   ⚠ C89 — LE DETAIL AUSSI, ET IL L'AVAIT PAR ACCIDENT.
   `openDetail` court-circuitait `showView` et laissait `in-gallery` sur le
   <body> : ce cache s'appliquait donc au Detail sans que personne l'ait decide,
   et c'est la seule raison pour laquelle la bande du safe-area y etait couverte.
   Mesure a l'appui (43 proprietes fuyaient, une seule visible : celle-ci).
   Retirer `in-gallery` sans ajouter `in-detail` ICI aurait donc REGRESSE sur
   iPhone, alors que le banc serait reste vert — en headless la bande fait 0px.
   Les deux corrections sont indissociables, ne pas separer.

   ⚠ C93 — ET TOUTES LES AUTRES VUES, POUR LA MEME RAISON.
   Ce cache n'existait que pour la galerie, puis pour le Detail. Les neuf autres
   vues V2 — Prepa, Profil, Compte, Reglages, Records, Timer, Creation, Support
   — laissaient donc leur contenu defiler a decouvert sous l'encoche, exactement
   le defaut que C64 a corrige. Personne ne l'avait vu parce que personne ne
   pouvait le voir : en headless env() vaut 0 et la bande a une hauteur nulle.
   `v2-ambient` est la classe qui porte deja le fond noir et le voile du haut :
   une vue qui a l'ambiance V2 a, par construction, besoin du cache. */
body.v2-ambient .main-content::after {
  content: ''; position: fixed; top: 0; left: 0; right: 0;
  height: var(--safe-top);
  background: var(--bg-base); pointer-events: none; z-index: 4;
  /* C108 — le contre-décalage du pull-to-refresh. `.main-content` est translaté
     pendant le tirage, ce qui en fait le bloc conteneur de ses descendants
     `position: fixed` : ce cache descendait donc AVEC le contenu au lieu de
     rester sous l'encoche. Le raisonnement complet est dans `setPull`
     (script.js), qui écrit `--ptr-dist`. Vaut 0 hors tirage : aucune incidence
     sur les dix autres vues V2. */
  transform: translateY(calc(-1 * var(--ptr-dist, 0px)));
}

/* ⚠ LE FONDU SOUS L'EN-TETE, ET POURQUOI IL N'EST PAS DECORATIF.
   Meme avec un fond opaque pleine largeur, une carte qui glisse dessous se fait
   TRANCHER net : on lit la moitie basse de « PUSH POWER », ce qui se remarque
   plus qu'un titre entier. Douze pixels de degrade font disparaitre le texte au
   lieu de le couper — c'est ce que fait iOS sous ses barres.
   Le degrade part de --bg-base et va vers --bg-base-0, QUI EST LA MEME COULEUR
   EN ALPHA 0 et non `transparent` : plusieurs moteurs interpolent `transparent`
   en passant par du noir, ce qui produit un halo sombre en theme clair. Deux
   jetons, un par theme, comme pour les barres de C5.

   ⚠ ET IL NE DOIT EXISTER QUE QUAND L'EN-TETE EST COLLE. Rendu en permanence,
   il posait une bande sombre au-dessus des premieres cartes de chaque
   categorie, alors qu'il n'y a rien a faire disparaitre a cet endroit-la —
   signale a l'usage comme « un fondu noir au-dessus des cards ». CSS ne sait
   pas exprimer « je suis colle » ; c'est un ecouteur de defilement qui pose la
   classe (cf. majEnTetesCollees dans script.js). */
body.in-gallery .cat-title-btn::after {
  content: ''; position: absolute; left: 0; right: 0; top: 100%;
  height: 12px; pointer-events: none;
  background: linear-gradient(to bottom, var(--bg-base), var(--bg-base-0));
  opacity: 0; transition: opacity .12s ease;
}
body.in-gallery .cat-title-btn.collee::after { opacity: 1; }

/* ══════════════════════════════════════════════════════════════════════════
   C89 — L'EN-TETE COLLE, CONVENTION PARTAGEE

   C63 et C64 ont paye la mise au point de cette barre sur les en-tetes de
   categorie de la galerie : fond opaque de bord a bord, du padding et jamais de
   marge a l'interieur, un fondu de 12px qui ne s'allume QUE quand la barre est
   reellement collee, et un cache pour la bande du safe-area. Le Detail avait
   exactement le meme besoin et rien de tout ca : en descendant dans un training
   on perdait le nom, le bouton retour, et le programme se faisait trancher net
   au bord haut.

   CE BLOC N'EST DONC SCOPE A AUCUNE VUE. Les regles de la galerie ci-dessus
   restent prefixees `body.in-gallery` parce qu'elles habillent un bouton de
   categorie ; celles-ci decrivent la BARRE elle-meme, et Reglages et Compte
   doivent pouvoir l'adopter en ajoutant une classe, sans recopier une valeur.
   Ce sont les memes 12px, le meme --bg-base-0, la meme classe `.collee`.

   ⚠ SPECIFICITE. `.view-header` (0,1,0) declare `flex-wrap: wrap` et `gap: 16px`
   bien plus haut dans le fichier ; `.entete-colle` a la MEME specificite et ne
   gagne que parce qu'elle est declaree apres. Un deplacement de ce bloc vers le
   haut du fichier casserait la mise en page sans casser aucun test de couleur.
   ══════════════════════════════════════════════════════════════════════════ */
.entete-colle {
  position: sticky; top: var(--safe-top); z-index: 3;
  background: var(--bg-base);
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: nowrap; gap: 12px;
  /* Meme calcul qu'en C63 : une largeur de 100% s'arrete aux marges de
     .main-content et laisse les gouttieres a decouvert, et des marges negatives
     seules DEPLACENT la boite sans l'elargir. Largeur = 100% + 2 x padding. */
  width: calc(100% + 80px); margin-left: -40px;
  /* Du padding a l'interieur, jamais de marge : une marge est hors du fond
     opaque, et le contenu s'y voit defiler. La marge du BAS, elle, est voulue :
     c'est l'espace avant le hero, et le fondu ci-dessous l'occupe.

     ⚠ C104 — LE PLANCHER EST `min-height`, PAS LE PADDING. 3px + 38px + 3px
     = 44, et 44 est aussi la hauteur de la zone tactile que C92 pose en
     `::after` sur `.hdr-btn`, centree sur une pastille de 38 : a 44px de barre
     la zone la remplit AU PIXEL PRES. Si la barre descendait sous 44, ces zones
     DEBORDERAIENT hors du fond opaque sur le bord haut de la carte — qui est
     cliquable (titre editable en ligne, etoile favori) : la barre volerait ses
     taps sans que rien ne se voie.
     Mais c'est `min-height: 44px` qui l'empeche, PAS la valeur du padding :
     verifie en regression, un padding ramene a 1px laisse la barre a 44 et la
     garde reste verte. Le defaut n'apparait qu'en abaissant `min-height`. Donc
     le padding se regle librement ; `min-height` ne descend jamais sous la
     hauteur de la zone tactile qu'il contient. C'est cette formulation-la que
     le banc controle, et pas le chiffre 3.

     La marge passe de 24px a 14px : le fondu fait 12px de haut, donc a 24 il
     restait 12px que rien n'occupait — le commentaire ci-dessus etait faux de
     moitie depuis C89. A 14 il redevient vrai. */
  min-height: 44px; margin-top: 0; margin-bottom: 14px;
  padding: 3px 40px;
}
@media (max-width: 768px) {
  .entete-colle { width: calc(100% + 40px); margin-left: -20px;
                  padding-left: 20px; padding-right: 20px; }
}
/* Le fondu. Meme forme et meme raison qu'en C63 : un fond opaque suffit a
   masquer mais il TRANCHE, et le degrade fait disparaitre au lieu de couper.
   Il va vers --bg-base-0, la meme couleur en alpha 0, surtout pas `transparent`
   que plusieurs moteurs interpolent en passant par du noir. */
.entete-colle::after {
  content: ''; position: absolute; left: 0; right: 0; top: 100%;
  height: 12px; pointer-events: none;
  background: linear-gradient(to bottom, var(--bg-base), var(--bg-base-0));
  opacity: 0; transition: opacity .12s ease;
}
.entete-colle.collee::after { opacity: 1; }

/* Le titre qui apparait dans la barre. Il ne remplace pas le titre du hero : il
   prend le relais quand celui-ci sort de l'ecran, comme le grand titre d'iOS.
   Il garde sa place (flex:1) meme invisible, sinon les deux pastilles se
   deplaceraient au moment ou il s'allume — un mouvement pour rien. */
.entete-colle-titre {
  flex: 1 1 auto; min-width: 0;
  font-family: 'Barlow Condensed', Inter, system-ui, sans-serif;
  font-size: 19px; font-weight: 700; letter-spacing: .01em;
  /* Capitales : c'est la regle de l'app pour un nom de training, deja tenue par
     .detail-title et .cc-name. Le nom est stocke tel que Chris l'a saisi. */
  text-transform: uppercase;
  color: var(--text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  opacity: 0; transition: opacity .18s ease;
  pointer-events: none;
}
.entete-colle.collee .entete-colle-titre { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .entete-colle::after, .entete-colle-titre { transition: none; }
}

/* Le retour en pastille. Il porte deja .btn-icon, donc il herite de la taille,
   du verre de C66 et de la zone tactile etendue des deux autres pastilles : la
   barre se lit comme trois pastilles et un titre. Il ne CHANGE JAMAIS de taille
   au defilement — une cible qui retrecit sous le doigt etait la seule objection
   serieuse a cette forme. Le mot « Retour » part, l'aria-label le remplace. */
.btn-icon.btn-retour { flex-shrink: 0; }
.btn-icon.btn-retour svg {
  width: 19px; height: 19px;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}

/* Le jalon de couleur : il redit la categorie sans ecrire son nom deux fois, et
   c'est le seul endroit de la galerie ou la couleur de categorie porte encore
   une information depuis que le halo des cartes a ete retire. */
body.in-gallery .cat-title-jalon {
  width: 3px; height: 15px; border-radius: 2px; flex-shrink: 0;
  background: rgb(var(--cat-rgb));
}
body.in-gallery .cat-title-txt { text-transform: none; }
body.in-gallery .cat-title-ico {
  width: 17px; height: 17px; flex-shrink: 0;
  fill: none; stroke: var(--text-3); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
/* Le chevron « ouvrir » est pousse a droite ; celui du retour reste a gauche,
   la ou l'oeil cherche une fleche arriere. */
body.in-gallery .cat-title-ico-fin { margin-left: auto; }
@media (hover: hover) {
  body.in-gallery .cat-title:hover .cat-title-ico { stroke: var(--text-primary); }
}
/* ⚠ ANNULE LE SCALE GLOBAL. `button:active:not(.btn-primary)` applique un
   `transform: scale(0.94)` a TOUS les boutons de l'app. La regle a du sens sur
   une pastille ou une icone ; sur un en-tete pleine largeur, elle fait
   visiblement RETRECIR PUIS GRANDIR le titre a chaque tap — signale a l'usage,
   et ca ressemble a un bug d'affichage plus qu'a un retour tactile.
   Le retour reste, porte par l'opacite, qui ne deplace rien. */
body.in-gallery .cat-title-btn:active { transform: none; opacity: .6; }

/* ══ C61 — LA GRILLE REMPLACE LE CARROUSEL ════════════════════════════════
   Deux colonnes, pas trois ni quatre : a 402px de large, 2 colonnes donnent
   177px par carte (dont 143px utiles), ce qui est le minimum pour que les trois
   colonnes de stats tiennent ET qu'un nom au maximum de caracteres reste sur une
   seule ligne. A 4 colonnes on tombe a 88px, et les deux se cassent.
   `auto-fill` avec un minimum plutot qu'un `repeat(2, 1fr)` fige : sur une
   tablette ou en paysage la grille se remplit d'elle-meme au lieu d'etirer deux
   cartes sur toute la largeur.

   LE MINIMUM VAUT 150px, ET IL EST CALCULE — pas choisi a l'oeil.
   La zone de contenu vaut la largeur d'ecran moins 36px de marges.
     iPhone 15 (402px) : 366 dispo. 2 colonnes demandent 2x150+12 = 312 -> OK.
                         3 colonnes demanderaient 474 -> refuse, on reste a 2.
     iPhone SE (375px) : 339 dispo. 312 <= 339 -> 2 colonnes tiennent encore.
   A 160px on serait passe a UNE seule colonne sur SE (332 > 339 de justesse
   selon les marges), ce qui aurait donne une galerie a une carte par rangee sur
   les petits ecrans sans que rien ne le signale. */
body.in-gallery .cat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
  margin: 0 0 18px;
  padding: 0;
}

/* ══ C65 — l'apercu du programme dans la feuille d'appui long ═════════════
   L'appui long ouvrait une liste d'actions dont la premiere etait
   « Programmer » : on demandait de decider avant d'avoir vu. L'apercu repond
   d'abord a « c'est quoi celui-la », les actions viennent apres.
   Un <button> : l'apercu est tapable et ouvre le training, comme sur iOS. */
.quick-apercu {
  display: block; width: 100%; position: relative;
  margin: 4px 0 14px; padding: 0;
  border: 1px solid var(--border-1); border-radius: var(--radius-md);
  background: var(--surface-1); text-align: left; cursor: pointer;
  overflow: hidden;
}
/* 34vh : assez pour lire l'entree d'un programme, jamais assez pour que la
   feuille mange l'ecran. Au-dela on defile DANS l'apercu — et ce defilement ne
   doit pas se propager a la feuille, d'ou overscroll-behavior: contain. */
.quick-apercu-corps {
  max-height: 34vh;
  /* ⚠ LES DEUX AXES, ET LE SECOND N'EST PAS THEORIQUE.
     Seul `overflow-y` etait gere. Vu sur l'appareil : un programme ecrit sur UNE
     SEULE LIGNE avec des separateurs « / » — « 5 Pull Up / 1 Muscle Up / 5 Bar
     Dips / 2 Muscle Up » — depasse la largeur de la carte et se faisait couper
     NET a droite, sans le moindre signe qu'il manquait quelque chose.
     On ne peut pas faire retourner la ligne a la ligne : la geometrie du
     programme appartient au renderer contractuel. On rend donc le debordement
     ATTEIGNABLE plutot que masque. */
  overflow: auto; overscroll-behavior: contain;
  padding: 12px 14px 26px;
  -webkit-overflow-scrolling: touch;
}
.quick-apercu-corps::-webkit-scrollbar { display: none; }
/* Le libelle d'action, pose sur un fondu : il dit que l'apercu est une PORTE,
   pas une image. Sans lui rien n'indique qu'on peut le taper — c'est l'erreur
   corrigee en C7 sur « Ajouter un objectif ». */
.quick-apercu-ouvrir {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 14px 14px 9px;
  font-size: .78rem; font-weight: 600; color: var(--theme-color); text-align: right;
  background: linear-gradient(to bottom, var(--bg-base-0), var(--surface-1) 62%);
  pointer-events: none;
}
@media (hover: hover) { .quick-apercu:hover { border-color: var(--border-2); } }
/* Meme raison que sur l'en-tete de categorie : le scale global de
   `button:active` sur un bloc de cette taille se lit comme un defaut. */
.quick-apercu:active { transform: none; opacity: .8; }

/* C105 — le « N autres en X » de C62 est retire : il doublait le titre de
   categorie, qui fait la meme action, porte deja son chevron et son compte, et
   ne quitte jamais l'ecran (une section fait 374px sur 874, apercu coupe a 4).
   Le raisonnement complet est dans script.js, la ou le bouton etait construit.
   Ses regles sont supprimees plutot que neutralisees : du CSS qui ne s'applique
   a rien est un piege pour la prochaine lecture. */

/* C67 — la carte seule occupe les deux colonnes. `grid-template-columns: 1fr`
   plutot qu'un `grid-column: 1 / -1` sur l'enfant : la grille n'a alors qu'une
   colonne, la carte la remplit naturellement, et on n'a pas a se soucier de la
   ligne implicite qu'un span cree. */
body.in-gallery .cat-grid-solo { grid-template-columns: 1fr; }
/* Elargir la carte ne suffisait pas : le contenu restait empile en colonne et
   laissait la moitie droite vide, ce qui se lisait comme une carte ratee plutot
   que comme une banniere. On repose donc le MEME contenu en grille, stats a
   droite du nom — sans toucher au DOM, uniquement par zones nommees. */
body.in-gallery .cat-grid-solo .cat-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "tete  tete"
                       "nom   stats"
                       "vu    stats";
  align-items: center;
  column-gap: 16px;
}
body.in-gallery .cat-grid-solo .cc-head  { grid-area: tete; }
body.in-gallery .cat-grid-solo .cc-name  { grid-area: nom; margin-bottom: 2px; }
/* C114 : la marge remonte a 0 ici — en `grid-area` l'espacement se comporte
   autrement qu'en flux, 4px d'ecart avec la grille a deux colonnes. Les deux
   valeurs visent le meme resultat a l'ecran (4,8px d'encre), pas la meme
   declaration. Voir le bloc `.cc-last` plus bas pour le tableau de mesures. */
body.in-gallery .cat-grid-solo .cc-last  { grid-area: vu; margin-top: 0; }
/* Les stats gardent leur ligne de base commune ; c'est leur ALIGNEMENT qui
   change, pas leur typographie — la hierarchie posee en C58 reste intacte. */
body.in-gallery .cat-grid-solo .cc-stats { grid-area: stats; align-self: center; }

/* L'etat vide propre a la galerie. #emptyState ne convient pas ici : il propose
   « Creer un training », ce qui n'est pas la reponse a « rien trouve ». */
body.in-gallery .gal-vide { padding: 34px 8px; text-align: center; }
body.in-gallery .gal-vide-txt { color: var(--text-secondary); font-size: .92rem; margin: 0 0 14px; }
body.in-gallery .gal-vide-btn {
  padding: 9px 16px; min-height: 40px; border-radius: 999px;
  background: var(--bg-hover); border: 1px solid var(--border);
  color: var(--text-primary); font-size: .85rem; font-weight: 600;
}

body.in-gallery .cat-card {
  /* HISTORIQUE DE LA LARGEUR, parce que la contrainte n'a jamais change : un nom
     au MAXIMUM de caracteres (le champ est en maxlength=18, soit 127px mesures)
     doit tenir sur UNE ligne.
       78% / 300px  -> 282px de carte, 195px de zone de nom
       C58 : 68% / 260px -> 252px de carte, 164px de zone (37px de marge)
       C61 : grille  -> 177px de carte, 143px utiles
     A 177px, le #num et l'etoile ne peuvent plus partager la ligne du nom : ils
     lui prenaient 62px et il n'en restait que 81 pour 127 demandes. Ils sont
     donc remontes sur une ligne a eux (cf. .cc-head), et le nom recupere les
     143px — 16px de marge, la plus courte des trois, mais positive et mesuree.
     C'est la contrepartie du passage en 2 colonnes, annoncee avant de coder.
     Le clamp a 2 lignes de C39 reste : il ne sert plus qu'aux noms herites de
     30 caracteres, qu'on ne peut plus creer mais qui existent encore. */
  position: relative; overflow: hidden;
  border-radius: 18px; padding: 14px 15px;
  background: var(--surface-card-a);   /* Refonte : fond UNI (pas de bande) */
  /* C58 — ESSAI DE RETRAIT, ANNULE PAR LA MESURE. Question posee : « en sombre,
     faudrait pas enlever le lisere autour des cartes ? »
     J'ai d'abord conclu que oui, en comparant la bordure a la CARTE :
     rgba(255,255,255,.06) sur le fond de page donne ~rgb(27,28,33) contre une
     carte a rgb(23,27,35), soit 4 unites — et deux captures indiscernables.
     Le calcul etait pose sur le mauvais couple. Le travail d'une bordure est de
     separer la carte du FOND DE PAGE, pas d'elle-meme : rgb(27,28,33) contre
     rgb(12,14,19) donne ~1,35:1, la ou le fond seul plafonne a 1,12:1.
     test-parite-themes.js a tranche : « clair 1.13:1 (contour), sombre 1.12:1
     (fond) -> invisible en SOMBRE ». La bordure reste. */
  border: 1px solid var(--border-0);
  /* C61 : ombre reduite de 8/20 a 4/12. Dans un carrousel les cartes etaient
     isolees et l'ombre les faisait flotter ; dans une grille elles sont a 12px
     l'une de l'autre, et une ombre de 20px de flou deborde sur la voisine — ce
     qui donne une grille sale au lieu d'un ensemble de cartes. */
  box-shadow: 0 4px 12px rgba(0,0,0,.32);
  transition: transform .25s ease;
}
body.in-gallery .cat-card:active { transform: scale(.985); }

/* ══ C87 — LES CHIFFRES S'ALIGNENT D'UNE CARTE A L'AUTRE ═══════════════════
   VU SUR L'APPAREIL. « PUSH » et « FINALE ELITE CALISTHENIC CUP » sont voisines
   dans la grille. La seconde passe sur deux lignes, la premiere non — et comme
   le contenu est colle en haut, leurs REPS/KCAL se retrouvent decales d'une
   ligne entiere. Les cartes ont pourtant la meme hauteur : c'est la grille qui
   les etire, et l'interieur qui ne suit pas.
   Une rangee ou les chiffres forment une ligne nette se lit comme dessinee ;
   une ou ils sautent se lit comme du HTML empile. C'est le genre d'ecart qu'on
   ne sait pas nommer en regardant, mais qu'on sent.
   La carte devient une colonne flex et le bloc de stats est pousse en bas :
   quelle que soit la longueur du nom, les chiffres partent du meme fond.

   ⚠ `:not(.cat-grid-solo)` N'EST PAS DECORATIF. Une categorie a un seul
   training recoit `class="cat-grid cat-grid-solo"` — les DEUX classes. Sans
   l'exclusion, `margin-top: auto` s'appliquerait aussi a sa carte, ou il
   ecraserait l'`align-self: center` de sa mise en page en zones nommees et
   decollerait ses stats vers le bas. On corrige un alignement en en cassant un
   autre, dans le seul cas ou il n'y a personne a aligner. */
body.in-gallery .cat-grid:not(.cat-grid-solo) .cat-card {
  display: flex;
  flex-direction: column;
}
body.in-gallery .cat-grid:not(.cat-grid-solo) .cc-stats {
  margin-top: auto;
}
/* Halo catégorie doux dans le coin — radial-gradient (pas de filter:blur, aucun débordement) */
body.in-gallery .cat-card::before {
  display: none;   /* Refonte : halo de catégorie retiré (faisait une "bande" chelou sur les cartes) — la couleur reste portée par le numéro + le statut */
}
/* C61 : la tete ne contient plus que le #num et l'etoile, qui se partagent une
   ligne a eux. `space-between` et non un flex:1 sur le num — les deux elements
   sont petits, l'espace entre eux n'a pas a etre attribue a l'un des deux. */
body.in-gallery .cc-head { display: flex; align-items: center; justify-content: space-between;
  gap: 9px; margin-bottom: 6px; padding-left: 0; min-height: 19px; }
body.in-gallery .cc-num { font-size: var(--fs-micro); font-weight: 600; color: rgb(var(--cat-rgb)); }
/* 16px et non 17px : a 143px de large, la mesure donne 127px pour un nom au
   maximum en 17px — ca passe, mais avec 16px de marge seulement. Un point de
   moins ramene le pire cas a ~120px et rend la marge confortable sans que la
   difference se voie a cote d'un libelle de stat a 16px. */
body.in-gallery .cc-name { font-size: 16px; font-weight: 800; color: #fff; letter-spacing: -.02em;
  margin-bottom: 6px;
  text-transform: uppercase;
  /* C39 : etait `white-space: nowrap` + ellipse, donc UNE ligne quoi qu'il arrive.
     Mesure sur les vrais noms du compte : 197px disponibles, et 6 sur 19 depassent —
     « RENFO F45 FULL BODY 18/06/2026 » demande 287px. Ils etaient coupes en plein
     milieu d'un mot (« RENFO F45 FULL BOD… »), alors que la carte a de la place.
     Deux lignes AU BESOIN, et non deux lignes toujours : 13 noms sur 19 tiennent sur
     une seule, leur reserver une seconde ligne aurait creuse un trou sur les deux
     tiers des cartes — c'est l'erreur que j'ai faite sur le hero avant de la corriger.
     C61 : la grille CSS etire ses items sur la hauteur de la rangee (align-items
     vaut stretch par defaut, comme le faisait le carrousel flex), donc une carte
     a nom sur deux lignes ne cree pas de dent de scie avec sa voisine. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere; }
body.in-gallery .cc-fav { background: none; border: none; font-size: 15px; color: var(--text-3);
  padding: 2px; cursor: pointer; line-height: 1; }
/* C43 : le glyphe texte est devenu un SVG (contour/plein). On garde exactement la meme
   boite — 15px + 2px de padding — pour que la tete de carte ne bouge pas d'un pixel.
   Le `fill` DOIT etre pose ici : la regle globale `svg { fill: none }` (l. 387) bat
   l'attribut de presentation `fill="currentColor"` ecrit dans le HTML — en CSS, une
   regle l'emporte toujours sur un attribut de presentation. Sans cette ligne le SVG
   reste creux dans les deux etats, et on n'a fait que remplacer un glyphe plein
   toujours identique par un contour toujours identique. C'est exactement ce que le
   test a attrape a la premiere execution. */
body.in-gallery .cc-fav svg { display: block; width: 15px; height: 15px;
  stroke: currentColor; stroke-width: 2; stroke-linejoin: round; }
body.in-gallery .cc-fav.on { color: var(--theme-color); }
body.in-gallery .cc-fav.on svg { fill: currentColor; }
body.in-gallery .cc-stats { display: flex; align-items: stretch; padding-left: 0; }
body.in-gallery .cc-stat { flex: 1; text-align: center; }
body.in-gallery .cc-sep { width: 1px; background: var(--surface-4); margin: 3px 0; }
/* C58 : etait `color: #fff` en 800 — a EGALITE avec le nom du training (17px/800
   blanc) juste au-dessus. Une carte de galerie se parcourt en cherchant un nom :
   les chiffres doivent etre lisibles, pas concurrents. Poids 700 et couleur
   secondaire pour les TROIS colonnes.
   La regle `:not(.cc-sv-est)` de C53 disparait avec : sur cette surface les trois
   chiffres se valent desormais, la distinction estimation/mesure reste sur le
   Detail et le Profil, ou on lit au lieu de survoler. */
body.in-gallery .cc-sv { font-size: 16px; font-weight: 700; color: var(--text-secondary); }
body.in-gallery .cc-sl { font-size: var(--fs-micro); font-weight: 600; color: var(--text-3); letter-spacing: .08em; margin-top: 2px; }

/* ══════════════════════════════════════════
   Bloc "Séance du jour" / "Suggéré pour toi" — étape 2
   ══════════════════════════════════════════ */
body.in-gallery .day-section { margin-bottom: 8px; }

/* C33 : retour a 196px, la hauteur d'origine.
   C31 avait du monter a 234px parce qu'un titre sur deux lignes coupait les libelles
   REPS / KCAL — la carte n'offrait pas la place. Mais la place existait : elle etait
   gaspillee par la pastille de statut, qui occupait une ligne entiere pour 79px de
   large. En la ramenant sur la ligne des chiffres (bas-droite), le contenu redescend a
   185px, titre sur deux lignes compris. La carte retrouve donc sa proportion connue,
   avec 11px de mou, et n'a plus jamais besoin de grandir.
   La lecon : j'ai d'abord agrandi le contenant, alors qu'il fallait mieux ranger le
   contenu. */
body.in-gallery { --day-block-h: 196px; }
body.in-gallery .day-hero {
  /* C31 : etait `height` FIXE (196px) avec overflow:hidden et des enfants en
     flex-shrink:0. Un nom sur deux lignes demande 184px de contenu la ou la carte
     n'en offre que 160 utiles (196 moins les 2x18 de padding) : les 24px de trop
     partaient sous le bord, et ce sont les libelles REPS / KCAL qui se faisaient
     couper en deux. Vu sur l'appareil avec « ENDURANCE ATLAS FEMALE ».
     Ce n'est pas un cas limite : sur le compte, 14 trainings ont un nom d'au moins
     22 caracteres, jusqu'a 30 (« RENFO F45 FULL BODY 18/06/2026 »).
     min-height garde exactement la meme carte quand le nom tient sur une ligne, et
     la laisse grandir quand il en faut deux. Le pager etant un flex en align-items
     stretch, toutes les pages s'alignent sur la plus haute — donc pas de saut de
     hauteur d'une page a l'autre. */
  position: relative; overflow: hidden; text-align: center;
  height: var(--day-block-h); box-sizing: border-box;
  /* Centrage vertical conserve : le bloc titre ayant desormais une hauteur fixe (deux
     lignes), la composition est identique quel que soit le nom, et il n'y a plus rien
     a ancrer ni a compenser. */
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  border-radius: 24px; padding: 18px 20px;
  /* Refonte : hero = affiche signature bleu->violet (ne reprend plus la couleur de catégorie) */
  background: var(--grad-hero);
  border: none;
  /* ⚠ C116 — L'OMBRE DOIT TENIR DANS LE PAGER, ET ELLE N'Y TENAIT PAS.
     Le commentaire d'origine disait « ombre douce (le day-pager la clippait
     avant) » : le probleme etait connu, la correction etait COURTE DE 2 PIXELS.

         bas du hero                          391
         portee de l'ombre  391 + 10 + 26  =  427
         bord du pager                        425      -> 2px coupes

     `.day-pager` est un carrousel en `overflow-x: auto`, et une boite qui
     defile sur un axe rogne AUSSI l'autre : l'ombre etait tranchee en pleine
     descente, ce qui se lit comme une barre nette sous la carte. Signale par
     Chris sur le theme clair, ou l'ombre est coloree — mais les DEUX themes
     etaient coupes.

     8 + 22 = 30 de portee pour 34 disponibles : 4px de marge. Elargir le pager
     etait l'autre voie, ecartee — sa hauteur (`--day-block-h`) est une
     constante dont depend la composition du bloc du jour. */
  box-shadow: 0 8px 22px rgba(0,0,0,.42);
  transition: transform .25s ease;
}
body.in-gallery .day-hero > * { flex-shrink: 0; }
body.in-gallery .day-hero:active { transform: scale(.985); }
body.in-gallery .day-hero::before {
  content: ''; position: absolute; top: -55%; left: -20%; right: -20%; height: 130%;
  background: radial-gradient(ellipse 70% 55% at 82% 6%, rgba(255,255,255,.20) 0%, transparent 60%);
  pointer-events: none;
}
/* C13 : le ::before du hero pose un halo blanc a 20 % pile a 82% 6% — la ou vit
   le #num. Le degrade ne passe qu'a 4,64:1 nu, donc il n'a aucune marge : sous le
   halo il retombe a 3,18:1. Rendre le degrade robuste a ce halo demanderait de le
   pousser a #213fff, un decalage brutal de la marque pour UN label de 12 px.
   Le #num prend donc le meme verre sombre que .dh-chip : lisible ou qu'il tombe
   sur le degrade, et le marqueur de coin devient volontaire au lieu de flotter. */
body.in-gallery .dh-num {
  position: relative; font-size: var(--fs-micro); font-weight: 700; color: #fff;
  letter-spacing: .12em;
  padding: 3px 8px;
  border-radius: var(--r-full, 999px);
  background: rgba(13,16,23,.28);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
/* C31 avait fixe le titre a DEUX lignes toujours, pour que la composition ne se
   deforme pas selon le nom. C'etait juste avec Inter : un tiers des noms du compte
   depassaient, donc la reservation servait souvent.

   C41 change la donnee. Barlow Condensed gagne 28 a 32 % de largeur : sur les 19 noms
   reels, UN SEUL depasse encore a 30px. Reserver une seconde ligne creuserait donc un
   vide sur 95 % des cartes au lieu d'un tiers — exactement le defaut que C31b corrigeait,
   retourne. La reservation est retiree : les 5 % de noms tres longs font glisser la
   composition d'une ligne, ce qui est bien preferable a un trou permanent.
   Le clamp a 2 lignes reste : un nom long s'affiche en entier, il ne se fait pas couper. */
body.in-gallery .dh-name { position: relative; font-size: 29px; font-weight: 800; color: #fff; text-transform: uppercase; letter-spacing: -.02em; line-height: 1.1; margin: 3px 0 9px; padding-bottom: 2px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
body.in-gallery .dh-chip { position: relative; display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-xs); font-weight: 600; color: rgba(255,255,255,.92); padding: 5px 12px; border-radius: 16px; background: rgba(13,16,23,.22); border: 1px solid rgba(255,255,255,.22); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); margin-top: 0; }
body.in-gallery .dh-dot { width: 7px; height: 7px; border-radius: 50%; }
/* Refonte : sur la hero (fond dégradé signature), le point de catégorie passe en blanc pour l'unité */
body.in-gallery .day-hero .dh-dot { background: rgba(255,255,255,.92) !important; }
/* Refonte épurée : stats nettes, reps à gauche ↔ kcal à droite (pas de pastilles) */
body.in-gallery .dh-stats { position: relative; display: flex; justify-content: space-between; width: 100%; max-width: 260px; margin: 18px auto 0; }
body.in-gallery .dh-stat { text-align: center; }
/* C38 : les chiffres etaient en 30px/800/blanc pur — exactement le titre. Deux elements
   de force identique sur la meme carte, donc aucune hierarchie : l'oeil ne savait pas
   par ou commencer, et Chris les trouvait « trop blancs ».
   Le sujet de la carte est le TRAINING ; les chiffres disent ce qu'il coute. Ils
   reculent d'un cran sur les deux axes plutot que beaucoup sur un seul : 27px au lieu
   de 30, et 88% de blanc au lieu de 100. A 27px on est toujours du « grand texte »
   (seuil 3:1) et on mesure 5.3:1 — la lisibilite n'est pas en jeu, seulement le rang. */
/* C59 : meme regle que les cartes (C58), sur le meme ecran. Les stats etaient en
   27px/800 a 88% de blanc, face a un nom en 33px/800 blanc plein : quasiment a
   egalite. Poids 700 et 62 % d'opacite — le chiffre reste large et lisible, il
   cesse d'etre un titre. Le `:not(.dh-sv-est)` de C53 disparait : sur la galerie
   les trois colonnes se valent, la distinction estimation/mesure vit sur le
   Detail et le Profil. */
body.in-gallery .dh-sv { font-size: 27px; font-weight: 700; color: rgba(255,255,255,.62); line-height: 1; }
body.in-gallery .dh-sl { font-size: var(--fs-micro); font-weight: 700; color: #fff; letter-spacing: .1em; margin-top: 5px; }

/* Carrousel paginé du bloc du jour — snap mandatory VOULU (pagination stories, ≠ carrousels catégories) */
body.in-gallery .day-pager {
  display: flex;
  /* C22 : le gap DOIT valoir le padding lateral (18px), sinon la carte suivante
     depasse dans le viewport. Calcul : le hero fait flex:0 0 100%, donc la largeur
     du contenu = V - 36 (les deux paddings). La carte 2 demarre a
     18 + (V-36) + gap = V - 18 + gap. Avec gap:14 elle demarrait donc 4 px AVANT
     le bord droit — d'ou un lisere qui se lisait comme un defaut de rendu, alors
     que les points sous le carrousel disent deja qu'il y a plusieurs pages.
     Mesure : gap:18 laissait encore 2 px, parce que la marge negative du pager
     (-18) ne compense pas exactement le padding du PARENT (20). Ce qui compte est
     la distance du bord droit du hero au bord de l'ecran, soit le padding parent.
     Avec gap:20 la carte 2 demarre pile a x=402 sur un viewport de 402 : invisible,
     snap intact. */
  gap: 20px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 10px 18px 34px 18px;   /* Refonte : place H+V pour que l'ombre du hero s'estompe sur les 4 côtés (avant : clippée = lignes) */
  margin: 0 -18px -20px -18px;
}
body.in-gallery .day-pager::-webkit-scrollbar { display: none; }
body.in-gallery .day-pager .day-hero { flex: 0 0 100%; scroll-snap-align: center; }

body.in-gallery .day-dots {
  display: flex; justify-content: center; gap: 5px; margin-top: -24px; position: relative; z-index: 2;  /* Refonte : points remontés dans le padding anti-clip du hero */
  min-height: 5px; /* réserve la place même sans dots (1 item / suggestion) → zéro layout shift */
}
body.in-gallery .day-dots i {
  width: 5px; height: 5px; border-radius: 3px;
  /* C35 : etait rgba(255,255,255,.18), AVEUGLE AU THEME. Un blanc a 18% sur le fond
     clair (#F1F4F9) donne #F4F6FA, soit 1.02:1 — la puce inactive etait litteralement
     invisible, et on ne savait donc pas qu'il y avait d'autres cartes a cote.
     Signale par Chris, qui voyait l'active sans voir les autres.
     Un gris neutre par theme donne 2.21:1 des deux cotes : presente, mais nettement
     en retrait de l'active (5.6:1 en clair, 6.1:1 en sombre, soit x2.5 de presence).
     Le meme rapport dans les deux themes, c'est ce qui rend la lecture identique. */
  background: #4B4B4B;
  transition: all .25s var(--ease-v2);
}
[data-theme="light"] body.in-gallery .day-dots i { background: #A6A6A6; }
/* ══════════════════════════════════════════
   C22 — LIGNE DE RACCOURCIS FAVORIS
   Le hero repond a « qu'est-ce que je fais maintenant ». Un favori repond a
   « emmene-moi vite quelque part » : c'est de la navigation, pas de l'actualite.
   D'ou une bande compacte d'une seule ligne, et non une page de carrousel.
   Elle n'apparait que les jours ou le hero est pris par la seance du jour.
   ══════════════════════════════════════════ */
body.in-gallery .fav-rail {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
  margin: 10px -20px 0;
  padding: 0 20px;
  /* Meme langage que les carrousels de categorie : on ne coupe pas net. */
  -webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 24px), transparent 100%);
          mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 24px), transparent 100%);
}
body.in-gallery .fav-rail::-webkit-scrollbar { display: none; }
body.in-gallery .fav-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  max-width: 62vw;
  padding: 8px 13px;
  border-radius: var(--r-full, 999px);
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  color: var(--text-primary);
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
body.in-gallery .fav-chip:active { transform: scale(.96); }
body.in-gallery .fav-chip-nom {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* L'etoile prend la couleur de la categorie, resolue par le solveur theme-aware. */
body.in-gallery .fav-chip-ico {
  width: 13px; height: 13px;
  flex: none;
  fill: rgb(var(--cat-rgb, 255,255,255));
  stroke: none;
}

body.in-gallery .day-dots i.on {
  width: 16px;
  /* C32 : etait le degrade de marque. Sur une pastille de 16x4px, un degrade a 135deg
     n'a pas la place de se deployer — il se rend comme un aplat. On depensait donc la
     signature de l'app sur un element ou elle est litteralement invisible, tout en
     diluant sa rarete. Un aplat d'accent donne le meme pixel a l'ecran, en disant la
     verite sur ce qu'il est. */
  background: var(--theme-color);
}

/* Ligne "Bien joué 💪" — état 'done' célébration (marges resserrées : la variante done tient dans --day-block-h) */
body.in-gallery .dh-cheer {
  font-size: 11px;
  color: rgba(255,255,255,.5);
  margin-top: 4px;
  position: relative;
}

/* Dernière séance — label discret sous head (card) et sous chip (hero) */
/* ══ C114 — LE NOM ET SA LIGNE DE DESSOUS SE TOUCHAIENT ═════════════════════
   Signalé par Chris. Mesuré sur l'ENCRE et non sur les boîtes — c'est l'encre
   qu'on voit : hauteur de ligne moins le corps, réparti en haut et en bas.

       nom -> sous-titre      -1,2 px   les deux encres se chevauchent
       chiffre -> unité        4,7 px   dans la MÊME carte, à trois centimètres

   La cause était un `margin-top: -6px` sans commentaire. Et la preuve que
   c'était un oubli plutôt qu'un parti pris tient trois lignes plus bas :
   `.dh-last` portait le même -6px, puis une ligne le remettait à +6. Le bloc du
   jour avait été corrigé, la carte non.

   ⚠ LA VALEUR VIENT DE L'APP, PAS DE MON GOÛT — et il en faut DEUX.
   Première tentative : marge à 0, mesurée sur une carte seule, 4,8 px. Mais en
   grille à deux colonnes la même marge donnait 8,8 px : la carte solo passe en
   `grid-area` (règle `.cat-grid-solo` plus haut) et l'espacement s'y comporte
   autrement — 4 px d'écart entre les deux dispositions. Une seule valeur ne
   peut pas satisfaire les deux, et se contenter du cas solo aurait aéré la
   grille au double de la référence.

       marge      carte seule    grille 2 colonnes
        -6           -1,2             2,8      ← l'état d'avant
        -4            0,8             4,8
         0            4,8             8,8

   D'où -4 par défaut et 0 pour le solo : 4,8 px dans LES DEUX CAS, l'écart
   chiffre/unité de la même carte au dixième près. */
body.in-gallery .cc-last {
  font-size: var(--fs-xs); font-weight: 500; color: var(--text-2);
  margin: -4px 0 10px; position: relative;
}
body.in-gallery .dh-last {
  font-size: var(--fs-xs); font-weight: 500; color: rgba(255,255,255,.72);
  margin: -6px 0 10px; position: relative;
}
body.in-gallery .dh-last { margin: 6px 0 0; }
body.in-gallery .cc-last.never, body.in-gallery .dh-last.never { color: rgb(var(--cat-rgb)); }

/* Résumé hebdo — ligne compacte au-dessus de "Séance du jour" */
body.in-gallery .week-summary { font-size: 12.5px; color: var(--text-3); padding: 2px 0 8px; }
body.in-gallery .week-summary b { color: #fff; font-weight: 700; }
body.in-gallery .ws-ico { color: var(--theme-color); margin-right: 2px; }

/* Compteur trainings sous titre catégorie */
body.in-gallery .cat-count { font-size: 12px; font-weight: 500; color: var(--text-3); letter-spacing: 0; }

/* Bouton play sur le hero — clic passe au hero → openDetail */
body.in-gallery .dh-play {
  position: absolute; bottom: 16px; right: 16px;
  width: 46px; height: 46px; border-radius: 16px;
  background: #fff; color: var(--theme-secondary);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 8px 20px rgba(0,0,0,.28);
}

/* Crossfade du hero suggéré (rotation) */
@keyframes heroFadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes heroFadeOut { from { opacity: 1; } to { opacity: 0; } }
body.in-gallery .day-hero.hero-in { animation: heroFadeIn .3s ease both; }
body.in-gallery .day-hero.hero-out { animation: heroFadeOut .24s ease both; }

/* Entrée animée des sections en galerie — UNIQUEMENT à l'entrée (classe .gal-entering) */
@keyframes gal-in { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
body.in-gallery.gal-entering .day-section,
body.in-gallery.gal-entering .cat-section { animation: gal-in .38s ease both; }
body.in-gallery.gal-entering .cat-section:nth-child(2) { animation-delay: .04s; }
body.in-gallery.gal-entering .cat-section:nth-child(3) { animation-delay: .08s; }
body.in-gallery.gal-entering .cat-section:nth-child(4) { animation-delay: .12s; }
body.in-gallery.gal-entering .cat-section:nth-child(5) { animation-delay: .16s; }
body.in-gallery.gal-entering .cat-section:nth-child(6) { animation-delay: .20s; }
@media (prefers-reduced-motion: reduce) {
  body.in-gallery.gal-entering .day-section,
  body.in-gallery.gal-entering .cat-section { animation: none; }
}

/* ═══════════════════════════════════════════════════════════
   DETAIL VIEW V2 — langage surfaces galerie (conteneurs only)

   ⚠ CE COMMENTAIRE ETAIT CASSE, ET IL EMPORTAIT LA REGLE SUIVANTE.
   Il enumerait des prefixes separes par des slashs, dont un glob « fw- » suivi
   d'une etoile puis d'un slash. Cette etoile-slash-la est une FERMETURE de
   commentaire pour le parseur, qui ne fait aucune difference entre une vraie et
   une accidentelle. Le commentaire se terminait donc en plein milieu, et la
   suite du texte se retrouvait nue dans la feuille de style. Le parseur entre
   alors en recuperation d'erreur : il avale les tokens jusqu'a la prochaine
   accolade fermante, ce qui faisait tomber la regle `body.in-detail
   .detail-hero` en entier — depuis le jour ou cette ligne a ete ecrite.

   Meme famille qu'en C53 (un paragraphe tombe APRES la fermeture), et
   test-css-valide.js ne peut pas la voir : il interroge le CSSOM, donc il ne
   connait que les declarations ayant SURVECU au parsing. Une regle avalee y est
   indistinguable d'une regle jamais ecrite. D'ou test-commentaires-css.js, qui
   lit le fichier sans navigateur.
   Regle a retenir : pas d'etoile suivie d'un slash dans un commentaire, meme au
   milieu d'un mot. Ici les prefixes sont donc separes par des virgules.

   Aucune modif renderer, parseWorkout, fw-… , timer, edition inline.
   (Fond noir main-content : herite de body.v2-ambient — plus de doublon ici.)
   ═══════════════════════════════════════════════════════════ */

/* Bloc 1 — Hero (nom/cat/diff/date/stats/#num/objectif) */
body.in-detail .detail-hero {
  background: var(--surface-card-a);
  border: 1px solid var(--surface-card-brd);
  border-radius: 24px;
  box-shadow: 0 8px 22px rgba(0,0,0,.45);   /* Refonte : hero détail flottant, fond uni (pas de bande), sans liseré interne */
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  position: relative;
  overflow: hidden;
}
/* Halo catégorie (miroir de body.in-gallery .day-hero::before) */
body.in-detail .detail-hero::before {
  content: ''; position: absolute; top: -60%; left: -20%; right: -20%; height: 130%;
  background: radial-gradient(ellipse 90% 55% at 50% 0%, rgba(var(--cat-rgb), .20) 0%, rgba(var(--cat-rgb), .06) 50%, transparent 75%);
  pointer-events: none;
  z-index: 0;
}
body.in-detail .detail-hero > *:not(.card-fav-btn) { position: relative; z-index: 1; }

/* Nom (déjà uppercase 800 -.02em côté .detail-title) — couleur pleine */
body.in-detail .detail-title { color: #fff; }

/* Catégorie habillée en chip avec point coloré (mirror .dh-chip galerie) */
body.in-detail .detail-hero-cat .detail-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* C113 : corrigé ICI et pas dans la règle groupée de fin de feuille — ce
     sélecteur fait 0-3-0 et la battait. La catégorie porte de l'information. */
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  color: rgba(255,255,255,.75);
  padding: 5px 12px;
  border-radius: 16px;
  /* C40 : fond et bordure etaient en rgba(255,255,255,.06) et .05, AVEUGLES AU THEME.
     Sur la carte blanche du theme clair : 1.00:1 — la pastille etait litteralement
     invisible. On voyait donc « ● Push » decale de 12px vers la droite sans rien qui
     explique ce retrait, alors que c'etait le padding d'une pastille qu'on ne voyait
     pas. C'est ce qui m'a mis la puce a l'oreille en unifiant l'axe de lecture.
     Meme famille que les puces du carrousel (C35) et la jauge de cycle (C36) : une
     valeur ecrite en pensant a un seul des deux fonds. Valeurs explicites par theme,
     1.22:1 des deux cotes — assez pour que la pastille EXISTE, assez discret pour
     qu'elle ne concurrence pas le titre. La couleur du texte, elle, avait deja son
     override clair : c'est pour ca que seul le contenant avait disparu. */
  background: #2B2B2B;
  border: 1px solid rgba(255,255,255,.08);
}
[data-theme="light"] body.in-detail .detail-hero-cat .detail-badge {
  background: #E8E8E8;
  border-color: rgba(12,22,52,.10);
}
body.in-detail .detail-badge-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--cat-color);
  flex-shrink: 0;
}

/* #num au-dessus du nom (mirror .dh-num galerie) */
body.in-detail .detail-num {
  font-size: 12px;
  font-weight: 700;
  color: rgb(var(--cat-rgb));
  letter-spacing: .06em;
  text-align: center;
  margin-bottom: 2px;
}

/* Meta ligne : difficulté · date · dernière séance — 11px discrets */
body.in-detail .detail-hero-meta2 {
  font-size: 11px;
  color: var(--text-3);
  gap: 8px;
}
/* C113 : ces deux-là aussi sont corrigées ici — 0-3-0 contre 0-1-0. La
   difficulté et « Ajouter une date » (qui est une ACTION) portent de
   l'information, elles quittent le plancher des unités. */
body.in-detail .detail-hero-meta2 .card-diff {
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
}
body.in-detail .detail-hero-meta2 .detail-date-span {
  font-size: var(--fs-xs);
  letter-spacing: 0;
}
body.in-detail .detail-last { color: var(--text-3); }
/* C40 : « Jamais fait » etait peint dans la COULEUR DE LA CATEGORIE. La ligne meta
   empilait donc trois systemes de couleur sur onze pixels de haut : le rouge de la
   difficulte, le gris italique du placeholder de date, et la teinte de la categorie.
   Trois signaux pour trois informations de meme rang — l'oeil ne sait plus laquelle
   compte. Et cette teinte-la ne disait rien de neuf : la categorie est deja affichee
   en toutes lettres dans la pastille juste au-dessus.
   « Jamais fait » reste distingue, mais par le POIDS de l'encre et non par une
   couleur de plus. La difficulte garde la sienne : elle, elle porte une echelle. */
body.in-detail .detail-last.never { color: var(--text-2); font-weight: 600; }

/* Stats — valeurs pleines blanches pour cohérence avec la galerie */
body.in-detail .detail-stat-val:not(.detail-stat-val-est) { color: #fff; font-weight: 800; }
body.in-detail .detail-stat-val.detail-stat-val-est { font-weight: 800; }

/* Objectif — italic déjà appliqué, harmonise couleur */
body.in-detail .detail-hero .detail-objective {
  color: var(--text-3);
}

/* Blocs 2/3/4 — surface commune (programme, timer, séances) */
body.in-detail .detail-workout-block,
body.in-detail .inline-timer,
body.in-detail .detail-sessions-block {
  background: var(--surface-card-a);
  border: 1px solid var(--surface-card-brd);
  border-radius: 20px;
  box-shadow: 0 8px 20px rgba(0,0,0,.4);   /* Refonte : blocs détail flottants, fond uni (pas de bande ni liseré) */
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
[data-theme="light"] body.in-detail .detail-workout-block,
[data-theme="light"] body.in-detail .inline-timer,
[data-theme="light"] body.in-detail .detail-sessions-block { background:#fff; border-color:rgba(12,22,52,.06); box-shadow:0 8px 18px rgba(20,28,60,.10); }

/* Titres de section — harmonise sur .cat-title galerie (16px 700 blanc) */
body.in-detail .detail-workout-header {
  color: #fff;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -.01em;
  text-transform: none;
  opacity: 1;
}

/* Items séance : style minicard (contenu/clic intacts) */
body.in-detail .session-item {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-0);
  border-radius: 15px;
}

/* Menu ... : z-index bumpé au-dessus des calques V2 étendus (defense-in-depth).
   Le dropdown était à 300, safe en théorie, mais taps sur Modifier/Dupliquer morts en pratique.
   pointer-events: auto explicite sur les items neutralise tout héritage accidentel. */
body.in-detail .detail-dropdown { z-index: 500; }
body.in-detail .detail-dropdown-item { pointer-events: auto; position: relative; z-index: 1; }

/* Chrono — label "Durée de la séance" harmonisé (petit, tracked, uppercase, muted) */
body.in-detail .itimer-toplabel {
  /* C113 : « Durée de la séance » se lit pendant l'effort. 0-2-0 ici. */
  font-size: var(--fs-xs);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-3);
}

/* ═══════════════════════════════════════════════════════════
   TIMER VIEW V2 — chrome uniquement (mode-bar + config + boutons secondaires)
   Cercle SVG, .sw-circle-overlay, play/pause icons, mode immersif, logique : INTACTS.
   ═══════════════════════════════════════════════════════════ */
body.on-timer #view-timer { background: var(--bg-base); }

/* Sélecteur de mode (CHRONO / AMRAP / EMOM) — pilules verre */
body.on-timer .sw-mode-bar {
  background: var(--surface-2);
  border: 1px solid var(--border-0);
  border-radius: 18px;
  padding: 6px;
  gap: 6px;
}
body.on-timer .sw-mode-btn {
  border-radius: 13px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  letter-spacing: .04em;
  background: transparent;
  padding: 9px 4px;
}
body.on-timer .sw-mode-btn.active {
  background: var(--surface-4);
  color: #fff;
}

/* Config / drums — bande indicateur TRANSPARENTE (recouvre le chiffre central, doit laisser voir) */
body.on-timer .sw-drum-indicator {
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  border-top: 1px solid var(--border-2);
  border-bottom: 1px solid var(--border-2);
  border-radius: 12px;
  box-shadow: none;
}
body.on-timer .sw-drum-item.sel {
  color: #fff;
  font-weight: 800;
}

/* Pick-btn (chevron picker) — surface V2 si utilisé */
body.on-timer .sw-pick-btn {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-0);
  border-radius: 12px;
  color: #fff;
  font-weight: 700;
}
body.on-timer .sw-pick-val {
  color: #fff;
  font-weight: 800;
}
body.on-timer .sw-picker-label {
  color: var(--text-3);
  letter-spacing: .06em;
}

/* Boutons secondaires — round button en surface V2, stop garde son rouge signature */
body.on-timer .sw-view-round-btn {
  background: var(--surface-3);
  border: 1px solid var(--border-1);
  border-radius: 14px;
  color: #fff;
  font-weight: 700;
}
body.on-timer .sw-view-stop-btn {
  border-radius: 14px;
}

/* ═══════════════════════════════════════════════════════════
   CREATE VIEW V2 — conteneurs uniquement
   Autocomplete, listeners, parser/renderer/fw-* : INTACTS.
   (fond noir + position/z-index : voir sélecteur étendu plus haut in-gallery)
   ═══════════════════════════════════════════════════════════ */

/* Header — safe-area sur le titre (viewport >768px sans safe-area sur base .main-content).
   Neutralise le padding-top de .main-content en create : le safe-area est porté par .view-header
   uniquement, le contenu démarre en haut d'écran (pas de double marge). */
body.in-create .main-content { padding-top: 0; padding-bottom: 190px; }
body.in-create .view-header {
  padding-top: calc(var(--safe-top) + 12px);
  margin-bottom: 20px;
}

/* Barre boutons FIXE en bas + fond gradient noir sous elle (le contenu qui défile ne se lit
   plus juste derrière les boutons). Pas de transform sur ancêtres en create (PTR scoped in-gallery)
   → position:fixed reste ancrée au viewport. */
body.in-create .form-actions {
  position: fixed;
  left: 16px;
  right: 16px;
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  z-index: 400;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 0;
  margin: 0;
}
/* Gradient DERRIÈRE les boutons : z-index -1 scopé dans le stacking context de .form-actions
   (créé par z-index:400). Ordre : contenu qui défile < gradient < boutons.
   Hauteur 180px pour couvrir la zone des DEUX boutons empilés (2×~48px + gap 8 + offset). */
body.in-create .form-actions::before {
  content: '';
  position: fixed;
  left: 0; right: 0;
  bottom: 0;
  height: 180px;
  pointer-events: none;
  /* C20 : meme correctif que le voile haut — fondait vers du #000 pur, donc une
     bande noire sous le formulaire en theme clair. */
  background: linear-gradient(to top,
    var(--bg-base) 0%,
    color-mix(in srgb, var(--bg-base) 90%, transparent) 40%,
    transparent 100%);
  z-index: -1;
}
body.in-create .form-actions .btn-primary,
body.in-create .form-actions .btn-secondary {
  position: relative;
  z-index: 1;
  width: 100%;
  justify-content: center;
}
/* Annuler create : override 030 pour battre la règle v2-ambient (020) qui met
   backdrop-filter:none. Le blur restaure la lisibilité au-dessus du fondu noir. */
/* C19 : « Annuler » etait une pleine largeur aussi lourde que « Sauvegarder » —
   deux boutons empiles de meme poids, dont l'abandon au-dessus. Une action
   d'abandon ne se met pas au niveau d'une validation : elle devient un bouton
   texte, plus bas en contraste et moins haut. Le blur reste, il porte la
   lisibilite au-dessus du fondu. */
body.in-create .form-actions .btn-secondary {
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-2);
  font-weight: 600;
  padding-top: 10px;
  padding-bottom: 10px;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
body.in-create .form-actions .btn-secondary:active { color: var(--text-1); }

/* Inputs (date, select type/diff, notes) — anti-zoom iOS (≥16px) + surface V2 discrète */
body.in-create .form-input {
  font-size: 16px;
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  border-radius: 14px;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
/* C20 : la vue creation etait la SEULE a ne poser aucune couleur de placeholder —
   elle heritait donc de celle du navigateur, mesuree a 3,74:1. Tous les autres
   formulaires de l'app posent --text-3. Alignee : coherence et lisibilite. */
body.in-create .form-input::placeholder,
body.in-create .form-textarea::placeholder { color: var(--text-3); }
body.in-create .form-input:focus {
  border-color: var(--theme-color);
  box-shadow: 0 0 0 3px rgba(255,255,255,.03);
}

/* Input NOM + bouton shuffle NU (style Spotify) — icône posée dans l'input, pas d'encadré.
   L'input prend toute la largeur ; padding-right laisse la place à l'icône. */
body.in-create .name-input-inner { position: relative; }
body.in-create .name-input-inner .form-input { width: 100%; padding-right: 42px; }
body.in-create .btn-name-random {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  width: 24px;
  height: 24px;
  background: none;
  border: none;
  padding: 0;
  color: var(--text-2);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: color .15s ease, transform .15s ease;
  touch-action: manipulation;
}
body.in-create .btn-name-random svg { width: 22px; height: 22px; display: block; }
body.in-create .btn-name-random:active {
  color: var(--theme-color);
  transform: translateY(-50%) rotate(-20deg) scale(.9);
}
@media (hover: hover) {
  body.in-create .btn-name-random:hover { color: var(--text-1); }
}

/* Textarea = hero de création : surface V2 mise en avant, mono conservée */
body.in-create .form-textarea {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-1);
  border-radius: 20px;
  font-size: 16px;
  line-height: 1.6;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.05);
}
body.in-create .form-textarea:focus {
  border-color: var(--theme-color);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.05), 0 0 0 3px rgba(255,255,255,.03);
}

/* Bouton photo — pilule verre V2 (bouton IA texte commenté HTML depuis juin 2026).
   Icône en couleur thème claire (--theme-color) : --theme-secondary est réservé aux dégradés. */
body.in-create .textarea-wrap .btn-format-photo {
  width: 38px;
  height: 38px;
  min-height: 38px;
  border-radius: 13px;
  background: var(--surface-3);
  border: 1px solid var(--border-1);
  color: var(--theme-color);
  top: 10px;
  left: 10px;
}
body.in-create .textarea-wrap .btn-format-photo svg { width: 18px; height: 18px; }

/* Toggle autocomplete ⚡ — track V2 muted en OFF, thème en ON (handler intact) */
body.in-create .autocomplete-toggle {
  top: 10px;
  right: 12px;
  color: var(--text-2);
  opacity: 1;
}
body.in-create .autocomplete-toggle-label {
  color: var(--text-2);
  font-weight: 600;
}
body.in-create .autocomplete-toggle-track {
  background: var(--surface-4);
}
body.in-create #autocompleteToggle:checked ~ .autocomplete-toggle-track {
  background: var(--theme-color);
}

/* Aperçu #programPreview — mirror .detail-workout-block V2 (contenu fw-* INTOUCHÉ) */
body.in-create .program-preview-wrap {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-0);
  border-radius: 20px;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.05);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  padding: 18px 20px;
}

/* Sparkline progression — sous-carte discrète en tête du bloc "Séances effectuées" */
body.in-detail .d-spark {
  /* C57 : `position: relative` pour ancrer la tete de courbe, qui est un element
     HTML et non un <circle> — voir le commentaire cote JS : dans un viewBox
     etire par preserveAspectRatio="none", un cercle devient un ovale. */
  position: relative;
  padding: 12px 14px;
  border-radius: 14px;
  background: var(--surface-2);
  border: 1px solid var(--border-0);
  margin-bottom: 10px;
}
body.in-detail .d-spark-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-3);
  margin-bottom: 8px;
}
/* C57 : les classes .good et .bad sont retirees. Le vert affirmait qu'une seance
   plus COURTE est meilleure — vrai sur un WOD chronometre, faux sur de
   l'endurance, et rien ne permet de trancher ici. Meme decision qu'en C45 pour le
   poids de corps : on montre le mouvement, on ne le juge pas. */
body.in-detail .d-spark-delta { color: var(--text-secondary); font-weight: 600; }
/* La tete de courbe : un point rond, POSE en pourcentage sur le bloc plutot que
   dessine dans le SVG etire. C'est la meme grammaire que .pr-spark-tete et
   .bw-spark-tete — les trois courbes de l'app marquent leur derniere valeur, et
   elle seule. */
/* La tete est posee dans un CALQUE qui recouvre exactement le SVG, et non sur le
   bloc entier : ses pourcentages se comptent alors dans le meme repere que le
   trace. Premiere version calee sur des marges fixes devinees — mesure : la tete
   atterrissait 28px HORS du bloc. On ne devine pas une position, on l'ancre. */
body.in-detail .d-spark-calque {
  position: absolute;
  left: 14px; right: 14px;
  /* 12px de padding + la tete (11px de police, ~13px de haut) + 8px de marge */
  top: 33px; height: 34px;
  pointer-events: none;
}
body.in-detail .d-spark-tete {
  position: absolute;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--theme-color);
  transform: translate(-50%, -50%);
}
body.in-detail .d-spark-tally { font-weight: 500; color: var(--text-3); }
body.in-detail .d-spark svg { width: 100%; height: 34px; display: block; overflow: visible; }
body.in-detail .d-spark-lbl {
  display: flex;
  justify-content: space-between;
  font-size: var(--fs-xs);
  color: var(--text-3);
  margin-top: 3px;
}
body.in-detail .d-spark-ticks { display: flex; gap: 4px; }
body.in-detail .d-spark-ticks span { flex: 1; height: 6px; border-radius: 3px; }
/* C57 : la case VIDE etait peinte en rgba(255,255,255,.08), ecrit en dur dans le
   JS — invisible sur une carte blanche en theme clair. Le fond vient maintenant
   d'ici, donc il suit le theme. La case pleine garde sa couleur de categorie,
   posee en style inline. */
body.in-detail .d-spark-ticks span { background: var(--surface-4); }

/* ═══════════════════════════════════════════════════════════
   HISTORY VIEW V2 — langage surfaces galerie (composant réutilisable)
   Aucune refonte de logique : groupement mois + clic → openDetail intacts.
   (fond noir + nav transparente : voir sélecteurs étendus plus haut in-gallery)
   ═══════════════════════════════════════════════════════════ */

/* En-tête de mois — style .cat-title galerie (16px 700 blanc) + compteur discret */
body.in-history .history-month-header {
  padding: 18px 0 8px;
  gap: 8px;
}
body.in-history .history-month-label {
  font-size: 16px;
  font-weight: 700;
  /* C11 : etait #fff en dur -> 1.1:1 en theme clair, l'en-tete de mois etait un
     fantome. Corrige DANS la regle scopee : une regle de composant ajoutee en fin
     de fichier perdrait contre body.in-history (0,2,1). */
  color: var(--text-primary);
  letter-spacing: -.01em;
  text-transform: uppercase;
  opacity: 1;
}
body.in-history .history-month-count {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-3);
  letter-spacing: 0;
  white-space: nowrap;
}
body.in-history .history-month-line {
  background: var(--surface-3);
}
body.in-history .history-month-group { gap: 10px; }

/* Card séance : minicard V2 */
body.in-history .history-session-card {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-0);
  border-radius: 15px;
  padding: 13px 14px;
  gap: 10px;
  transition: transform .18s ease, background .2s ease;
}
body.in-history .history-session-card:active {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  transform: scale(.985);
}
/* Halo catégorie discret — remplace la barre verticale par défaut */
body.in-history .history-session-card::before {
  content: '';
  position: absolute;
  top: -40px; right: -40px; left: auto; bottom: auto;
  width: 130px; height: 130px;
  background: radial-gradient(circle, rgba(var(--cat-rgb), .16) 0%, transparent 70%);
  border-radius: 0;
  opacity: 1;
  pointer-events: none;
}

/* #num — couleur catégorie (11px, 700) */
body.in-history .hsc-num {
  font-size: 11px;
  font-weight: 700;
  color: rgb(var(--cat-rgb));
  letter-spacing: .04em;
  white-space: nowrap;
  position: relative;
}
/* Date — info primaire (14px, 700, blanc) */
body.in-history .hsc-date {
  font-size: 14px;
  font-weight: 700;
  /* C11 : etait #fff en dur -> 1.0:1 en clair, blanc sur blanc, la date etait
     litteralement invisible. Elle occupait quand meme sa colonne, ce qui donnait
     l'illusion d'un titre centre dans la 1re liste et aligne a gauche dans la 2de
     (qui, elle, n'a pas de date). Un seul bug, trois symptomes. */
  color: var(--text-primary);
  letter-spacing: 0;
  font-family: var(--font-body);
  min-width: auto;
  position: relative;
}
/* Nom du training — secondaire (12px, 600, muted, uppercase) */
body.in-history .hsc-name {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  text-transform: uppercase;
  letter-spacing: .04em;
  position: relative;
}
/* Durée — pilule discrète, chiffres blancs 700 */
body.in-history .hsc-duration {
  font-size: 11px;
  font-weight: 700;
  /* C11 : etait #fff en dur sur --surface-3 -> 1.14:1 en clair. */
  color: var(--text-primary);
  padding: 4px 10px;
  border-radius: 100px;
  background: var(--surface-3);
  border: 1px solid var(--border-0);
  letter-spacing: 0;
  position: relative;
}

/* ══════════════════════════════════════════
   PR VIEW V2 — surfaces anthracite + pilules verre
   Le doré (#ffd700) du best PR reste — il pop plus sur anthracite qu'avant.
   ══════════════════════════════════════════ */

/* Onglets "Par exercice / Par date" — pilules verre (pattern timer mode-bar) */
body.in-pr .pr-tab-bar {
  background: var(--surface-2);
  border: 1px solid var(--border-0);
  border-radius: 18px;
  padding: 6px;
  gap: 6px;
}
body.in-pr .pr-tab {
  border-radius: 13px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-2);
  letter-spacing: .02em;
  background: transparent;
  padding: 10px 6px;
}
body.in-pr .pr-tab.active {
  background: var(--surface-4);
  color: #fff;
  box-shadow: none;
}

/* .btn-add-pr : hérite entièrement de .hdr-btn — aucune surcharge de couleur (règle finale : neutralité). */

/* Cards liste "Par exercice" — surface V2 */
body.in-pr .pr-exercise-card {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-0);
  border-radius: 20px;
  padding: 16px 18px;
}

/* ══════════════════════════════════════════
   C14 — RECORDS : hero du dernier record, progression, sparkline
   Ces regles vivent dans le bloc body.in-pr pour etre au meme niveau de
   specificite que les regles de carte existantes (0,2,0), sinon elles perdraient.
   ══════════════════════════════════════════ */

/* Hero : le dernier record en date, devant tout le reste. */
body.in-pr .pr-hero {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 16px 18px 15px;
  margin-bottom: 14px;
  border-radius: 22px;
  background: var(--grad-hero);
  box-shadow: 0 12px 28px rgba(0,0,0,.42);
  cursor: pointer;
  touch-action: manipulation;
  transition: transform var(--dur-1) var(--ease);
  position: relative;
  overflow: hidden;
}
body.in-pr .pr-hero:active { transform: scale(.985); }
body.in-pr .pr-hero-haut {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
body.in-pr .pr-hero-kicker {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--text-on-fill);
}
body.in-pr .pr-hero-quand {
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--text-on-fill);
}
body.in-pr .pr-hero-exercice {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 800;
  letter-spacing: -.02em;
  line-height: 1.15;
  color: var(--text-on-fill);
  margin: 4px 0 6px;
}
body.in-pr .pr-hero-bas {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
body.in-pr .pr-hero-perf {
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-size: 22px;
  font-weight: 800;
  color: var(--text-on-fill);
}

/* Puce de progression. Sur le hero (aplat colore) elle passe en verre sombre :
   une pastille verte saturee sur du violet est illisible et salit l'aplat. */
body.in-pr .pr-delta {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .02em;
  padding: 3px 8px;
  border-radius: var(--r-full, 999px);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
body.in-pr .pr-delta--haut     { color: var(--ok);     background: var(--ok-soft); }
body.in-pr .pr-delta--bas      { color: var(--danger); background: var(--danger-soft); }
body.in-pr .pr-delta--egal     { color: var(--text-2); background: var(--surface-3); }
body.in-pr .pr-delta--premier  { color: var(--warn);   background: var(--warn-soft); }
body.in-pr .pr-hero .pr-delta {
  color: var(--text-on-fill);
  background: rgba(13,16,23,.28);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

/* Ligne d'exercice : compteur + progression sur une meme ligne meta. */
body.in-pr .pr-exercise-meta {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 3px;
  flex-wrap: wrap;
}
body.in-pr .pr-exercise-count { margin-top: 0; }

/* Sparkline : la trajectoire, pas la valeur. Discrete par construction —
   elle ne doit pas concurrencer le chiffre du record. */
body.in-pr .pr-spark {
  flex: 0 0 auto;
  overflow: visible;
  opacity: .85;
}
body.in-pr .pr-spark polyline {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
body.in-pr .pr-spark .pr-spark-tete {
  fill: var(--accent);
  stroke: none;
}

/* Confirmation d'ajout : un flash bref sur ce qui vient de tomber. Ni rebond ni
   confettis — on souligne le fait, on ne le celebre pas a la place de l'utilisateur. */
@keyframes prFlash {
  0%   { box-shadow: 0 0 0 0 var(--accent-ring); }
  35%  { box-shadow: 0 0 0 5px var(--accent-ring); }
  100% { box-shadow: 0 0 0 0 rgba(0,0,0,0); }
}
body.in-pr .pr-vient-de-tomber {
  animation: prFlash 1s var(--ease) 1;
}
@media (prefers-reduced-motion: reduce) {
  body.in-pr .pr-vient-de-tomber { animation: none; }
}

/* Etat vide : dit a quoi sert l'ecran et propose l'action. */
body.in-pr .pr-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 56px 28px;
}
body.in-pr .pr-empty-icone {
  width: 40px; height: 40px;
  fill: none;
  stroke: var(--text-3);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
body.in-pr .pr-empty-titre {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 800;
  color: var(--text-primary);
}
body.in-pr .pr-empty-texte {
  font-size: var(--fs-sm);
  color: var(--text-2);
  max-width: 30ch;
  line-height: 1.5;
  text-align: center;
}
body.in-pr .pr-empty .btn-primary { margin-top: 6px; }
@media (hover: hover) {
  body.in-pr .pr-exercise-card:hover {
    border-color: var(--border-2);
    background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  }
}

/* Rows liste "Par date" — surface V2 plus discrète */
body.in-pr .pr-date-row {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-0);
  border-radius: 16px;
  margin-bottom: 8px;
}
@media (hover: hover) {
  body.in-pr .pr-date-row:hover {
    border-color: var(--border-1);
    background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  }
}

/* Card best PR (hero du panneau détail) — surface V2 + border DORÉE fine (signature) */
body.in-pr .pr-detail-best {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: 1px solid rgba(255,215,0,.35);
  border-radius: 24px;
  box-shadow: 0 10px 28px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,215,0,.08);
}
body.in-pr .pr-detail-best::before {
  background: linear-gradient(90deg, transparent, rgba(255,215,0,.55), transparent);
  opacity: .7;
}
body.in-pr .pr-detail-best::after { display: none; }

/* Chart wrap — surface V2 */
body.in-pr .pr-detail-chart-wrap {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: 1px solid var(--border-0);
  border-radius: 20px;
  box-shadow: 0 6px 22px rgba(0,0,0,.4);
}

/* ══════════════════════════════════════════
   SETTINGS VIEW V2 — nappe couleur thème + surfaces anthracite
   Signature : radial gradient thème en tête (suit --theme-rgb).
   Le picker de thèmes, les couleurs catégorie, les swatches : logique intacte.
   ══════════════════════════════════════════ */

/* Contexte de positionnement + isolation stacking pour la nappe ::before.
   isolation:isolate → ::before z-index:-1 reste scopé sous les enfants, sans se
   promener au niveau global (pas de conflit avec sticky-header z-index:50). */
body.in-settings #view-settings { position: relative; isolation: isolate; }

/* Nappe signature : radial-gradient couleur thème en haut, s'estompe vers le bas.
   position:absolute → défile avec le contenu (pattern profil app sportive) */
body.in-settings #view-settings::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 260px;
  pointer-events: none;
  background: radial-gradient(130% 100% at 50% -30%, rgba(var(--theme-rgb), .30), transparent 70%);
  z-index: -1;
}
/* ⚠ C116 — LA MEME NAPPE NE PEUT PAS SERVIR LES DEUX THEMES.
   30% de la couleur de theme sur du noir, c'est une lueur ; sur du blanc, c'est
   une tache. Signale par Chris : « dans Personnalisation y a du sombre en
   haut ». Mesure au pixel, en clair : (227, 223, 247) tout en haut contre
   (241, 244, 249) pour le fond — un lavande franc sur 85px.

   ⚠ ET POURQUOI ELLE ETAIT INTROUVABLE : `pointer-events: none` + `z-index: -1`.
   `document.elementsFromPoint` IGNORE les elements en `pointer-events: none` —
   ma premiere sonde a donc renvoye « rien ne peint ici » alors que les pixels
   disaient le contraire. Quand la sonde et l'image se contredisent, c'est la
   sonde qui a tort.

   .12 plutot que 0 : la nappe est la signature de l'ecran, on la garde — juste
   assez pour se sentir, plus assez pour se voir. Essaye a .30 (tache), .12
   (retenu) et 0 (propre mais anonyme). */
[data-theme="light"] body.in-settings #view-settings::before {
  background: radial-gradient(130% 100% at 50% -30%, rgba(var(--theme-rgb), .12), transparent 70%);
}

/* Sticky header : transparent en haut de page (laisse la nappe respirer),
   verre dépoli dès qu'on scrolle (lisibilité conservée) */
body.in-settings .settings-sticky-header {
  background: transparent;
  transition: background .2s ease, backdrop-filter .2s ease;
}
/* ══ C117 — LE VERRE DE CET EN-TETE ETAIT NOIR DANS LES DEUX THEMES ═════════
   Signale par Chris : « dans Personnalisation, c'est AU SCROLL que ca
   s'assombrit en haut ». Mesure, en clair, avant et apres defilement :

       haut de page   rgba(0, 0, 0, 0)
       defile 300px   rgba(0, 0, 0, 0.55)   <- un voile NOIR sur fond blanc

   La meme valeur en dur dans les deux themes : sur le noir c'est le verre
   depoli voulu, sur le blanc c'est une barre sombre qui tombe du haut.

   ⚠ ET LE JETON POUR CA EXISTAIT DEJA, AVEC ZERO USAGE. `--scrim-header` est
   declare dans les deux themes — rgba(12,14,19,.72) en sombre,
   rgba(241,244,249,.80) en clair — et personne ne s'en servait. Meme schema
   qu'en C113 avec l'echelle typo : la convention est ecrite, le code l'ignore,
   et le defaut arrive par la valeur en dur qui la contourne.
   (`--scrim-modal` est dans le meme cas : declare, zero usage, et le voile des
   modales est ecrit en dur. Pas touche ici — un defaut a la fois, mesure
   avant.) */
body.in-settings.gal-scrolled .settings-sticky-header {
  background: var(--scrim-header);
  backdrop-filter: blur(20px) saturate(1.3);
  -webkit-backdrop-filter: blur(20px) saturate(1.3);
}

/* Onglets Catégories / Format / Mes styles / Thème — pilules verre (pattern timer) */
body.in-settings .settings-tab-bar {
  background: var(--surface-2);
  border: 1px solid var(--border-0);
  border-radius: 18px;
  padding: 6px;
  gap: 6px;
}
body.in-settings .settings-tab {
  border-radius: 13px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  letter-spacing: .04em;
  background: transparent;
  padding: 9px 4px;
}
body.in-settings .settings-tab.active {
  background: var(--surface-4);
  color: #fff;
}

/* Liste catégories — conteneur surface V2 anthracite (drag/édition/couleurs intacts) */
body.in-settings .categories-list {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  backdrop-filter: none;
  border: 1px solid var(--border-0);
  border-radius: 20px;
}
body.in-settings .category-row {
  border-bottom: 1px solid var(--border-0);
  background: transparent;
}
body.in-settings .category-row:last-child { border-bottom: none; }
body.in-settings .new-cat-color-section {
  border-bottom: 1px solid var(--border-0);
}

/* Bouton primary "Ajouter une catégorie / un mot" — pas de restyle,
   il utilise déjà le style thème global. On assure juste le radius V2. */
body.in-settings #openAddCategoryBtn,
body.in-settings #addCustomStyleBtn { border-radius: 16px; }

/* Cards du picker de thème — surface V2 anthracite (previews et checks intacts) */
body.in-settings .theme-option {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-0);
  border-radius: 20px;
}
@media (hover: hover) {
  body.in-settings .theme-option:hover {
    border-color: var(--border-2);
    background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  }
}
body.in-settings .theme-option.active {
  border-color: rgba(var(--theme-rgb), .45);
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  box-shadow:
    0 8px 22px rgba(0,0,0,.5),
    inset 0 0 0 1px rgba(var(--theme-rgb), .15);
}

/* Titre de section — un poil plus visible sur nappe colorée */
body.in-settings .settings-section-title { color: var(--text-2); }

/* ══════════════════════════════════════════
   MODALES & TOASTS V2 — contenants uniquement (open/close/handlers intacts)
   Surface anthracite, border hairline, radius 22px, shadow dense.
   Inputs internes : .form-input garde son 16px iOS (aucune override taille ici).
   ══════════════════════════════════════════ */

/* ══ C117 — LE VOILE DES MODALES PASSE AU JETON, LUI AUSSI ══════════════════
   Meme defaut que l'en-tete de Personnalisation, trouve en cherchant ses
   freres : `rgba(0,0,0,.6)` en dur, identique dans les deux themes. Sur du
   blanc, 60% de noir derriere une modale, c'est lourd — les references du
   marche sont a 32-40%.
   `--scrim-modal` etait declare pour ca, dans les deux themes, avec ZERO usage :
   rgba(6,7,10,.66) en sombre (a un cheveu de la valeur en dur, donc le sombre
   ne bouge pas) et rgba(20,26,40,.38) en clair. */
.modal-overlay {
  background: var(--scrim-modal);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

/* Carte modale — surface V2 gradient anthracite */
.modal {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: 1px solid rgba(255,255,255,.08);
  border-radius: 22px;
  box-shadow: 0 20px 50px rgba(0,0,0,.6);
}
/* Liseré supérieur : blanc discret (V2 neutre) au lieu de theme-color (V1 néon) */
.modal::before {
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.18), transparent);
  opacity: 1;
}

/* Bouton primary de modale — dégradé thème signature (harmonisé avec .btn-primary V2), radius V2 */
.modal-footer .btn-primary {
  background: var(--gradient-accent);
  border: 1px solid transparent;
  color: var(--theme-text-on-accent, #fff);
  border-radius: 14px;
  box-shadow: 0 8px 22px rgba(0,0,0,.35);
}
@media (hover: hover) {
  .modal-footer .btn-primary:hover { filter: brightness(1.08); box-shadow: 0 4px 16px rgba(0,0,0,.35); }
}

/* Bouton secondary de modale — verre discret */
.modal-footer .btn-secondary {
  background: var(--surface-3);
  border: 1px solid var(--border-1);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  color: var(--text-1);
  border-radius: 14px;
}
@media (hover: hover) {
  .modal-footer .btn-secondary:hover { background: var(--surface-4); border-color: var(--border-2); color: #fff; }
}

/* Toast — surface anthracite discrète, radius 14px */
.toast {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: 1px solid var(--border-1);
  border-radius: 14px;
  box-shadow: 0 12px 32px rgba(0,0,0,.55);
}

/* ══════════════════════════════════════════
   BOUTONS PRIMAIRES V2 — harmonisation des dégradés cyan→bleu V1 restants.
   Scope v2-ambient : auth (hors v2-ambient) conserve son .btn-primary V1 original.
   Cible tous les .btn-primary dans l'app active (inbox, share, settings, create, PR,
   modales — via cascade cohérente avec l'override .modal-footer V2 précédent).
   ══════════════════════════════════════════ */
/* C13 : ce dégradé était en dur / passait par --theme-color, l'alias hérité de
   --accent (#5B8CFF), que le socle de tokens déclare lui-même inutilisable avec du
   blanc dessus (3,16:1). Les tokens de marque, eux, passent déjà : --grad-primary
   part de #4B63FF (4,64:1) et --grad-hero de #4B63FF aussi. La marque n'avait pas
   besoin de changer — il fallait juste que ces aplats la consomment. */
body.v2-ambient .btn-primary {
  background: var(--gradient-accent);
  background-size: auto;
  border-radius: 14px;
  box-shadow: 0 8px 22px rgba(0,0,0,.35);
  color: var(--theme-text-on-accent, #fff);
}
@media (hover: hover) {
  body.v2-ambient .btn-primary:hover {
    transform: none;
    filter: brightness(1.08);
    box-shadow: 0 10px 26px rgba(0,0,0,.42);
    background-position: 0 0;
  }
}
body.v2-ambient .btn-primary:active { transform: scale(.96); }

/* Boutons secondaires — verre V2, contraste renforcé (couleur #fff + border plus marquée)
   pour rester lisibles au-dessus du fondu noir de create et sur les modales anthracite. */
body.v2-ambient .btn-secondary {
  background: var(--surface-3);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: 1px solid var(--border-2);
  color: #fff;
  font-weight: 600;
  border-radius: 14px;
}
@media (hover: hover) {
  body.v2-ambient .btn-secondary:hover {
    background: var(--surface-4);
    border-color: var(--border-2);
    color: #fff;
  }
}

/* QR code (share modal) — surface propre, plus de glow néon autour du conteneur.
   Le fond reste var(--bg-base) : le QR est rendu en couleur thème dessus, contraste OK. */
body.v2-ambient .qr-container {
  border: 1px solid var(--border-1);
  box-shadow: none;
}

/* ══════════════════════════════════════════
   TITRES DE VUE V2 — bicolore → blanc uniforme + barre thème signature dessous
   Le <em> est conservé (styles/JS peuvent le référencer) : seule sa couleur est neutralisée.
   EXCEPTION : .gallery-logo (logo NEXSET du topbar galerie) — classe distincte, non impactée.
   ══════════════════════════════════════════ */
body.v2-ambient .view-title { position: relative; }
body.v2-ambient .view-title em { color: inherit; }
body.v2-ambient .view-title::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -8px;
  width: 34px;
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--theme-color), var(--theme-secondary));
}

/* ══════════════════════════════════════════
   AUDIT COULEUR — surcharges des bleus legacy rgba(59,139,255,...) restants.
   Ces rgba étaient un bleu figé #3b8bff, indépendant du thème. Sous v2-ambient
   on repasse en teinte thème (bordures) ou neutre (surfaces), pour suivre Bleu/Violet/Mixte.
   ══════════════════════════════════════════ */

/* Bouton "+ Ajouter une séance" — surface neutre, accent thème */
body.v2-ambient .btn-add-session {
  background: var(--surface-3);
  border: 1px solid var(--border-bright);
  color: var(--theme-color);
}
@media (hover: hover) {
  body.v2-ambient .btn-add-session:hover {
    background: var(--surface-3);
    border-color: var(--border-2);
  }
}

/* Focus des textareas (hors create qui a déjà son override thème) — teinte thème via variables */
body.v2-ambient .form-textarea:focus {
  border-color: var(--border-bright);
  box-shadow: none;   /* C15 : etait `0`, invalide donc ignore */
}

/* ══════════════════════════════════════════
   DROPDOWN DÉTAIL "..." — langage V2
   Cosmétique uniquement : z-index, position, open/close, handlers = intacts.
   ══════════════════════════════════════════ */
body.in-detail .detail-dropdown {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: 1px solid var(--border-1);
  border-radius: 16px;
  box-shadow: 0 16px 40px rgba(0,0,0,.6);
  padding: 6px;
}
body.in-detail .detail-dropdown-item {
  border-radius: 11px;
  padding: 11px 14px;
  color: #fff;
  font-weight: 500;
  transition: background var(--dur-micro) ease;
}
@media (hover: hover) {
  body.in-detail .detail-dropdown-item:hover { background: var(--surface-3); }
}
body.in-detail .detail-dropdown-item:active { background: var(--surface-3); }
body.in-detail .detail-dropdown-sep {
  height: 1px;
  background: var(--surface-3);
  margin: 4px 8px;
}
/* Danger (Supprimer) : rouge conservé, hover teinté rouge léger */
body.in-detail .detail-dropdown-danger { color: var(--accent-red); }
@media (hover: hover) {
  body.in-detail .detail-dropdown-danger:hover { background: rgba(255,71,87,.09); }
}
body.in-detail .detail-dropdown-danger:active { background: rgba(255,71,87,.09); }

/* ══════════════════════════════════════════
   MODALES RECORD (Nouveau / Modifier) — langage V2
   Cosmétique uniquement : handlers save/delete/cancel/close intacts,
   ainsi que prExerciseSuggest/prEditExerciseSuggest et la sélection d'items.
   ══════════════════════════════════════════ */

/* Inputs des 2 modales — alignés sur .form-input V2 de create.
   font-size 16px conservé implicitement (héritage .form-input global), anti-zoom iOS. */
#addPRModal .form-input,
#editPRModal .form-input {
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  border-radius: 14px;
  /* C25 : etait #fff en dur. Un override clair existait pour #editPRModal (l.9521)
     mais PAS pour #addPRModal — la ligne jumelle avait ete oubliee. Les 6 champs de
     « Nouveau record » etaient donc blanc sur blanc en theme clair alors que ceux de
     « Modifier » allaient bien. Corrige ici, dans la regle partagee, pour que les
     deux modales ne puissent plus diverger. */
  color: var(--text-primary);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
#addPRModal .form-input::placeholder,
#editPRModal .form-input::placeholder { color: var(--text-3); }
#addPRModal .form-input:focus,
#editPRModal .form-input:focus {
  border-color: var(--theme-color);
  box-shadow: 0 0 0 3px rgba(255,255,255,.03);
}

/* Segmented Force / Cardio — pilules verre (pattern timer/PR/settings) */
#addPRModal .pr-type-toggle,
#editPRModal .pr-type-toggle {
  background: var(--surface-2);
  border: 1px solid var(--border-0);
  border-radius: 18px;
  padding: 6px;
  gap: 6px;
}
#addPRModal .pr-type-btn,
#editPRModal .pr-type-btn {
  border-radius: 13px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  letter-spacing: .04em;
  background: transparent;
  padding: 10px 6px;
}
#addPRModal .pr-type-btn.active,
#editPRModal .pr-type-btn.active {
  background: var(--surface-4);
  color: #fff;
  box-shadow: none;
}

/* Droplist de suggestions d'exercices — surface V2 flottante.
   Classe réelle : .pr-suggestions (avec id #prSuggestions / #editPRSuggestions).
   Items dynamiques : .pr-suggestion-item (rendu par prExerciseSuggest script.js:8215).
   z-index 500 déjà sur .pr-suggestions ligne 5559 — scope au modal via absolute positioning
   dans .pr-suggest-wrap (position: relative) → paint au-dessus du contenu modal suivant. */
#addPRModal .pr-suggestions,
#editPRModal .pr-suggestions {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-1);
  border-radius: 14px;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: 0 16px 40px rgba(0,0,0,.6);
  padding: 4px;
}
#addPRModal .pr-suggestion-item,
#editPRModal .pr-suggestion-item {
  border-radius: 10px;
  padding: 10px 12px;
  color: #fff;
  font-size: 14px;
  transition: background var(--dur-micro) ease;
}
@media (hover: hover) {
  #addPRModal .pr-suggestion-item:hover,
  #editPRModal .pr-suggestion-item:hover { background: var(--surface-3); }
}
#addPRModal .pr-suggestion-item:active,
#editPRModal .pr-suggestion-item:active { background: var(--surface-3); }
/* Retire la border-top entre items (surface V2 sans séparateurs internes) */
#addPRModal .pr-suggestion-item + .pr-suggestion-item,
#editPRModal .pr-suggestion-item + .pr-suggestion-item { border-top: none; }

/* ══════════════════════════════════════════
   SUPPORT / ADMIN-SUPPORT — langage V2
   Handlers (sendSupportMessage, sendAdminReply, toggleAdminThreadStatus,
   showAdminThreadList, loadSupportView, loadAdminSupportView) intacts.
   Nav bottom masquée : body.in-support .bottom-nav { display:none } déjà en place.
   ══════════════════════════════════════════ */

/* Note : showView pose body.in-support pour name === 'support' ET 'admin-support' (script.js:1605-1608).
   → tous les sélecteurs chat utilisent body.in-support ; les éléments admin-only
   (#adminThreadList, .admin-thread-item…) sont scopés #view-admin-support (jamais rendus ailleurs). */

/* Labels au-dessus des bulles — plus discrets, blancs semi-transparents */
body.in-support .support-msg-label {
  color: var(--text-3);
  font-size: var(--fs-xs);
  letter-spacing: .08em;
}

/* Bulle USER — dégradé thème signature */
body.in-support .support-msg-user .support-bubble {
  background: var(--gradient-accent);
  color: var(--theme-text-on-accent, #fff);
  border: none;
  border-radius: 16px;
  border-bottom-right-radius: 6px;
  box-shadow: 0 6px 18px rgba(0,0,0,.35);
}

/* Bulle ADMIN — surface V2 anthracite */
body.in-support .support-msg-admin .support-bubble {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-0);
  color: #fff;
  border-radius: 16px;
  border-bottom-left-radius: 6px;
}

/* Timestamps sous les bulles — 10px muted */
body.in-support .support-msg-time {
  font-size: var(--fs-xs);
  color: var(--text-3);
}

/* Zone de saisie — input V2 + border-top allégé */
body.in-support .support-input-bar {
  border-top: 1px solid var(--border-0);
}
body.in-support .support-input-bar .form-input {
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  border-radius: 14px;
  /* C20 : etait #fff en dur, exactement comme les champs du profil physique. En
     theme clair --surface-2 vaut #FFFFFF : ce qu'on tapait dans le support etait
     BLANC SUR BLANC, 1,0:1. Mesure faite sur les 9 vues x 2 themes : c'etaient les
     deux seuls champs concernes. */
  color: var(--text-primary);
  font-size: 16px; /* anti-zoom iOS */
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
body.in-support .support-input-bar .form-input::placeholder {
  color: var(--text-3);
}
body.in-support .support-input-bar .form-input:focus {
  border-color: var(--theme-color);
  box-shadow: 0 0 0 3px rgba(255,255,255,.03);
}

/* Header du chat — border-bottom allégé, back button en verre V2 */
body.in-support .support-chat-header {
  border-bottom: 1px solid var(--border-0);
}
body.in-support .support-back-btn { color: var(--text-2); }
@media (hover: hover) {
  body.in-support .support-back-btn:hover {
    background: var(--surface-3);
    color: #fff;
  }
}

/* Panel admin — items thread en minicard V2 (scope #view-admin-support = jamais rendus ailleurs) */
#view-admin-support .admin-thread-item {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-0);
  border-radius: 16px;
  padding: 14px 16px;
}
@media (hover: hover) {
  #view-admin-support .admin-thread-item:hover {
    background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
    border-color: var(--border-1);
  }
}
#view-admin-support .admin-thread-pseudo { color: #fff; font-weight: 700; }
#view-admin-support .admin-thread-preview { color: var(--text-3); }
#view-admin-support .admin-thread-time { color: var(--text-3); }
/* Badge non-lu — pastille thème + accent left */
#view-admin-support .admin-unread-dot { background: var(--theme-color); }
#view-admin-support .admin-thread-unread {
  border-left: 3px solid var(--theme-color);
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  padding-left: 13px;
}
#view-admin-support .admin-thread-unread .admin-thread-pseudo { color: var(--theme-color); }

/* Header détail admin — border allégé */
#view-admin-support .admin-detail-header {
  border-bottom: 1px solid var(--border-0);
}

/* ══════════════════════════════════════════
   PROFIL — inputs "Profil physique" (Poids / Sexe / Âge) au langage V2
   type=number / inputmode / select natif intacts ; handlers de sauvegarde intacts.
   ══════════════════════════════════════════ */
body.in-profile .physio-field .form-input {
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  border-radius: 14px;
  /* C20 : etait #fff en dur. En theme clair --surface-2 vaut #FFFFFF : la valeur
     saisie etait donc BLANC SUR BLANC, 1,0:1, totalement invisible. Cause racine
     documentee du projet — une regle scopee body.in-* bat l'override de theme
     (0,2,1 contre 0,2,0), donc on corrige ICI et pas en fin de fichier. */
  color: var(--text-primary);
  font-size: 16px; /* anti-zoom iOS */
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  text-align: center;
  text-align-last: center; /* select : centre le texte du placeholder aussi */
}
body.in-profile .physio-field .form-input::placeholder {
  color: var(--text-3);
}
body.in-profile .physio-field .form-input:focus {
  border-color: var(--theme-color);
  box-shadow: 0 0 0 3px rgba(255,255,255,.03);
  outline: none;
}
/* Sexe = <select> natif : conserve la flèche système, aligne juste la surface.
   -webkit-appearance:menulist-button restaure la caret sur iOS malgré appearance:none
   posé globalement sur input/textarea/select. */
body.in-profile .physio-field select.form-input {
  -webkit-appearance: menulist;
  appearance: menulist;
  padding-right: 14px;
}
body.in-profile .physio-field label {
  color: var(--text-3);
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
}

/* ══════════════════════════════════════════
   MODALES COMPTE — inputs V2 (pseudo, mot de passe, reset)
   type="password"/"text" + autocomplete/maxlength intacts. Aucun handler touché.
   Pas d'œil afficher/masquer présent en HTML — rien à harmoniser côté toggle.
   ══════════════════════════════════════════ */
#editPasswordModal .form-input,
#resetPasswordModal .form-input {
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  border-radius: 14px;
  /* C25 : etait #fff en dur sur --surface-2, qui vaut #FFFFFF en clair — donc le
     mot de passe qu'on tape etait BLANC SUR BLANC. Troisieme occurrence du meme
     motif apres le profil physique et le support : #fff en dur dans une regle de
     modale, sans override de theme. */
  color: var(--text-primary);
  font-size: 16px; /* anti-zoom iOS */
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
#editPasswordModal .form-input::placeholder,
#resetPasswordModal .form-input::placeholder {
  color: var(--text-3);
}
#editPasswordModal .form-input:focus,
#resetPasswordModal .form-input:focus {
  border-color: var(--theme-color);
  box-shadow: 0 0 0 3px rgba(255,255,255,.03);
  outline: none;
}

/* ══════════════════════════════════════════
   MODALE SHARE (#shareModal) — onglet "Envoyer à" au langage V2
   Handlers switchShareTab / sendToUser / loadRecentRecipients / copyToClipboard
   et tous les onclick intacts. Cosmétique pure.
   ══════════════════════════════════════════ */

/* Input pseudo destinataire — surface V2 alignée */
#shareModal .form-input {
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  border-radius: 14px;
  /* C25 : etait #fff en dur, sans aucun override clair — le pseudo du destinataire
     s'ecrivait donc en blanc sur blanc. */
  color: var(--text-primary);
  font-size: 16px; /* anti-zoom iOS */
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
#shareModal .form-input::placeholder { color: var(--text-3); }
#shareModal .form-input:focus {
  border-color: var(--theme-color);
  box-shadow: 0 0 0 3px rgba(255,255,255,.03);
  outline: none;
}

/* Bouton Envoyer (#sendToUserBtn) hors .modal-footer → doit avoir le même style V2 que
   les autres primaires. Il hérite déjà de body.v2-ambient .btn-primary (dégradé thème
   + shadow + radius 14px) quand la modale est ouverte au-dessus d'une vue V2 — vérifié. */

/* Chips récents (.recipient-chip, générés par loadRecentRecipients script.js:5267) */
#shareModal .recipient-chip {
  background: rgba(255,255,255,.06);
  border: 1px solid rgba(255,255,255,.09);
  border-radius: 13px;
  color: #fff;
  font-size: 13px;
  font-weight: 500;
  padding: 7px 13px;
  transition: background var(--dur-micro) ease, border-color var(--dur-micro) ease;
}
@media (hover: hover) {
  #shareModal .recipient-chip:hover {
    background: var(--surface-3);
    border-color: var(--border-2);
    color: #fff;
  }
}
#shareModal .recipient-chip:active {
  background: var(--surface-4);
  border-color: var(--border-2);
}

/* Onglets QR Code / Envoyer à — soulignement conservé (convention modale), couleurs V2 */
#shareModal .share-tabs {
  border-bottom: 1px solid var(--border-0);
}
#shareModal .share-tab {
  color: var(--text-2);
}
#shareModal .share-tab.active {
  /* C25 : etait #fff en dur sur --surface-2, qui vaut #FFFFFF en theme clair —
     donc BLANC SUR BLANC. Vu sur l'appareil : dans la modale « Programmer un
     training », le titre etait absent et les NOMS DES TRAININGS invisibles. */
  color: var(--text-primary);
  border-bottom-color: var(--theme-color);
}

/* Label "Récents" — harmonisé sur le style muted uppercase (déjà OK, mais on affine
   la couleur pour cohérence V2) */
#shareModal .recent-label {
  color: var(--text-3);
  font-size: 11px;
  letter-spacing: .1em;
}

/* Expiry mention QR — teinte muted V2 */
#shareModal .share-expiry {
  color: var(--text-3);
}

/* ══════════════════════════════════════════
   PRÉPA (phase 1) — coquille + nav semaine
   Le titre .view-title hérite de la barre dégradée V2 (::after) via body.v2-ambient.
   ══════════════════════════════════════════ */

/* Header : le titre à gauche, l'icône calendrier à droite */
body.in-prepa .view-header {
  justify-content: space-between;
  align-items: center;
}
body.in-prepa .prepa-header-actions {
  display: flex; align-items: center; gap: 8px;
}

/* .prepa-cal-btn / #prepaGoalsBtn : héritent de .hdr-btn (36×36 radius 12, verre) — aucune règle spécifique. */

/* Navigation semaine — flèches ‹ › + libellé au centre */
body.in-prepa .prepa-week-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 18px;
}

/* ══ Prépa — objectifs (carte compète dorée + bandeau cycle) ══ */
body.in-prepa .prepa-goals {
  display: flex; flex-direction: column; gap: 10px;
  margin-bottom: 14px;
}
body.in-prepa .prepa-goals:empty { display: none; }

/* Carte compète dorée */
body.in-prepa .prepa-compet-card {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: 12px;
  padding: 15px 18px;
  border-radius: 18px;
  /* Refonte : teinte dorée chaude (bannière compét qui ressort) + relief flottant */
  background: linear-gradient(150deg, rgba(255,190,40,.16), rgba(255,150,20,.05)), linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid rgba(255,190,40,.3);
  box-shadow: 0 10px 24px rgba(0,0,0,.4);
  cursor: pointer;
  transition: transform .1s ease;
}
[data-theme="light"] body.in-prepa .prepa-compet-card { box-shadow: 0 8px 18px rgba(120,90,20,.14); }
body.in-prepa .prepa-compet-card:active { transform: scale(.985); }
body.in-prepa .prepa-compet-card::before {
  content: ''; position: absolute; top: -50%; right: -20%; width: 80%; height: 160%;
  background: radial-gradient(circle at 70% 30%, rgba(255,215,0,.14) 0%, transparent 60%);
  pointer-events: none;
}
body.in-prepa .pcc-left { position: relative; flex: 1; min-width: 0; }
body.in-prepa .pcc-name {
  font-size: 13px; font-weight: 800; color: #fff;
  text-transform: uppercase; letter-spacing: .02em; line-height: 1.15;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body.in-prepa .pcc-meta {
  font-size: var(--fs-xs); color: var(--text-3); margin-top: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body.in-prepa .pcc-right { position: relative; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; flex-shrink: 0; }
/* C44 : le compte a rebours avant compete etait le second gros chiffre reste en
   Inter 800 (mesure). C'est le chiffre le plus charge de sens de la Prepa — il
   doit porter la meme voix que les autres. Barlow Condensed est aussi plus etroite,
   ce qui laisse de la place a « J-100 » sans toucher au transform-origin. */
body.in-prepa .pcc-j {
  font-family: var(--font-display);
  font-size: 22px; font-weight: 800; color: #ffd700; line-height: 1;
  letter-spacing: -.01em;
  transform-origin: right center;
}
@keyframes pccJourJPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }
body.in-prepa .pcc-j-jourJ {
  animation: pccJourJPulse 1.8s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  body.in-prepa .pcc-j-jourJ { animation: none; }
}
body.in-prepa .pcc-tag {
  /* C11 : etait rgba(255,215,0,.6) en dur, et l'override clair rgba(184,115,10,.85)
     = 2.66:1. --warn porte deja le role « or » dans les deux themes. */
  font-size: var(--fs-xs); font-weight: 700; color: var(--warn-ink);
  letter-spacing: .08em;
}

/* Bandeau cycle : NOM coloré · barre · S3/5 */
body.in-prepa .prepa-cycle-bar {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 14px;
  border-radius: 12px;
  background: var(--surface-2);
  border: 1px solid var(--border-0);
  cursor: pointer;
  transition: transform .1s ease;
}
body.in-prepa .prepa-cycle-bar:active { transform: scale(.985); }
body.in-prepa .pcb-name { color: var(--text-2);
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; flex-shrink: 0;
  /* C27 : etait max-width 90px, un nombre magique. « PRÉPA FORCE » tient a 81px et
     passait donc juste, ce qui a masque le probleme ; « INTENSIFICATION » demande
     ~110px et s'affichait « INTENSIFICA… ». Or la barre fait 362px dont 208 pour la
     piste : la place existait, c'est le plafond qui coupait. Une part relative
     s'adapte a la longueur du nom ET a la largeur de l'ecran, la ou une valeur fixe
     ne pouvait etre juste que pour un seul nom. */
  max-width: 45%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body.in-prepa .pcb-track {
  /* Plancher : sans lui, un nom tres long reduirait la piste a un trait de quelques
     pixels ou l'avancement ne se lirait plus. Le nom cede en premier (ellipse), la
     jauge garde de quoi rester une jauge. */
  flex: 1; min-width: 72px; height: 5px; border-radius: 3px;
  /* C36 : etait var(--surface-3), a 1.10:1 du fond en sombre et 1.14 en clair — on ne
     voyait donc pas ou la piste s'ARRETE, et sans son extremite un remplissage ne dit
     plus une proportion, juste une longueur. Les jetons de surface du projet sont tous
     a ~1.1:1 de leur fond par construction (les cartes se detachent par leur bordure) :
     il en faut un plus marque ici, parce que la piste EST l'information.
     Valeurs explicites par theme plutot qu'un color-mix : la version color-mix ne se
     resolvait pas en couleur calculable, donc invisible a la mesure — et ce qu'on ne
     peut pas mesurer, on ne peut pas garantir. 1.35:1 des deux cotes. */
  background: #323232;
  overflow: hidden; position: relative;
}
[data-theme="light"] body.in-prepa .pcb-track { background: #DEDEDE; }
body.in-prepa .pcb-fill {
  height: 100%; border-radius: 3px;
  transition: width .35s var(--ease-v2);
}
body.in-prepa .pcb-week {
  font-size: var(--fs-xs); font-weight: 600; color: var(--text-3);
  flex-shrink: 0; letter-spacing: .02em;
}
body.in-prepa .prepa-week-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
  color: #fff;
  letter-spacing: .02em;
  text-align: center;
  flex: 1;
}
body.in-prepa .wnav {
  width: 30px;
  height: 30px;
  border-radius: 10px;
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  color: var(--text-2);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background var(--dur-micro) ease, color var(--dur-micro) ease;
  touch-action: manipulation;
  flex-shrink: 0;
}
@media (hover: hover) {
  body.in-prepa .wnav:hover {
    background: var(--surface-3);
    color: #fff;
  }
}
body.in-prepa .wnav:active { transform: scale(.9); }
body.in-prepa .wnav svg { width: 15px; height: 15px; }

/* Conteneur des 7 rangées de jours */
body.in-prepa .prepa-week {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* Rangée jour : colonne gauche (LUN 6) + zone droite (cards ou Repos) */
body.in-prepa .pw-row {
  display: flex;
  align-items: stretch;
  gap: 12px;
  min-height: 62px;
}
body.in-prepa .pw-day {
  flex: 0 0 46px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 4px 0;
}
body.in-prepa .pw-day-abbr {
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: .1em;
  color: var(--text-3);
}
body.in-prepa .pw-day-num {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 800;
  color: var(--text-1);
  line-height: 1;
}
body.in-prepa .pw-row-today .pw-day-num { color: var(--theme-color); }
/* Jours cliquables (aujourd'hui + futur) : cursor + feedback :active (le tap sur la date suffit
   comme affordance — plus de mini "+" additionnel). */
body.in-prepa .pw-day-actionable { cursor: pointer; }
body.in-prepa .pw-day-actionable:active { transform: scale(.92); transition: transform 80ms; }

/* Zone droite : conteneur flex qui accueille pw-cards ou pw-rest */
body.in-prepa .pw-slot {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: stretch;
}

/* Cards : par défaut mono-card pleine largeur, en mode multi → carrousel horizontal */
body.in-prepa .pw-cards {
  flex: 1;
  min-width: 0;
  display: flex;
  gap: 8px;
}
body.in-prepa .pw-cards.multi {
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  position: relative;
}
body.in-prepa .pw-cards.multi::-webkit-scrollbar { display: none; }
body.in-prepa .pw-cards.multi .pw-card {
  flex: 0 0 72%;
}
/* Fondus latéraux retirés : le PEEK (card suivante coupée à 72% width) suffit comme indicateur
   de contenu, cohérent avec les carrousels de la galerie. Le listener scroll JS est retiré aussi. */

/* Card séance — minicard V2 + halo catégorie */
body.in-prepa .pw-card {
  position: relative;
  flex: 1;
  min-width: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-rows: auto auto;
  column-gap: 10px;
  row-gap: 2px;
  align-items: center;
  padding: 10px 14px;
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-0);
  border-radius: 15px;
  overflow: hidden;
  cursor: pointer;
  transition: border-color var(--dur-micro) ease;
}
body.in-prepa .pw-card::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(120% 100% at 0% 0%, rgba(var(--cat-rgb, 255,255,255), .10), transparent 60%);
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-micro) ease;
}
body.in-prepa .pw-card[style*="--cat-rgb"]::before { opacity: 1; }
body.in-prepa .pw-card > * { position: relative; z-index: 1; }
body.in-prepa .pw-card:active { transform: scale(.98); transition: transform 80ms; }
/* Aujourd'hui : border teintée thème */
body.in-prepa .pw-row-today .pw-card {
  border-color: rgba(var(--theme-rgb), .35);
}

body.in-prepa .pw-num {
  grid-column: 1;
  grid-row: 1 / span 2;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 13px;
  /* C11 : catRgb() resout une couleur qui atteint TOUT JUSTE 4.5:1. Reappliquer
     un alpha .9 par-dessus la fait retomber sous le seuil (mesure 3.88 et 4.15
     en theme clair). Le solveur decide, le CSS ne rediscute pas. */
  color: rgb(var(--cat-rgb, 255,255,255));
}
body.in-prepa .pw-name {
  grid-column: 2;
  grid-row: 1;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 14px;
  color: #fff;
  text-transform: uppercase;
  letter-spacing: -.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
/* C30 : un ETAT n'est pas une mesure. Dans une rangee faite, le second jeton est une
   duree ou un decompte ; dans une rangee programmee, c'est un statut. L'italique les
   separe sans changer un mot ni la mise en page — c'est deja la convention de l'app
   pour ce qui n'est pas une donnee (le repos, « unbroken »). */
body.in-prepa .pw-state { font-style: italic; }
body.in-prepa .pw-sub {
  grid-column: 2;
  grid-row: 2;
  font-size: var(--fs-xs);
  color: var(--text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
/* Programmé (planned) — bordure teintée thème */
body.in-prepa .pw-card-planned {
  border-color: rgba(var(--theme-rgb), .25);
}
/* Compétition — carte dorée (surface + halo + border) */
body.in-prepa .pw-card-compet {
  border-color: rgba(255,215,0,.35);
}
body.in-prepa .pw-card-compet::before {
  content: '';
  position: absolute; inset: 0; border-radius: inherit;
  background: radial-gradient(120% 100% at 100% 0%, rgba(255,215,0,.14), transparent 60%);
  opacity: 1;
  pointer-events: none;
}
/* C17 : etait #ffd700 en dur — invisible ou presque sur une carte claire.
   Maintenant que c'est une icone qui suit currentColor, le token suffit. */
body.in-prepa .pw-num-compet { font-size: 15px; color: var(--warn-ink); }
body.in-prepa .pw-card-compet .pw-sub { color: rgba(255,215,0,.55); }
/* Manqué (planned + date passée) — opacité réduite */
body.in-prepa .pw-card-missed {
  /* C39 : etait `opacity: .5`, ce qui faisait tomber le nom du training a 3.57:1 et le
     sous-titre a 2.4:1 — on ne lisait plus ce qu'on avait manque, alors que c'est
     exactement l'information de cette carte.

     Defaut revele par le CHANGEMENT DE DATE, pas par une modification de code : a
     minuit, les seances programmees la veille sont devenues « manquees » et le banc a
     rencontre cet etat pour la premiere fois en un mois de travail.

     J'ai d'abord cherche la bonne valeur d'opacite — .7, puis .8, puis .88. Mauvaise
     methode : une opacite posee sur le PARENT degrade tout son contenu, et aucun enfant
     ne peut s'en extraire (contrairement a une couleur, qu'on peut surcharger). C'est le
     piege deja documente : un alpha annule la garantie du solveur de contraste.

     L'etat passe donc par la COULEUR, que le solveur sait garantir : surface en retrait,
     bordure neutre, et le numero de set qui perd sa couleur de categorie — un numero
     gris dit « inactif » aussi bien qu'un voile, sans rien rendre illisible. Le mot
     « manque » en italique porte le reste. */
  background: var(--surface-1);
  border-color: var(--border-0);
}
body.in-prepa .pw-card-missed .pw-num { color: var(--text-3); }
body.in-prepa .pw-card-missed .pw-name { color: var(--text-2); }

/* Cellule vide : "Repos" — dashed inerte */
body.in-prepa .pw-rest {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed var(--border-1);
  border-radius: 15px;
  font-size: 11px;
  color: var(--text-3);
  padding: 10px 14px;
  text-align: center;
}
body.in-prepa .pw-rest-future { color: var(--text-3); cursor: pointer; }
body.in-prepa .pw-rest-future:active { background: var(--surface-2); }

/* ══════════════════════════════════════════
   PRÉPA — bottom-sheet (picker + actions)
   Réutilise .modal-overlay (voile + z-index 500), panneau ancré en bas.
   PTR : les sheets sont sous .modal-overlay → couvertes par l'exclusion PTR existante.
   ══════════════════════════════════════════ */
.prepa-sheet-overlay {
  align-items: flex-end;
  padding: 0;
}
.prepa-sheet-overlay.visible { display: flex; }
.prepa-sheet {
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  background: linear-gradient(180deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-1);
  border-bottom: none;
  border-radius: 28px 28px 0 0;
  padding: 8px 20px max(24px, env(safe-area-inset-bottom, 24px));
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-height: 82vh;
  overflow: hidden;
  /* Apparition : keyframe dédiée (translateY seul) — la transition height n'est plus déclenchée
     dès l'apparition, évitant la saccade "apparaît puis se recale" observée sur la sheet date. */
  /* C112 : meme valeur qu'avant, mais par le jeton — une duree en dur qui vaut
     juste par hasard est une duree qui derivera. */
  animation: sheet-in var(--dur-v2) var(--ease-v2);
}
@keyframes sheet-in {
  from { transform: translateY(100%); }
  to   { transform: none; }
}
/* prefers-reduced-motion : plus d'anim d'entrée */
@media (prefers-reduced-motion: reduce) {
  .prepa-sheet { animation: none !important; }
}
/* Sheet étirée au focus recherche : hauteur FIXE (pas max-height).
   Sinon la sheet suivrait la hauteur du contenu et rétrécirait quand la recherche filtre à
   2 résultats → passerait sous le clavier. Height fixe → la liste (flex:1) prend tout l'espace
   restant, résultats restent en haut visibles au-dessus du clavier.
   Fallback : 100vh d'abord, 100dvh ensuite (support dynamic viewport si disponible). */
.prepa-sheet-overlay.sheet-expanded .prepa-sheet {
  height: calc(100vh - var(--safe-top) - 12px);
  height: calc(100dvh - var(--safe-top) - 12px);
  max-height: none;
}
/* Liste : flex:1 + min-height:0 en mode étiré → prend tout l'espace, header/recherche/créer sont
   flex-shrink:0 (déjà en place) donc jamais compressés. */
.prepa-sheet-overlay.sheet-expanded .prepa-sheet-list {
  flex: 1;
}
/* Android : padding-bottom qui suit la navigation gestuelle (pattern bottom-nav/FAB) */
body.platform-android .prepa-sheet {
  padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
}

/* Calendrier-sheet : hauteur PLEINE dès l'ouverture (même pattern que .sheet-expanded) */
.prepa-sheet-cal,
.prepa-sheet-goals {
  height: calc(100vh - var(--safe-top) - 12px);
  height: calc(100dvh - var(--safe-top) - 12px);
  max-height: none;
}
/* Réouverture "retour détail" : coupe l'anim sheet-in verticale (le fondu overlay reste). */
.prepa-sheet-cal.no-anim { animation: none !important; }

/* ══ Goals sheet — cycles + compétitions ══ */
.prepa-goals-content {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  display: block;
  padding: 4px 2px 8px;
}
.prepa-goals-content::-webkit-scrollbar { display: none; }

.pgs-section {
  margin-bottom: 22px;
}
.pgs-section-title {
  font-size: 11px; font-weight: 700;
  color: var(--text-3);
  letter-spacing: .12em; text-transform: uppercase;
  margin: 4px 4px 10px;
}
.pgs-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.pgs-row {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px;
  border-radius: 12px;
  background: var(--surface-2);
  border: 1px solid var(--border-0);
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}
.pgs-row:active { background: var(--surface-2); }
.pgs-row.editing {
  border-color: rgba(var(--theme-rgb), .5);
  background: rgba(var(--theme-rgb), .06);
}
.pgs-row-body { flex: 1; min-width: 0; }
.pgs-row-name {
  /* C25 : etait #fff en dur sur --surface-2, qui vaut #FFFFFF en theme clair —
     donc BLANC SUR BLANC. Vu sur l'appareil : dans la modale « Programmer un
     training », le titre etait absent et les NOMS DES TRAININGS invisibles. */
  font-size: 14px; font-weight: 700; color: var(--text-primary);
  letter-spacing: -.01em; line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pgs-row-meta {
  font-size: 11px; color: var(--text-3); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pgs-empty {
  font-size: 12px; color: var(--text-3);
  padding: 10px 14px; text-align: center;
}

.pgs-form { display: flex; flex-direction: column; gap: 10px; }
.pgs-field { display: flex; flex-direction: column; gap: 4px; }
.pgs-label {
  font-size: 11px; font-weight: 700;
  color: var(--text-3);
  letter-spacing: .1em; text-transform: uppercase;
  padding-left: 4px;
}
.pgs-input {
  width: 100%;
  min-height: 44px;
  padding: 12px 14px;
  border-radius: 14px;
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  /* C25 : etait #fff en dur sur --surface-2, qui vaut #FFFFFF en theme clair —
     donc BLANC SUR BLANC. Vu sur l'appareil : dans la modale « Programmer un
     training », le titre etait absent et les NOMS DES TRAININGS invisibles. */
  color: var(--text-primary);
  font-size: 16px; /* ≥16 pour éviter le zoom iOS */
  font-family: inherit;
  outline: none;
  transition: border-color .15s ease, background .15s ease;
  box-sizing: border-box;
}
.pgs-input::placeholder { color: var(--text-3); }
.pgs-input:focus {
  border-color: var(--theme-color);
  background: var(--surface-3);
}
.pgs-daterow { display: flex; gap: 8px; }
.pgs-daterow > .pgs-input { flex: 1; }
.pgs-daterow > .pgs-field { flex: 1; }
.pgs-colorpicker {
  display: flex; flex-wrap: wrap; gap: 8px;
  padding: 4px 2px;
}
.pgs-color {
  width: 26px; height: 26px; border-radius: 50%;
  cursor: pointer;
  border: 2px solid transparent;
  transition: transform .1s ease, border-color .15s ease;
  box-sizing: border-box;
}
.pgs-color:active { transform: scale(.9); }
.pgs-color.on { border-color: rgba(255,255,255,.9); }
.pgs-add-btn {
  width: 100%;
  min-height: 44px;
  padding: 12px 16px;
  border-radius: 14px;
  background: var(--gradient-accent);
  border: none;
  color: #fff;
  font-size: 14px; font-weight: 700;
  letter-spacing: .01em;
  cursor: pointer;
  transition: transform .1s ease, filter .15s ease;
}
.pgs-add-btn:active { transform: scale(.98); filter: brightness(1.05); }

/* Boutons secondaires en mode édition */
.pgs-danger-btn, .pgs-cancel-btn {
  width: 100%;
  min-height: 44px;
  padding: 12px 16px;
  border-radius: 14px;
  font-size: 14px; font-weight: 700;
  cursor: pointer;
  transition: transform .1s ease, background .15s ease;
}
.pgs-danger-btn {
  background: rgba(255,80,80,.14);
  border: 1px solid rgba(255,80,80,.3);
  color: #ff6b6b;
}
.pgs-danger-btn:active { transform: scale(.98); background: rgba(255,80,80,.22); }
.pgs-cancel-btn {
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  color: var(--text-2);
}
.pgs-cancel-btn:active { transform: scale(.98); background: var(--surface-3); }

/* Bouton "+ Ajouter" dashed — replie/déplie le formulaire (pattern .prepa-sheet-create) */
.pgs-add-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 12px 16px;
  background: transparent;
  border: 1px dashed rgba(var(--theme-rgb), .4);
  border-radius: 14px;
  color: var(--theme-color);
  font-size: 14px; font-weight: 600;
  cursor: pointer;
  transition: background var(--dur-micro) ease, transform .1s ease;
}
.pgs-add-toggle svg { width: 18px; height: 18px; }
.pgs-add-toggle:active { transform: scale(.98); background: rgba(var(--theme-rgb), .06); }
/* Zone scrollable : contient calendrier mois + journal accordéon.
   ⚠️ display:block (PAS flex column) — sinon les enfants héritent flex-shrink:1 et se compressent
   au lieu de scroller (bug jumeau de l'inbox commit 862fc88 : cards coupées, headers écrasés). */
.prepa-cal-content {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  display: block;
  padding: 2px 2px 8px;
}
.prepa-cal-content::-webkit-scrollbar { display: none; }
/* Espacements entre briques : nav, dow row, grille, accordéon — via margin (pas gap) */
.prepa-cal-content > .prepa-cal-monthblock { margin-bottom: 18px; }
.prepa-cal-monthblock > .prepa-cal-nav     { margin-bottom: 8px; }
.prepa-cal-monthblock > .prepa-cal-dowrow  { margin-bottom: 4px; }
.prepa-cal-content > .prepa-journal-month  { margin-bottom: 8px; }

/* Nav mois : ‹ Juillet 2026 › */
.prepa-cal-nav {
  display: flex; align-items: center; justify-content: space-between;
  padding: 2px 4px 0;
}
.prepa-cal-nav-btn {
  width: 34px; height: 34px; border-radius: 11px;
  background: var(--surface-2);
  border: 1px solid var(--border-0);
  color: var(--text-1);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: transform .1s ease, background .15s ease;
}
.prepa-cal-nav-btn:active { transform: scale(.94); background: var(--surface-3); }
.prepa-cal-nav-btn svg { width: 16px; height: 16px; }
.prepa-cal-nav-label {
  /* C25 : etait #fff en dur sur --surface-2, qui vaut #FFFFFF en theme clair —
     donc BLANC SUR BLANC. Vu sur l'appareil : dans la modale « Programmer un
     training », le titre etait absent et les NOMS DES TRAININGS invisibles. */
  font-size: 15px; font-weight: 700; color: var(--text-primary);
  letter-spacing: -.01em;
  text-transform: capitalize;
}

/* Rangée L M M J V S D */
.prepa-cal-dowrow {
  display: grid; grid-template-columns: repeat(7, 1fr);
  gap: 4px; padding: 0 2px;
}
.prepa-cal-dow {
  font-size: var(--fs-xs); font-weight: 700;
  color: var(--text-3);
  text-align: center;
  letter-spacing: .1em;
  padding-bottom: 2px;
}

/* Grille 7 colonnes — cellules carrées */
.prepa-cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
  padding: 0 2px;
}
.prepa-cal-cell {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: 11px;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 3px;
  color: var(--text-1);
  font-size: 13px; font-weight: 500;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background .12s ease;
}
.prepa-cal-cell:active { background: var(--surface-3); }
.prepa-cal-cell.prepa-cal-out { color: var(--text-3); }
.prepa-cal-cell.prepa-cal-today {
  background: rgba(var(--theme-rgb), .12);
  border-color: rgba(var(--theme-rgb), .4);
}
.prepa-cal-num { line-height: 1; }
.prepa-cal-markers {
  display: flex; align-items: center; justify-content: center;
  gap: 3px; min-height: 5px;
}
.prepa-cal-dot {
  width: 5px; height: 5px; border-radius: 50%;
  flex-shrink: 0;
}
.prepa-cal-dot-compet { background: #ffd700 !important; }

/* Traits de cycles en bas de cellule — 2px, ~70% largeur, empilés (max 2), pointer-events none.
   Positionné absolu → n'affecte pas le layout de la cellule (aspect-ratio 1/1 préservé). */
.prepa-cal-cycles {
  position: absolute;
  left: 15%; right: 15%;
  bottom: 4px;
  display: flex; flex-direction: column;
  gap: 1.5px;
  pointer-events: none;
}
.prepa-cal-cycle-trait {
  height: 2px;
  border-radius: 1px;
}
.prepa-cal-ring {
  width: 5px; height: 5px; border-radius: 50%;
  border: 1.4px solid var(--theme-color);
  background: transparent;
  box-sizing: border-box;
  flex-shrink: 0;
}

/* Journal — accordéon par mois (sous le calendrier). Anim = affichage direct + rotation chevron
   uniquement. Pas d'anim de hauteur : rendu paresseux → première ouverture snappy, mois 30+ cards
   jamais janky (max-height/grid-rows sur contenu variable → soit inexact, soit incompatible legacy).
   NB : le titre "Journal" est redondant avec le head de la sheet — supprimé côté JS. */
.prepa-journal-month {
  border-radius: 14px;
  background: var(--surface-2);
  border: 1px solid var(--border-0);
  overflow: hidden;
}
.prepa-journal-month-head {
  display: flex; align-items: center; justify-content: space-between;
  height: 48px;
  padding: 0 16px 0 14px;
  cursor: pointer;
  user-select: none;
  transition: background .12s ease;
}
.prepa-journal-month-head:active { background: var(--surface-2); }
.prepa-journal-month-label {
  /* C25 : etait #fff en dur — « Juillet 2026 » mesurait 1:1 en theme clair, donc
     litteralement invisible dans le journal du calendrier de prepa. */
  font-size: 14px; font-weight: 700; color: var(--text-primary);
  letter-spacing: -.01em;
  text-transform: capitalize;
}
.prepa-journal-month-count {
  font-size: 12px; font-weight: 500; color: var(--text-3);
  text-transform: none;
  margin-left: 2px;
}
.prepa-journal-chev {
  color: var(--text-3);
  display: flex; align-items: center; justify-content: center;
  transition: transform .28s var(--ease-v2);
}
.prepa-journal-chev svg { width: 16px; height: 16px; }
.prepa-journal-month.open .prepa-journal-chev { transform: rotate(180deg); }
.prepa-journal-month-body { display: none; }
.prepa-journal-month.open .prepa-journal-month-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 6px 8px 12px;
}
.prepa-sheet-grab {
  width: 40px;
  height: 4px;
  border-radius: 2px;
  background: var(--surface-4);
  margin: 4px auto 8px;
  flex-shrink: 0;
}
.prepa-sheet-head {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 0 4px 4px;
  flex-shrink: 0;
}
.prepa-sheet-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 18px;
  /* C25 : etait #fff en dur sur --surface-2, qui vaut #FFFFFF en theme clair —
     donc BLANC SUR BLANC. Vu sur l'appareil : dans la modale « Programmer un
     training », le titre etait absent et les NOMS DES TRAININGS invisibles. */
  color: var(--text-primary);
  letter-spacing: -.01em;
}
.prepa-sheet-subtitle {
  font-size: 13px;
  color: var(--text-3);
}

.prepa-sheet-search {
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  border-radius: 14px;
  /* C25 : etait #fff en dur sur --surface-2, qui vaut #FFFFFF en theme clair —
     donc BLANC SUR BLANC. Vu sur l'appareil : dans la modale « Programmer un
     training », le titre etait absent et les NOMS DES TRAININGS invisibles. */
  color: var(--text-primary);
  font-size: 16px; /* anti-zoom iOS */
  padding: 12px 14px;
  flex-shrink: 0;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none; /* override du glow legacy .form-input:focus (l.1225) */
}
/* Override explicite du focus legacy (.form-input:focus glow cyan) → focus V2 discret */
.prepa-sheet-search:focus {
  border-color: var(--theme-color);
  box-shadow: 0 0 0 3px rgba(255,255,255,.03);
  outline: none;
}
.prepa-sheet-search::placeholder { color: var(--text-3); }

.prepa-sheet-create {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 16px;
  background: transparent;
  border: 1px dashed rgba(var(--theme-rgb), .4);
  border-radius: 14px;
  color: var(--theme-color);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--dur-micro) ease;
  flex-shrink: 0;
}
.prepa-sheet-create svg { width: 18px; height: 18px; }
@media (hover: hover) {
  .prepa-sheet-create:hover { background: rgba(var(--theme-rgb), .08); }
}
.prepa-sheet-create:active { transform: scale(.97); }

.prepa-sheet-list {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 4px 0;
  scrollbar-width: none;
}
.prepa-sheet-list::-webkit-scrollbar { display: none; }

.prepa-pick-section {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--text-3);
  margin: 6px 0 2px;
}
.prepa-pick-empty {
  text-align: center;
  color: var(--text-3);
  font-size: 13px;
  padding: 24px 8px;
}

/* Items picker : structure IDENTIQUE à .pw-card du Prépa (grid 3 cols × 2 rows) — hauteur
   naturelle intrinsèque, jamais écrasée par le flex-shrink du parent .prepa-sheet-list.
   CAUSE de l'écrasement iPhone diagnostiquée : le grid 1-row × 3-cols avec .pp-body wrapper
   laissait la row height dépendre du `min-content` du .pp-body (nom+sub empilés en block).
   Sous flex-shrink parent + safe-area iOS, la ligne se compressait sous les hauteurs de police.
   Fix : grid 2 rows sans wrapper, chaque texte est une cellule DIRECTE (pas d'intermédiaire). */
.prepa-pick-item {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-rows: auto auto;
  column-gap: 10px;
  row-gap: 2px;
  align-items: center;
  padding: 11px 13px;
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--border-0);
  border-radius: 14px;
  cursor: pointer;
  overflow: hidden;
  flex-shrink: 0; /* défensif : jamais compressé par le parent flex */
  transition: border-color var(--dur-micro) ease;
}
.prepa-pick-item::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(120% 100% at 0% 0%, rgba(var(--cat-rgb, 255,255,255), .10), transparent 60%);
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-micro) ease;
}
.prepa-pick-item[style*="--cat-rgb"]::before { opacity: 1; }
.prepa-pick-item > * { position: relative; z-index: 1; }
.prepa-pick-item:active { transform: scale(.98); transition: transform 80ms; }

.prepa-pick-item .pp-num {
  grid-column: 1;
  grid-row: 1 / span 2;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 13px;
  /* C25 : etait rgba(..., .9). catRgb() resout la teinte pour atteindre 4.5:1 sur le fond
     du theme, mais un alpha applique APRES redilue la couleur vers ce fond et annule la
     garantie — on retombait a 3.84:1. Les trois autres usages de --cat-rgb (.06, .16, .20)
     sont des teintes de SURFACE, ou l'alpha est le principe meme ; ici c'est du TEXTE, et
     l'ecart entre .9 et 1 est imperceptible face a la lisibilite qu'il coutait. */
  color: rgb(var(--cat-rgb, 255,255,255));
  align-self: center;
}
.prepa-pick-item .pp-name {
  grid-column: 2;
  grid-row: 1;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 14px;
  /* C25 : etait #fff en dur sur --surface-2, qui vaut #FFFFFF en theme clair —
     donc BLANC SUR BLANC. Vu sur l'appareil : dans la modale « Programmer un
     training », le titre etait absent et les NOMS DES TRAININGS invisibles. */
  color: var(--text-primary);
  text-transform: uppercase;
  letter-spacing: -.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.prepa-pick-item .pp-sub {
  grid-column: 2;
  grid-row: 2;
  font-size: 12px;
  color: var(--text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.prepa-pick-item .pp-meta {
  grid-column: 3;
  grid-row: 1 / span 2;
  font-size: 11px;
  color: var(--text-3);
  white-space: nowrap;
  font-family: var(--font-body);
  align-self: center;
}

/* Sheet actions (compacte) — Voir / Déprogrammer / Annuler */
.prepa-sheet.prepa-sheet-actions { max-height: none; }
.prepa-sheet-actions-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.prepa-act {
  padding: 14px 16px;
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  border-radius: 14px;
  /* C25 : etait #fff en dur sur --surface-2, qui vaut #FFFFFF en theme clair —
     donc BLANC SUR BLANC. Vu sur l'appareil : dans la modale « Programmer un
     training », le titre etait absent et les NOMS DES TRAININGS invisibles. */
  color: var(--text-primary);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--dur-micro) ease;
  text-align: left;
}
@media (hover: hover) {
  .prepa-act:hover { background: var(--surface-3); }
}
.prepa-act:active { transform: scale(.98); }
.prepa-act-danger {
  color: var(--accent-red);
  background: rgba(255,71,87,.06);
  border-color: rgba(255,71,87,.2);
}
.prepa-act-cancel {
  background: transparent;
  border-color: var(--border-0);
  color: var(--text-2);
  text-align: center;
  margin-top: 4px;
}
.prepa-act-primary {
  background: var(--gradient-accent);
  border-color: transparent;
  color: var(--theme-text-on-accent, #fff);
  text-align: center;
  box-shadow: 0 8px 22px rgba(0,0,0,.35);
}

/* Quick actions items — icône + label alignés horizontalement, hauteur 48px */
.prepa-act.quick-act {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 12px 16px;
  text-align: left;
}
.prepa-act.quick-act svg {
  width: 18px;
  height: 18px;
  color: var(--text-2);
  flex-shrink: 0;
}
#quickActionsTitle {
  /* C67 : `color` et `font-size` retires d'ici. Ce bloc affiche un NOM DE
     TRAINING, et un selecteur d'ID (1,0,0) bat toute classe : tant que la
     typographie etait declaree ici, `.nom-training` ne pouvait PAS s'appliquer
     — c'est ce qui laissait le nom en 12px gris maigre, dans l'apparence d'une
     legende. On ne surencherit pas en specificite, on retire la declaration de
     la ou elle n'a pas lieu d'etre. Reste `text-align`, qui releve de la mise
     en page et non de la voix typographique. */
  text-align: left;
}

/* Date-picker sheet (compacte : label + input date + boutons) */
.prepa-sheet-datepicker { max-height: none; }
.prepa-sheet-datepicker-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.prepa-date-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-3);
}
#prepaScheduleDateInput {
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  border-radius: 14px;
  /* C25 : etait #fff en dur. Aucun override de theme ne pouvait le rattraper —
     un selecteur d'id pese (1,0,0), les overrides clairs sont en (0,1,1). Corrige
     ICI, a la source, jamais par une regle ajoutee en fin de fichier. */
  color: var(--text-primary);
  font-size: 16px; /* anti-zoom iOS */
  padding: 12px 14px;
  min-height: 44px; /* cible tactile confortable (iOS/Android HIG) */
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none; /* override du glow legacy .form-input:focus (l.1225) */
}
#prepaScheduleDateInput:focus {
  border-color: var(--theme-color);
  box-shadow: 0 0 0 3px rgba(255,255,255,.03);
  outline: none;
}

/* C43 — RANGÉE D'ACTIONS DU HERO DÉTAIL.
   Avant : la seule action visible sans scroller était « Programmer », c'est-à-dire
   remettre à plus tard. Le vrai départ de séance vivait sous tout le programme.
   Maintenant : « Démarrer » en primaire, « Programmer » (ou la ligne « Programmé : … »)
   en secondaire, sur la même rangée. `flex-wrap` parce qu'une ligne « Programmé :
   lun. 3 · jeu. 6 · sam. 8 » peut être longue : elle passe dessous plutôt que de
   comprimer le bouton primaire. */
body.in-detail .detail-hero-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 12px;
  align-self: flex-start;
}
/* Les deux enfants portent chacun `margin-top: 10px` et `align-self: flex-start`,
   hérités de l'époque où ils étaient seuls dans une colonne flex. Dans une rangée,
   le premier les décale et le second les colle en haut au lieu de les centrer. */
body.in-detail .detail-hero-actions > * { margin-top: 0; align-self: center; }

/* ⚠ C101 — LE BOUTON PERD SON LIBELLE, PAS SA RAISON D'ETRE.
   Signale a l'usage : « le "Demarrer" me gene ». Le pave a gradient occupait la
   rangee entiere et ecrasait la ligne « Programme : … » a cote.

   IL N'EST PAS SUPPRIME, ET C'EST DELIBERE. C43 l'a ajoute apres avoir mesure
   le defaut inverse : sans lui, la seule action visible en haut du Detail etait
   « Programmer », c'est-a-dire FAIRE PLUS TARD, et lancer une seance demandait
   de scroller sous tout le programme — deux ecrans sur un workout long. Le
   retirer rejouerait ce defaut a l'identique. On reduit son POIDS, pas sa
   presence.

   Pastille ronde de 48px, poussee a droite de la rangee : la ligne
   « Programme : … » prend la largeur, l'action ferme la ligne. 48 et non 44 —
   une icone seule sans libelle offre moins de reperes visuels qu'un bouton
   texte, elle merite un peu plus de surface.

   `order: 2` plutot que de deplacer le noeud : la ligne de programmation est
   ajoutee APRES le bouton par `_prepaRefreshDetailScheduledLine`, et changer
   l'ordre du DOM aurait demande de toucher a cette fonction pour un effet
   purement visuel. */
body.in-detail .detail-hero-actions { width: 100%; }
body.in-detail .detail-start-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  order: 2;
  margin-left: auto;
  width: 48px; height: 48px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--gradient-accent);
  color: var(--theme-text-on-accent, #fff);
  cursor: pointer;
  transition: transform 80ms ease, filter var(--dur-micro) ease;
}
body.in-detail .detail-start-btn svg {
  /* C107 : 17px -> 24px, et le viewBox fait 24 — une unite vaut donc un pixel,
     ce qui rend les coordonnees de `_heroStartIcone` lisibles telles quelles. */
  width: 24px; height: 24px; flex-shrink: 0;
  fill: currentColor; stroke: none;   /* glyphe plein : `svg { fill:none }` gagnerait sinon */
  /* Plus de `margin-left` : le decalage optique du triangle est passe dans ses
     coordonnees (cf. `_heroStartIcone`). En marge il s'appliquait a la boite,
     donc aussi aux barres de pause, et il fallait une regle de plus pour l'y
     annuler. */
}
/* Le contour arrondi ne vise QUE le triangle : pose sur le <svg>, il
   epaissirait aussi les barres de pause, qui tiennent leur douceur de leur
   propre `rx`. Meme technique et meme rondeur (20% de la largeur) que
   `.sw-play-icon polygon` du Timer — c'est de la que vient le chiffre. */
body.in-detail .detail-start-btn svg polygon {
  stroke: currentColor; stroke-width: 2.5; stroke-linejoin: round;
}
body.in-detail .detail-start-btn:active { transform: scale(.96); }

/* Lancé (décompte ou en cours) : le bouton cesse d'inviter. Il devient un simple rappel
   de l'état — même teinte que le chrono, mais en tenue discrète, pour qu'il ne rivalise
   pas avec le chrono lui-même qui est, lui, la vraie zone de contrôle.
   `paused` garde exprès la tenue pleine : là, il y a bien quelque chose à faire. */
body.in-detail .detail-start-btn[data-state="running"],
body.in-detail .detail-start-btn[data-state="countdown"] {
  background: rgba(var(--theme-rgb), .14);
  border: 1px solid rgba(var(--theme-rgb), .3);
  color: var(--theme-color);
  /* C101 : la pastille est a taille fixe, `box-sizing: border-box` absorbe donc
     le bord de 1px — plus besoin de compenser le padding comme quand le bouton
     etait dimensionne par son texte. */
  box-sizing: border-box;
}

/* Ligne "📅 Programmé : ven. 10 juil." dans le hero détail — cliquable → ouvre Prépa
   pile sur la semaine de la 1re date programmée. */
body.in-detail .detail-scheduled-line {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  padding: 6px 10px;
  background: rgba(var(--theme-rgb), .1);
  border: 1px solid rgba(var(--theme-rgb), .25);
  border-radius: 10px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-1);
  align-self: flex-start;
  cursor: pointer;
  transition: background var(--dur-micro) ease;
}
body.in-detail .detail-scheduled-line:active { transform: scale(.96); transition: transform 80ms; }
@media (hover: hover) {
  body.in-detail .detail-scheduled-line:hover { background: rgba(var(--theme-rgb), .14); }
}
body.in-detail .detail-scheduled-line svg {
  color: var(--theme-color);
  flex-shrink: 0;
}

/* Chip "📅 Programmer" — affiché en mode ÉDITION si aucune programmation à venir.
   Style pilule dashed cohérent avec la ligne (même famille visuelle) mais tonalité neutre
   (pas de couleur thème forte : c'est une invitation discrète, pas un état actif). */
body.in-detail .detail-schedule-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  padding: 6px 10px;
  background: transparent;
  border: 1px dashed var(--border-2);
  border-radius: 10px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  align-self: flex-start;
  cursor: pointer;
  transition: background var(--dur-micro) ease, border-color var(--dur-micro) ease, color var(--dur-micro) ease;
}
body.in-detail .detail-schedule-chip:active { transform: scale(.96); transition: transform 80ms; }
@media (hover: hover) {
  body.in-detail .detail-schedule-chip:hover {
    background: var(--surface-2);
    border-color: var(--border-2);
    color: var(--text-2);
  }
}
body.in-detail .detail-schedule-chip svg {
  color: var(--text-2);
  flex-shrink: 0;
}

/* Historique — bouton retour scopé (l'historique n'est plus accessible via la nav) */
body.in-history .history-back-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 10px;
  background: var(--surface-2);
  border: 1px solid var(--border-1);
  color: var(--text-2);
  padding: 0;
  margin-right: 12px;
  cursor: pointer;
  transition: background var(--dur-micro) ease, color var(--dur-micro) ease;
  touch-action: manipulation;
  flex-shrink: 0;
}
@media (hover: hover) {
  body.in-history .history-back-btn:hover {
    background: var(--surface-3);
    color: #fff;
  }
}
body.in-history .history-back-btn:active { transform: scale(.9); }
body.in-history .history-back-btn svg { width: 15px; height: 15px; }

/* Voile fondu haut : le fade sur fond noir est invisible mais aide au blur du contenu qui scroll
   sous la safe-area. Pas de conflit — le support-chat-header a son propre safe-area padding. */

/* ══════════════════════════════════════════
   MODE DÉMO / INVITÉ (accès sans compte)
   ══════════════════════════════════════════ */
.auth-demo-link {
  text-align: center;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-muted);
  padding: 8px;
  transition: color 0.2s;
  width: 100%;
}
@media (hover: hover) {
  .auth-demo-link:hover { color: var(--accent-blue); }
}

/* Bandeau flottant : masqué par défaut, révélé uniquement en mode invité */
.demo-banner { display: none; }
body.demo-mode .demo-banner {
  position: fixed;
  left: 50%;
  bottom: calc(88px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 7px 7px 7px 16px;
  /* C36 : la pastille est un aplat quasi noir, pense pour un fond CLAIR — ou elle
     ressort a 16.8:1. Sur le fond sombre, qui est pourtant le theme par defaut, elle
     tombe a 1.01:1 : invisible. Le bandeau de demo, c'est-a-dire precisement l'ecran
     qu'on montre a quelqu'un d'autre, ne s'affichait donc pas.
     Trouve par test-parite-themes.js, pas a l'oeil : il n'y a aucun texte a mesurer
     ici, donc le scan de contraste ne pouvait rien en dire.
     Une pastille flottante se detache par son CONTOUR, pas par son fond — les jetons
     de surface du projet sont tous a ~1.1:1 de leur fond, c'est la bordure qui fait le
     relief. On garde donc l'aplat et on lui donne un contour lisible en sombre. */
  background: rgba(10, 11, 26, 0.97);
  border: 1px solid rgba(255,255,255,0.22);
  box-shadow: 0 8px 28px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.04);
  border-radius: 999px;
  z-index: 401;
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  max-width: calc(100vw - 32px);
}
.demo-banner-label {
  font-size: 0.82rem;
  font-weight: 600;
  color: #fff;
  opacity: 0.85;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.demo-banner-cta {
  background: var(--gradient-accent);
  color: #fff;
  border: none;
  border-radius: 999px;
  padding: 7px 15px;
  font-size: 0.82rem;
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.demo-banner-cta:active { transform: scale(0.96); }

/* Masquer le bandeau là où la bottom-nav est masquée (évite tout chevauchement) */
body.demo-mode.in-create   .demo-banner,
body.demo-mode.in-settings .demo-banner,
body.demo-mode.in-support  .demo-banner,
body.demo-mode.in-account  .demo-banner,
body.demo-mode.in-detail   .demo-banner,
body.demo-mode.in-timer    .demo-banner,
body.demo-mode.keyboard-open .demo-banner,
body.demo-mode.modal-open  .demo-banner,
body.demo-mode.share-mode  .demo-banner { display: none; }

/* Masquer les fonctions backend-only en mode invité (inbox, support, compte) */
body.demo-mode .topbar-inbox-btn,
body.demo-mode #inboxNavBtn,
body.demo-mode .nav-link[data-view="support"],
body.demo-mode .profile-help-pill,
body.demo-mode .profile-danger-section { display: none !important; }

/* ══════════════════════════════════════════════════════════════
   THÈME CLAIR — overrides ciblés (additifs, n'affectent QUE data-theme=light,
   zéro régression sombre). Construits vue par vue.
   ══════════════════════════════════════════════════════════════ */
/* — Galerie — (préfixe body.in-gallery pour battre la spécificité des règles sombres) */
[data-theme="light"] body.in-gallery .cat-title { color: var(--text-primary); }
[data-theme="light"] body.in-gallery .week-summary { color: var(--text-secondary); }
/* Refonte : le hero (dh-*) est un dégradé foncé en clair AUSSI → son texte reste blanc (base conservée).
   Seules les cartes (cc-*), blanches en clair, passent en texte foncé. */
[data-theme="light"] body.in-gallery .cc-name,
[data-theme="light"] body.in-gallery .cc-sv { color: var(--text-secondary); }
[data-theme="light"] body.in-gallery .cc-last { color: var(--text-secondary); }
[data-theme="light"] body.in-gallery .cc-sl { color: var(--text-muted); }
/* C43 : cette regle visait `.cc-fav` tout court. Elle a la MEME specificite (0,3,1) que
   `body.in-gallery .cc-fav.on` (l. ~6677) et se trouve plus bas dans le fichier : a
   specificite egale la derniere gagne, donc en clair le favori perdait sa couleur et
   devenait identique au non-favori. Le `:not(.on)` ne monte pas la specificite pour
   gagner une course — il dit ce que la regle veut vraiment dire : teinter l'etat ETEINT. */
[data-theme="light"] body.in-gallery .cc-fav:not(.on) { color: var(--text-muted); }

/* — Détail — */
[data-theme="light"] body.in-detail .detail-title,
/* C53 : `:not(.detail-stat-val-est)` — les kcal sont une estimation et gardent
   leur couleur attenuee en clair aussi. Sans ce restreint, cette regle (0,3,1)
   ecrasait la regle d'estimation et le Detail redevenait le seul ecran ou
   l'estimation se lisait comme une mesure. */
[data-theme="light"] body.in-detail .detail-stat-val:not(.detail-stat-val-est),
[data-theme="light"] body.in-detail .detail-workout-header,
[data-theme="light"] body.in-detail .detail-workout-header span { color: var(--text-primary); }
[data-theme="light"] body.in-detail .detail-last,
[data-theme="light"] body.in-detail .detail-objective,
[data-theme="light"] body.in-detail .detail-hero-cat,
[data-theme="light"] body.in-detail .detail-hero-meta2 { color: var(--text-secondary); }
[data-theme="light"] body.in-detail .itimer-toplabel,
[data-theme="light"] body.in-detail .detail-stat-lbl,
[data-theme="light"] body.in-detail .detail-stat-label { color: var(--text-muted); }

/* — Prépa — */
[data-theme="light"] body.in-prepa .pcc-name,
[data-theme="light"] body.in-prepa .prepa-week-label,
[data-theme="light"] body.in-prepa .pw-day-num,
[data-theme="light"] body.in-prepa .pw-name { color: var(--text-primary); }
/* C24 : cet override et la regle « aujourd'hui » (body.in-prepa .pw-row-today
   .pw-day-num, l.8359) ont EXACTEMENT la meme specificite (0,3,1) — c'est donc la
   derniere du fichier qui gagne, et c'est l'override. Resultat : le marqueur du
   jour disparaissait en theme clair, le 30 s'affichait noir comme les autres.
   Aucun scanner de contraste ne peut voir ca : les deux couleurs passent le seuil,
   c'est l'INFORMATION qui est perdue. */
[data-theme="light"] body.in-prepa .pw-row-today .pw-day-num { color: var(--accent); }
[data-theme="light"] body.in-prepa .pcc-meta,
[data-theme="light"] body.in-prepa .pw-sub { color: var(--text-secondary); }
/* Refonte : le doré (#ffd700) du compte à rebours J-xx est illisible sur la bannière claire → doré foncé */
[data-theme="light"] body.in-prepa .pcc-j { color: #b8730a; }
/* C11 : override devenu inutile — --warn bascule deja tout seul. Aligne dessus
   pour ne pas laisser une valeur en dur battre le token par specificite. */
[data-theme="light"] body.in-prepa .pcc-tag { color: var(--warn-ink); }
[data-theme="light"] body.in-prepa .pcb-week,
[data-theme="light"] body.in-prepa .pw-day-abbr,
[data-theme="light"] body.in-prepa .pw-rest { color: var(--text-muted); }

/* — PR — */
[data-theme="light"] body.in-pr .pr-tab-bar { background: rgba(12,22,52,.05); border-color: rgba(12,22,52,.08); }
[data-theme="light"] body.in-pr .pr-tab { color: var(--text-secondary); }
/* Refonte : onglet actif = pastille blanche + texte foncé (avant : blanc sur blanc illisible) */
[data-theme="light"] body.in-pr .pr-tab.active { background: var(--surface); color: var(--text-primary); box-shadow: 0 2px 8px rgba(20,28,60,.10); }

/* — Profil — */
[data-theme="light"] body.in-profile .profile-display-name,
[data-theme="light"] body.in-profile .stat-value:not(.stat-value-est),
[data-theme="light"] body.in-profile .profile-physio-title,
[data-theme="light"] body.in-profile .pfc-title { color: var(--text-primary); }
[data-theme="light"] body.in-profile .profile-physio-label { color: var(--text-secondary); }

/* — Settings — */
[data-theme="light"] body.in-settings .settings-section-title,
[data-theme="light"] body.in-settings .category-label,
[data-theme="light"] body.in-settings .theme-label,
[data-theme="light"] body.in-settings .btn-secondary { color: var(--text-primary); }
[data-theme="light"] body.in-settings .settings-tab { color: var(--text-secondary); }
/* Refonte : onglet réglages actif = pastille blanche + texte foncé (avant blanc sur blanc) */
[data-theme="light"] body.in-settings .settings-tab.active { background: var(--surface); color: var(--text-primary); box-shadow: 0 2px 8px rgba(20,28,60,.10); }

/* — Create — */
[data-theme="light"] body.in-create .btn-secondary { color: var(--text-primary); }
[data-theme="light"] body.in-create .autocomplete-toggle-label { color: var(--text-secondary); }

/* — Formulaires (labels/selects) globaux en clair — */
[data-theme="light"] .form-group label,
[data-theme="light"] .form-label { color: var(--text-secondary); }
[data-theme="light"] select option { color: #0e1017; }

/* — Timer — (la vue timer utilise body.on-timer, PAS in-timer) */
[data-theme="light"] body.on-timer .sw-mode-btn { color: var(--text-secondary); }
[data-theme="light"] body.on-timer .sw-mode-btn.active { background: rgba(12,22,52,.08); color: var(--text-primary); }
[data-theme="light"] body.on-timer .sw-drum-item.sel,
[data-theme="light"] body.on-timer .sw-pick-btn,
[data-theme="light"] body.on-timer .sw-pick-val,
[data-theme="light"] body.on-timer .sw-view-round-btn,
[data-theme="light"] body.on-timer .sw-time-display { color: var(--text-primary); }
[data-theme="light"] body.on-timer .sw-picker-label { color: var(--text-muted); }
[data-theme="light"] body.on-timer .sw-drum-indicator {
  background: rgba(12,22,52,.05);
  border-color: rgba(12,22,52,.12);
  border-top-color: rgba(12,22,52,.16);
  border-bottom-color: rgba(12,22,52,.16);
}
[data-theme="light"] body.in-timer .sw-round-label,
[data-theme="light"] body.in-timer .sw-hint,
[data-theme="light"] body.on-timer .sw-round-label,
[data-theme="light"] body.on-timer .sw-hint { color: var(--text-2); }

/* — Compléments oubliés (galerie / détail / support / prépa) — */
[data-theme="light"] body.in-gallery .week-summary b { color: var(--text-primary); }
[data-theme="light"] body.in-gallery .cat-count { color: var(--text-muted); }
[data-theme="light"] body.in-detail .d-spark-tally { color: var(--text-secondary); }
[data-theme="light"] body.in-support .support-back-btn { color: var(--text-primary); }
[data-theme="light"] body.in-prepa .pw-card-compet .pw-sub { color: var(--text-secondary); }
[data-theme="light"] body.in-prepa .pw-rest-future { color: var(--text-muted); }

/* — Écran de connexion (Auth) — */
[data-theme="light"] .auth-container {
  background: rgba(255,255,255,0.94);
  border-color: var(--border);
  box-shadow: 0 24px 60px rgba(20,28,60,0.14), inset 0 1px 0 rgba(255,255,255,0.7);
}
[data-theme="light"] .auth-tab.active { background: #ffffff; color: var(--text-primary); box-shadow: 0 2px 8px rgba(20,28,60,0.12); }
[data-theme="light"] .auth-form label { color: var(--text-secondary); }
[data-theme="light"] .form-input,
[data-theme="light"] .auth-form .form-input,
[data-theme="light"] select.form-input,
[data-theme="light"] textarea.form-input {
  background: #f4f6fb;
  border: 1px solid var(--border);
  color: var(--text-primary);
}
[data-theme="light"] .form-input::placeholder { color: var(--text-muted); }

/* — Thème clair, passe 2 : éléments multi-lignes manqués (scan systématique) — */
/* Refonte : "Bien joué" reste blanc sur le hero dégradé (clair aussi) */
[data-theme="light"] body.in-gallery .dh-cheer { color: rgba(255,255,255,.7); }
[data-theme="light"] body.in-detail .detail-hero-cat .detail-badge { color: var(--text-secondary); }
[data-theme="light"] body.in-detail .d-spark-head,
[data-theme="light"] body.in-detail .d-spark-lbl { color: var(--text-muted); }
[data-theme="light"] body.in-detail .detail-dropdown-item { color: var(--text-primary); }
[data-theme="light"] body.in-detail .detail-schedule-chip svg { color: var(--text-secondary); }
[data-theme="light"] body.in-profile .physio-field label { color: var(--text-secondary); }
[data-theme="light"] body.in-prepa .wnav { color: var(--text-secondary); }
[data-theme="light"] body.in-support .support-msg-label,
[data-theme="light"] body.in-support .support-msg-time { color: var(--text-muted); }

/* — Thème clair, passe 3 : OMBRES & BORDURES des cartes —
   Les box-shadow noires en dur (rgba(0,0,0,.4-.5)) faites pour le fond noir
   deviennent des halos gris sur blanc. On les remplace par des ombres douces. */
[data-theme="light"] body.in-gallery .cat-card,
[data-theme="light"] body.in-gallery .day-hero,
[data-theme="light"] body.in-detail .detail-hero {
  box-shadow: 0 6px 20px rgba(20,28,60,.08);
  border-color: rgba(12,22,52,.08);
}
/* Refonte : ombre diffuse et douce en clair aussi (ni trait ni cadre).
   C61 : 8/18 -> 3/10, meme raison qu'en sombre. Dans un carrousel les cartes
   etaient isolees ; dans une grille a 12px d'ecart, 18px de flou debordent sur
   la voisine et les deux ombres se cumulent entre les colonnes — ca fait une
   trainee grise au milieu de la grille au lieu de cartes posees. */
[data-theme="light"] body.in-gallery .cat-card { background: #ffffff; box-shadow: 0 3px 10px rgba(20,28,60,.10); border-color: rgba(12,22,52,.06); }
[data-theme="light"] body.in-gallery .day-hero {
  /* C116 : meme portee que le sombre (30px), pour la meme raison — le pager
     coupait les 2 derniers pixels. Voir le bloc `body.in-gallery .day-hero`. */
  box-shadow: 0 8px 22px rgba(79,50,220,.22);
  border-color: rgba(255,255,255,.16);
}
[data-theme="light"] body.in-pr .pr-detail-best,
[data-theme="light"] body.in-pr .pr-detail-chart-wrap {
  box-shadow: 0 6px 20px rgba(20,28,60,.08);
}
/* Popups / overlays : ombre plus douce en clair */
[data-theme="light"] .modal,
[data-theme="light"] .detail-dropdown,
[data-theme="light"] body.in-detail .detail-dropdown,
[data-theme="light"] .inline-dropdown,
[data-theme="light"] .pr-suggestions {
  box-shadow: 0 16px 40px rgba(20,28,60,.16);
}

/* — Thème clair, passe 4 : MODALES (.modal bascule en surface blanche en clair) — */
[data-theme="light"] .modal-footer .btn-secondary { background: rgba(12,22,52,.05); color: var(--text-primary); }
/* C25 : #addPRModal ajoute ici. Cette ligne ne couvrait que #editPRModal, si bien que
   « Nouveau record » gardait un fond #FFFFFF pendant que « Modifier » passait en #f4f6fb —
   deux modales jumelles qui ne se ressemblaient plus en theme clair. */
[data-theme="light"] #addPRModal .form-input,
[data-theme="light"] #editPRModal .form-input { background: #f4f6fb; border-color: var(--border); color: var(--text-primary); }
[data-theme="light"] #editPRModal .pr-type-btn { color: var(--text-secondary); }
/* Refonte : toggle Force/Cardio actif lisible en clair (addPR ET editPR) */
[data-theme="light"] #addPRModal .pr-type-btn.active,
[data-theme="light"] #editPRModal .pr-type-btn.active { background: var(--surface); color: var(--text-primary); box-shadow: 0 2px 8px rgba(20,28,60,.10); }
[data-theme="light"] #editPRModal .pr-suggestion-item { color: var(--text-primary); }
[data-theme="light"] .inbox-item-header,
[data-theme="light"] .inbox-item-name { color: var(--text-primary); }
[data-theme="light"] .inbox-date,
[data-theme="light"] .inbox-item-cat,
[data-theme="light"] .inbox-item-diff,
[data-theme="light"] .inbox-item-notes,
[data-theme="light"] .inbox-item-stats-inline { color: var(--text-secondary); }

/* — Thème clair, passe 5 : Support (bulle admin = surface claire, texte était blanc) — */
[data-theme="light"] body.in-support .support-msg-admin .support-bubble { color: var(--text-primary); }
[data-theme="light"] body.in-support .support-input-bar .form-input::placeholder { color: var(--text-muted); }

/* — Thème clair : icônes des boutons header (enveloppe, trophée, calendrier…) — */
[data-theme="light"] .hdr-btn,
[data-theme="light"] .topbar-inbox-btn,
[data-theme="light"] .prepa-cal-nav-btn { color: var(--text-secondary); }
[data-theme="light"] body.in-prepa .prepa-cal-cell.prepa-cal-out { color: var(--text-muted); }

/* ══ Chiffres tabulaires ═════════════════════════════════════════════════
   Sans ça, "1755" et "132" ne s'alignent pas verticalement d'une carte à
   l'autre : c'est une ligne de CSS qui change la perception de rigueur de
   toute l'app. */
.cc-sv, .dh-sv, .detail-stat-val, .detail-stat-value, .pr-value, .pr-val,
.stat-value, .stat-tile-value, .sw-time, .itimer-time, .pcc-j, .pw-day-num,
.cc-num, .detail-num, .prepa-cal-cell, .week-summary b, .profile-stat-value {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ══ Accessibilité ═══════════════════════════════════════════════════════ */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
/* ══ C112 — LE PLANCHER GLOBAL, ET L'EXCEPTION QUI LUI MANQUAIT ══════════════
   ⚠ D'ABORD, UNE ERREUR DE DIAGNOSTIC QUI VAUT D'ETRE GARDEE.
   J'ai releve l'etat de chaque surface sous « reduire les animations » et
   conclu que `slide-up` — donc QUATORZE modales — ignorait la preference. FAUX.
   Je lisais `animationName`, qui vaut toujours « slide-up » : c'est la DUREE
   que la regle ci-dessous ramene a 0,01 ms. Le nom d'une animation ne dit pas
   si elle se voit. Il fallait lire `animationDuration`.
   Ce plancher existait donc deja, et il fonctionnait.

   ⚠ CE QUI ETAIT REELLEMENT CASSE : `animation-iteration-count: 1` arrete AUSSI
   les indicateurs de chargement. Mesure, systeme sur « reduire » :

       .auth-spinner     0,00001s / 1 tour
       .ai-loader-ring   0,00001s / 1 tour
       .ptr-spin         0,00001s / 1 tour

   Un spinner fige ne se lit pas « animation reduite », il se lit « l'app a
   plante » — et c'est le seul element a l'ecran pendant qu'on attend le reseau.
   La preference demande de supprimer le mouvement DECORATIF, pas de supprimer
   le retour d'information.

   Les quatre sont nommes explicitement plutot que devines : releves au grep des
   animations infinies du fichier. Toutes les autres boucles — halos d'ambiance,
   pulsation du logo, reflet « nouveau », compte a rebours du jour J —
   s'arretent, et c'est bien ce qu'on veut. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .auth-spinner, .splash-ring, .ai-loader-ring, .ptr-spin {
    animation-duration: .8s !important;
    animation-iteration-count: infinite !important;
  }
}

/* ══ Programme du training — REVERT INTEGRAL AU BUILD 5 ═══════════════════
   Il y avait ici une grille alignée à gauche : lignes en flex, colonnes
   numériques à chasse fixe, séparateurs entre les lignes, charge repoussée
   à droite. Je l'avais défendue comme « 100 % CSS, le renderer n'est pas
   touché » — c'était vrai au sens strict et hors sujet : le renderer produit
   un rendu, et c'est ce rendu que Chris avait demandé de laisser tel quel.
   Le programme du training est la partie de l'app qu'il connaît par cœur et
   qu'il n'a jamais demandé à changer. Bloc retiré en entier, retour au
   centrage d'origine. Ne pas le réintroduire sans demande explicite. */

/* ══ Hero du jour — diagonale de lecture ══════════════════════════════════
   Avant : 5 blocs centrés empilés, 5 largeurs différentes, 5 axes optiques.
   L'œil n'avait nulle part où se poser. Ici un seul axe à gauche, le numéro
   en marqueur de coin, et les chiffres en bas — un seul balayage diagonal.
   Fait en CSS seul : l'ordre DOM est conservé, c'est `order` qui recompose. */
body.in-gallery .day-hero {
  text-align: left;
  align-items: stretch;
  justify-content: flex-start;
  padding: 16px 18px;
}
body.in-gallery .dh-chip  { order: 1; align-self: flex-start; margin: 0 0 auto; }
/* C41 : 30 -> 33px. Une condensee se lit plus petit a corps egal — c'est le principe
   meme d'une chasse etroite. Sans compensation, le passage a Barlow Condensed aurait
   fait PERDRE de la presence au titre alors que c'est le sujet de la carte.
   33px est mesure, pas choisi : c'est la derniere valeur ou « ENDURANCE ATLAS FEMALE »
   (22 caracteres, le nom qui a revele tous les defauts du hero) tient encore sur une
   seule ligne. A 36px il repasse a deux. */
body.in-gallery .dh-name  { order: 3; margin: 10px 0 0; font-size: 33px; letter-spacing: -.025em; }
/* C33 : la pastille de statut occupait une ligne entiere pour elle seule (79px de
   large sur 326 disponibles), pendant que la ligne des chiffres n'occupait que 44% de
   la largeur et laissait 184px de vide a sa droite. Deux demi-lignes qui se plaignaient
   l'une de l'autre.
   Elle passe en ancrage bas-droite, sur la meme ligne que REPS et KCAL : le vide de
   droite se remplit avec quelque chose qui etait deja la, et la ligne liberee rend 31px
   de hauteur a la carte. Rien n'est ajoute, tout est mieux place.
   Sortie du flux (absolute) : c'est ce qui rend les 31px. La carte est deja
   position:relative, donc rien d'autre a ancrer — verifie, pas suppose. */
body.in-gallery .dh-last  { order: 4; position: absolute; right: 18px; bottom: 16px; margin: 0;
  /* Garde-fou : la pastille est hors flux, donc rien ne l'empeche mecaniquement de
     recouvrir les chiffres. C'est arrive des le premier essai avec « ✓ 3 séances
     terminées aujourd'hui » (212px, 28px de recouvrement). Les textes ont ete
     raccourcis, mais un plafond de largeur evite qu'un libelle futur ne rouvre le
     probleme en silence : au pire il s'ellipse, il ne recouvre jamais. */
  max-width: 45%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.in-gallery .dh-stats { order: 5; justify-content: flex-start; gap: 28px;
                            width: auto; max-width: none; margin: auto 0 0; }
body.in-gallery .dh-stat  { text-align: left; }

/* Le numéro devient un marqueur de coin : il ne concurrence plus le titre. */
body.in-gallery .dh-num {
  order: 2; position: absolute; top: 16px; right: 18px; margin: 0;
  /* ⚠ C113 : DEUXIÈME déclaration de `body.in-gallery .dh-num` — l'autre est
     ~3300 lignes plus haut, MÊME spécificité, donc c'est celle-ci qui gagne par
     l'ordre. Corriger l'autre seule ne faisait rien ; mesuré. Le « #1 » est un
     marqueur de coin, pas une information : il reste au plancher des unités,
     comme le dit le commentaire juste au-dessus. */
  font-size: var(--fs-micro); letter-spacing: .1em;
  /* C35 : la pastille a voile sombre est retiree. Le commentaire ci-dessus dit que le
     numero doit etre un marqueur de coin qui ne concurrence pas le titre — or l'entourer
     d'un cadre sombre le rendait plus present, pas moins. C'etait la 3e pastille de la
     carte, avec le chip de categorie et le statut : trois objets pour trois broutilles.
     Reste le chiffre seul, a 80% de blanc (4.55:1 sur le degrade approfondi, donc
     lisible), soit un objet de moins et une carte plus calme. */
  background: none; border: none; padding: 0; backdrop-filter: none; -webkit-backdrop-filter: none;
  color: rgba(255,255,255,.80);
}

/* Statut en pastille de verre : jamais de couleur saturée sur le dégradé.
   Le vert néon sur violet était la pire paire chromatique possible —
   teintes opposées, luminances proches, ça vibre à la lecture. */
body.in-gallery .dh-last {
  display: inline-flex; align-self: flex-start; align-items: center;
  padding: 4px 10px; border-radius: var(--r-full);
  /* C13 : voile SOMBRE et non blanc — un voile blanc eclaircit le degrade sous
     lui et fait retomber le blanc pose dessus a 3,16:1. */
  background: rgba(13,16,23,.22);
  border: 1px solid rgba(255,255,255,.22);
  color: #fff !important; font-weight: 600;
}
/* C13 disait vrai a l'epoque : un alpha faisait tomber REPS/KCAL a 2,71:1, parce que
   le degrade etait trop clair pour laisser la moindre marge. C35 l'a approfondi de 7
   points, et un blanc a 80% tient maintenant 4,55:1 sur son arret le plus clair.
   Les libelles peuvent donc enfin reculer d'un cran. C'est le seul endroit du hero ou
   du blanc pur n'etait justifie par rien : le titre et les chiffres sont le sujet, les
   libelles ne font que les nommer. Six blancs de meme intensite, c'est ce que Chris a
   vu comme « trop d'ecriture blanche ». */
body.in-gallery .dh-sl { color: rgba(255,255,255,.80); font-weight: 600; }

/* ══ Retour au tap ════════════════════════════════════════════════════════
   Ce qui donne la sensation de matière : sans ça l'app paraît en papier. */
.cat-card, .pr-exercise-card, .pw-card, .settings-row,
.prepa-compet-card, .stat-tile, .profile-nav-card {
  transition: transform var(--dur-1) var(--ease), background-color var(--dur-1) var(--ease);
}
.cat-card:active, .pr-exercise-card:active, .pw-card:active,
.settings-row:active, .prepa-compet-card:active, .profile-nav-card:active {
  transform: scale(.975);
}

/* ══ Prépa — les jours de repos arrêtent d'écraser les séances ════════════
   Avant : la même phrase de 30 caractères répétée 5 fois, dans un cadre
   pointillé de la même hauteur qu'une vraie séance. L'écran racontait le
   vide, pas l'entraînement. Et le pointillé signifie "zone de dépôt /
   non finalisé" dans tout le langage UI moderne.

   Maintenant : une ligne discrète, basse, alignée à gauche, avec un +
   qui porte l'affordance. Le contraste de HAUTEUR fait le travail de
   hiérarchie — une séance occupe de la place, un repos n'en prend pas. */
body.in-prepa .pw-rest {
  justify-content: flex-start;
  border: 0;
  background: var(--surface-1);
  border-radius: var(--r-md);
  padding: 0 14px;
  min-height: 44px;
  text-align: left;
  font-size: var(--fs-sm);
  color: var(--text-3);
}
/* B3 — un jour passé sans rien n'est pas une décision, c'est une absence. Le
   tiret est centré et plus discret que le libellé du futur : il n'y a rien à
   lire ni rien à faire, il ne doit pas retenir l'œil pendant qu'on parcourt la
   semaine à la recherche de ce qu'on A fait. */
body.in-prepa .pw-rest-passe {
  justify-content: center;
  color: var(--text-muted);
}
body.in-prepa .pw-rest-future::after {
  content: '+';
  margin-left: auto;
  font-size: 20px;
  font-weight: 400;
  line-height: 1;
  color: var(--text-3);
}
body.in-prepa .pw-rest-future:active { background: var(--surface-3); }

/* ══ Calendrier — le cycle est un intervalle, pas une alerte ══════════════
   Le trait de cycle était présent sous CHAQUE jour du mois : une info
   constante ne porte aucune information. Et sur la couleur d'un cycle
   rouge, ça se lisait comme un mois d'échecs.
   Il devient une bande de fond continue, discrète, qui se lit comme une
   période et non comme un soulignement d'erreur. */
.prepa-cal-cycle-trait {
  height: 3px;
  border-radius: 0;
  opacity: .42;
}

/* ══ Nav basse — arrêter de laisser passer le contenu ═════════════════════
   Le verre etait a la fois trop transparent et code en dur en sombre
   (rgba(10,11,26,.97)) : en theme clair on avait une barre grise sale, et
   dans les deux themes on lisait des morceaux de cartes A TRAVERS les
   libelles. */
.bottom-nav { border-radius: var(--r-lg); }

/* L'indicateur actif etait une barre de 3px flottant AU-DESSUS de l'icone,
   detachee d'elle : ca se lisait comme un artefact, pas comme un etat.
   L'etat actif se suffit a lui-meme (icone + libelle en accent). */
.bnav-btn.active::before,
.bnav-btn.timer-active::before { display: none; }
.bnav-btn { transition: color var(--dur-1) var(--ease); }
.bnav-btn.active, .bnav-btn.timer-active { color: var(--accent); }

/* ══ Fin de liste ═════════════════════════════════════════════════════════
   La derniere carte de chaque vue etait systematiquement mangee par la nav. */
#view-gallery, #view-pr, #view-prepa, #view-profile, #view-settings {
  padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px));
}
/* C87 — LE BOUTON + RECOUVRAIT LA DERNIERE CARTE.
   Vu sur l'appareil : le FAB est pose sur le coin de la 4e carte, pas a cote.
   Les 96px ci-dessus degagent la BARRE DU BAS, pas le bouton : celui-ci est en
   `bottom: 96px` et fait 56 de haut, donc il occupe la bande 96 -> 152. Le
   contenu pouvait s'arreter a 96 et passait dessous.
   Qu'un FAB flotte au-dessus du contenu est normal ; qu'on ne PUISSE PAS faire
   defiler la derniere carte pour la degager ne l'est pas. On ajoute donc sa
   hauteur plus une respiration, et seulement en galerie — c'est la seule vue ou
   il s'affiche (`body.in-gallery .fab-new`). */
#view-gallery {
  padding-bottom: calc(96px + 56px + 14px + env(safe-area-inset-bottom, 0px));
}

/* ══ Formulaire de creation ═══════════════════════════════════════════════
   TYPE faisait 251 px et DIFFICULTE 95 px, sans aucune logique de contenu :
   deux champs de meme nature, deux largeurs. Et les <select> n'avaient pas
   de chevron, donc ils ressemblaient a des champs texte deja remplis.
   Corrige a la specificite de body.in-create, pas par une regle ajoutee. */
body.in-create .form-row.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
body.in-create .form-row.two-col > * { min-width: 0; }

body.in-create select.form-input,
#addPRModal select.form-input,
#editPRModal select.form-input,
body.in-profile select.form-input {
  /* le chevron rend l'affordance de choix explicite */
  appearance: none; -webkit-appearance: none;
  padding-right: 38px;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 20px) calc(50% + 1px), calc(100% - 14px) calc(50% + 1px);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}

/* ══ C61 — LE FONDU DES CARROUSELS DE GALERIE A ETE SUPPRIME ══════════════
   Il annoncait « il y a la suite de ce cote » sur un carrousel horizontal. La
   galerie est passee en grille verticale : il n'y a plus de suite laterale a
   annoncer, donc plus de fondu, plus de scroll-snap, plus de classes
   .fondu-gauche / .fondu-droite ni le JS qui les posait.

   UNE LECON A GARDER, elle vaut pour tout masque futur dans cette app :
   les quatre etats etaient ecrits EN DUR, sans var(), et ce n'etait pas de la
   negligence. C20 les avait factorises avec deux variables (--fondu-g /
   --fondu-d) ; le test headless passait au vert parce qu'il verifiait la CLASSE,
   pas le masque — mais sur l'iPhone aucun fondu n'apparaissait. WebKit gere mal
   var() a l'interieur d'un mask-image, et l'app tourne dans un WKWebView.
   Donc : pas de var() dans un mask-image, et un test qui verifie une classe ne
   verifie pas un rendu. */
/* C39 : il y avait ici un SECOND bloc `body.in-gallery .cc-name` qui redisait mot pour
   mot ce que la regle d'origine (l.6649) declarait deja — overflow, ellipse, nowrap.
   Inoffensif tant que les deux disaient la meme chose ; bloquant des qu'on touche a
   la premiere, puisqu'a specificite egale c'est la DERNIERE du fichier qui gagne.
   Mon passage du nom sur deux lignes n'avait aucun effet, et rien ne le signalait :
   la declaration etait valide, simplement ecrasee. Doublon retire, la regle vit a un
   seul endroit. */

/* ══════════════════════════════════════════
   C11 — PLANCHER DE CIBLE TACTILE (44 px, Apple HIG)
   34 cibles mesurees sous le seuil, dont l'etoile favori de la galerie a 17x19 px
   et le point de couleur de categorie a 14x14. Manquer un bouton est le defaut le
   plus frustrant d'une app, et c'est invisible sur un ecran de bureau.

   Technique : un ::after absolu et transparent, centre, de 44x44 minimum. La zone
   tactile grandit, la MISE EN PAGE NE BOUGE PAS — contrairement a du padding, qui
   deplacerait tout. Les selecteurs sont scopes comme les regles d'origine pour ne
   pas perdre en specificite (piege connu du projet).
   ══════════════════════════════════════════ */
/* C19 — CORRECTIF D'UNE REGRESSION DE C11.
   Cette liste posait `position: relative` sur TOUS les elements agrandis, y
   compris ceux qui etaient DEJA en `position: absolute`. Venant plus loin dans le
   fichier a specificite egale, elle gagnait et les decrochait de leur ancrage :
     - le de de nom aleatoire sortait de 10 px A GAUCHE du formulaire
     - la camera traversait la bordure haute du textarea
     - l'engrenage du profil quittait son coin haut-droit
   Un element deja absolu est deja un bloc conteneur pour son ::after : il n'a
   jamais eu besoin de cette declaration. Seuls les elements en flux normal la
   recoivent maintenant.
   Le commentaire d'origine disait « la MISE EN PAGE NE BOUGE PAS ». Elle bougeait.
   Mon test de zone tactile ne verifiait que l'atteignabilite au doigt, jamais que
   l'element etait reste en place — d'ou test-positions.js. */
body.in-gallery .cc-fav,
.profile-edit-btn,
body.in-prepa .wnav,
body.in-prepa .prepa-cal-btn,
.category-color-dot,
.btn-back,
.detail-fav-btn,
/* C89 : #backBtn a quitte .btn-back pour .btn-icon (pastille). Sans cette ligne
   il perdait la zone tactile etendue que .btn-back lui donnait — une cible qui
   passe de 106 a 44px de large est precisement celle qu'il ne faut pas oublier
   ici. Il rejoint donc ses deux voisines de barre. */
#backBtn,
#detailShareBtn,
#detailMenuBtn,
.btn-add-session,
#historyBackBtn,
/* C28 : les trois derniers champs editables du Detail, oublies par C11. Ils sont en
   position static (verifie, pas suppose — c'est en ajoutant `relative` a des elements
   DEJA absolus que C11 avait detache le de, l'appareil photo et l'engrenage). */
.card-diff.diff-editable,
.detail-date-span.detail-date--editable,
.detail-badge.detail-badge--editable,
.detail-objective.detail-objective--editable,
.detail-workout-header .btn-icon,
/* C81 : quatre cibles supplementaires, choisies sur MESURE et non sur leur
   taille seule — voir le bloc C81 sous le selecteur ::after. `.search-clear` et
   `.pwd-toggle` sont deja `position: absolute`, donc deja des blocs conteneurs :
   leur ajouter `relative` les DETACHERAIT de leur champ. C'est la faute exacte
   que C11 a commise sur le de, l'appareil photo et l'engrenage. */
.cat-rename-btn,
.cat-delete-btn,
.prepa-cal-nav-btn,
/* ── C92 — NEUF FAMILLES DE PLUS, ET DEUX LAISSEES EXPRES ─────────────────────
   C11, C75 et C81 avaient traite les cibles du DETAIL, de la galerie et de la
   Prepa. Un audit ecran par ecran a montre pourquoi le reste avait echappe :
   test-cibles-tactiles.js s'annonce general mais son propre message dit
   « cibles interactives visibles DANS LE DETAIL ». Un ecran sur quatorze, zero
   modale.

   ECARTS CENTRE A CENTRE releves sur l'appareil simule, pas estimes d'apres le
   CSS. La colonne qui decide est celle de l'axe ou la zone s'etend :

     .hdr-btn              36x36 · voisin a 75px (vertical)     -> etendue
     .support-back-btn     36x36 · voisin a 800px               -> etendue
     .pr-tab              171x36 · voisins a 177 / 656          -> etendue
     .settings-tab         83x33 · voisins a 89 / 98            -> etendue
     .sw-mode-btn         105x33 · voisins a 111 / 772          -> etendue
     .bw-add-btn           79x34 · voisin a 101                 -> etendue
     .profile-edit-pwd-link 316x19 · voisin a 164               -> etendue
     .profile-legal-link  316x33 · voisin a 463                 -> etendue
     .report-reason-btn   316x41 · voisin a 49                  -> etendue (49 > 44)
     .color-swatch         32x32 · voisin a 13px                -> LAISSEE
     .custom-style-chip    30px  · rangee qui passe a la ligne  -> LAISSEE

   ⚠ .color-swatch : C81 l'avait deja laissee sur un ecart de 40px entre
   swatches. La mesure d'aujourd'hui est pire — 13px, parce que le voisin le
   plus proche n'est pas un autre swatch mais `.category-color-dot`. Une zone de
   44 en avalerait trois. Le refus tient toujours, et pour une raison plus forte
   qu'annonce a l'epoque.

   ⚠ .custom-style-chip : sa rangee passe a la ligne (`flex-wrap`), donc l'ecart
   VERTICAL depend du nombre de styles enregistres — il n'est pas mesurable une
   fois pour toutes. Ca demande de revoir la mise en page de la rangee, pas
   d'etendre une zone.

   ⚠ .btn-primary / .btn-secondary / .btn-danger NE SONT PAS DANS CETTE LISTE.
   Ils font 43px — il en manque UN — mais `.btn-primary` est en `overflow:
   hidden` : un ::after de 44px y serait ROGNE, et la zone n'existerait que sur
   le papier. Ce sont de gros boutons d'action : c'est leur hauteur reelle qui
   doit etre juste, pas une zone invisible. Traites en min-height plus bas. */
.hdr-btn,
.support-back-btn,
.pr-tab,
.settings-tab,
.sw-mode-btn,
.bw-add-btn,
.profile-edit-pwd-link,
.profile-legal-link,
.report-reason-btn,
.pr-type-btn,
/* C75 : `.modal-close` fait 30x30 (declare l.2334) et n'avait PAS de position —
   sans elle, le ::after ci-dessous se serait ancre sur le premier ancetre
   positionne, c'est-a-dire ailleurs. C'est la faute exacte que C11 puis C28 ont
   documentee dans les deux sens ; ici le sens est celui-la. */
.modal-close {
  position: relative;
}
body.in-gallery .cc-fav::after,
.profile-edit-btn::after,
.profile-gear-btn::after,
body.in-create .btn-name-random::after,
body.in-prepa .wnav::after,
body.in-prepa .prepa-cal-btn::after,
.category-color-dot::after,
.card-fav-btn::after,
.btn-back::after,
.detail-fav-btn::after,
#backBtn::after,
#detailShareBtn::after,
#detailMenuBtn::after,
#photoFormatBtn::after,
.btn-add-session::after,
#historyBackBtn::after,
/* C28 : les trois champs editables du hero du Detail, oublies par C11. « Medium »
   mesurait 25x14 — la plus petite cible de l'app, et un appui dessus ouvre le choix
   de difficulte. Ils sont sur une meme ligne en flex : l'ecart le plus faible entre
   deux d'entre eux est de 19px et seul le plus etroit s'elargit reellement, donc pas
   de collision horizontale. La collision VERTICALE avec le badge, elle, a demande les
   12px ajoutes sur .detail-hero-meta2. */
.card-diff.diff-editable::after,
.detail-date-span.detail-date--editable::after,
.detail-badge.detail-badge--editable::after,
.detail-title.detail-title--editable::after,
/* Le bloc objectif (320x38) et l'icone du bloc Programme (32x32) : les deux derniers
   du Detail. Le premier n'a aucun voisin vertical, le second est seul a droite de son
   en-tete — aucun risque d'interception dans les deux cas, verifie par le test. */
.detail-objective.detail-objective--editable::after,
.detail-workout-header .btn-icon::after,
/* C75 — LA CIBLE LA PLUS SOUVENT MANQUEE DE L'APP, ET C11 NE L'AVAIT PAS VUE.
   `.modal-close` fait 30x30 et sert de croix a ONZE modales : c'est, de loin, le
   bouton sous-dimensionne le plus tape du projet. C11 avait traite les icones du
   Detail, de la galerie et de la Prepa — celles qu'on voit en parcourant les
   ecrans — et pas celle qui est partout a la fois.
   Sans collision possible : la croix est seule a droite de `.modal-header`, son
   unique voisin est le titre, a plus de 7px (l'extension ne deborde que de 7px de
   chaque cote sur un bouton de 30). Verifie par test-cibles-tactiles apres coup,
   pas suppose. */
/* ── C81 — QUATRE CIBLES DE PLUS, ET TROIS LAISSEES EXPRES ────────────────────
   MESURE AVANT DE DECIDER. Les ecarts CENTRE A CENTRE, releves sur l'appareil
   simule et non estimes d'apres le CSS :
     .pwd-toggle          32x32 · voisin a 64px (vertical)   -> etendue
     .prepa-cal-nav-btn   34x34 · voisin a 314px             -> etendue
     .cat-rename-btn      34x34 · voisin a 46px              -> etendue (46 > 44)
     .cat-delete-btn      34x34 · voisin a 46px              -> etendue
     .search-clear        20x20 · seule dans son champ       -> etendue
     .color-swatch        32x32 · voisin a 40px              -> LAISSEE
     .pgs-color           26x26 · meme famille               -> LAISSEE
     .style-color-dot     28x28 · meme famille               -> LAISSEE

   ⚠ POURQUOI LES TROIS PALETTES DE COULEUR RESTENT PETITES.
   Techniquement d'abord : 40px entre centres et 44px de zone, ce sont des
   zones qui SE CHEVAUCHENT de 4px. On echangerait un tap difficile contre un
   tap qui declenche la mauvaise action — c'est le refus que C11 a deja formule,
   et il vaut toujours.
   Mais surtout : la taille n'est pas le seul critere. Ce qui compte, c'est le
   COUT D'UN TAP RATE. Manquer la croix d'une modale, c'est rester coince dans
   un ecran qu'on veut quitter. Manquer une pastille de couleur, c'est voir
   immediatement la mauvaise couleur et retaper a cote. Une palette dense est un
   cas ou 44px n'est pas le bon objectif — les selecteurs de couleur d'iOS
   eux-memes sont plus serres que ca.
   Les elargir demanderait de reprendre la mise en page de la palette, pas
   d'etendre une zone. C'est un autre lot, avec une autre question. */
.search-clear::after,
.pwd-toggle::after,
.cat-rename-btn::after,
.cat-delete-btn::after,
.prepa-cal-nav-btn::after,
/* C92 — les neuf familles mesurees ci-dessus. `min-width/height: 100%` garde
   les cibles deja larges (.pr-tab fait 171px) a leur taille : la zone ne les
   retrecit pas, elle ne fait que garantir le plancher de 44 dans l'autre axe. */
.hdr-btn::after,
.support-back-btn::after,
.pr-tab::after,
.settings-tab::after,
.sw-mode-btn::after,
.bw-add-btn::after,
.profile-edit-pwd-link::after,
.profile-legal-link::after,
.report-reason-btn::after,
.pr-type-btn::after,
.modal-close::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  min-width: 100%;
  min-height: 100%;
  transform: translate(-50%, -50%);
  /* Ne mange pas les taps du voisin : purement une extension de CETTE cible. */
  border-radius: inherit;
}

/* ⚠ C92 — LE PIXEL MANQUANT DES GROS BOUTONS.
   `.btn-primary` et `.btn-secondary` portent `padding: 12px 22px` sur une ligne
   de 19px : 43px de haut, un sous le plancher. Mesure faite dans les DIX-HUIT
   modales, c'est le meme bouton partout — c'est donc le defaut le plus repandu
   de l'app, et le moins cher a corriger.
   `min-height` plutot que du padding : les trois sont deja en `inline-flex` +
   `align-items: center`, le texte reste donc centre sans toucher a l'horizontal.
   Et plutot qu'une zone ::after : `.btn-primary` est en `overflow: hidden`
   (verifie, pas suppose), une zone y serait rognee et n'existerait que sur le
   papier. Sur un bouton d'action pleine largeur, c'est de toute facon la
   hauteur REELLE qui doit etre juste — personne ne vise a cote d'un bouton
   « Sauvegarder », mais 43px reste sous ce que le projet s'est fixe. */
.btn-primary, .btn-secondary, .btn-danger { min-height: 44px; }

/* ⚠ C92 — `.prepa-act` : LA HAUTEUR, PAS UNE ZONE, ET C'EST MESURE.
   Les lignes d'action de la feuille d'appui long font 42-43px et sont EMPILEES
   SANS ecart : centres releves a 43, 43 et 53px. Une zone de 44 chevaucherait
   sa voisine d'un pixel — c'est exactement le refus formule en C81 pour les
   palettes (« 40px entre centres et 44px de zone, ce sont des zones qui SE
   CHEVAUCHENT »), en plus petit mais de meme nature. Sur une ligne pleine
   largeur, monter la hauteur reelle n'est pas un compromis : c'est mieux.
   L'ecart passe a 45+, donc plus de chevauchement du tout. */
.prepa-act { min-height: 44px; }
/* NON TRAITE ICI, volontairement : les spans de texte editables en ligne
   (.card-diff, .detail-date-span, .detail-title, .detail-badge) font 14 a 27 px de
   haut et se suivent sur une meme ligne. Leur donner 44 px de zone les ferait se
   chevaucher — on echangerait un tap difficile contre un tap qui declenche la
   mauvaise action. Ca demande de revoir la mise en page de la ligne meta, pas une
   extension de zone. Idem .cc-fav, dont le ::after est rogne par l'overflow du
   carrousel. Mesure a jour dans tools/ui-bench/audit-visuel.js. */

/* ══════════════════════════════════════════
   C17 — ICONES QUI REMPLACENT DES EMOJI
   Un emoji ne suit pas currentColor, change de glyphe selon l'OS et la version,
   et echappe a l'echelle typographique. Ces trois-la servaient d'ICONES d'interface
   (et non de voix editoriale, qui garde ses emoji dans les notifications).
   ══════════════════════════════════════════ */
.ws-ico {
  width: 13px; height: 13px;
  fill: currentColor;
  stroke: none;
  vertical-align: -1px;
  margin-right: 2px;
}
.pr-best-ico {
  width: 13px; height: 13px;
  fill: none;
  stroke: var(--warn-ink);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: -1px;
  margin-left: 3px;
}
body.in-prepa .pw-num-compet svg {
  width: 15px; height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ══ C40 — UN SEUL AXE DE LECTURE DANS TOUTE L'APP ════════════════════════════
   Le hero du jour a été rebâti sur un axe à gauche, et le commentaire qui
   accompagne ce travail dit pourquoi : « 5 blocs centrés empilés, 5 largeurs
   différentes, 5 axes optiques. L'œil n'avait nulle part où se poser. »

   Le diagnostic était juste. Il n'a simplement jamais été appliqué ailleurs.

   Le hero du DÉTAIL est resté exactement dans l'état décrit, en pire : SEPT blocs
   centrés de sept largeurs différentes — le numéro, le titre, la pastille de
   catégorie, la ligne méta, les chiffres, l'objectif, le bouton Programmer.
   Et les CARTES de la galerie mélangent les deux langages : en-tête et statut à
   gauche, chiffres centrés, dans un objet de 90 px de haut.

   Trois écrans, trois grammaires. C'est ce qui donne à l'app son air « pas fini »
   bien plus que le choix des couleurs. Ici tout passe sur l'axe gauche.
   Aucun texte n'est touché, aucune donnée : uniquement l'alignement.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Hero du Détail ─────────────────────────────────────────────────────────── */
body.in-detail .detail-hero { text-align: left; }
body.in-detail .detail-num { text-align: left; }
/* C58 — le numero, le titre et la categorie sur UNE rangee.
   Le titre garde toute la place restante ; les deux etiquettes qui l'encadrent ne
   prennent que la leur. `flex-wrap` pour qu'un nom tres long puisse rejeter la
   pastille sur une seconde ligne plutot que de se faire ecraser — la hauteur
   gagnee ne doit pas se payer en nom tronque. */
body.in-detail .detail-hero-titre {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 6px;
  /* L'etoile favori est en position ABSOLUE a 10px du bord droit, sur 32px de
     large. En amenant la pastille de categorie sur cette rangee, je l'ai envoyee
     DESSOUS — collision vue sur capture, invisible pour le banc qui ne mesure ni
     recouvrement ni debordement. On reserve son couloir. */
  padding-right: 46px;
}
body.in-detail .detail-hero-titre .detail-title { flex: 1 1 auto; min-width: 0; margin-bottom: 0; }
body.in-detail .detail-hero-titre .detail-num { flex: 0 0 auto; }
body.in-detail .detail-hero-titre .detail-hero-cat { flex: 0 0 auto; margin: 0; }
body.in-detail .detail-hero-cat { text-align: left; }
body.in-detail .detail-hero-meta2 { justify-content: flex-start; }
body.in-detail .detail-objective-line { justify-content: flex-start; }

/* Les chiffres : même traitement que le hero du jour — alignés à gauche, espacés
   par un vrai gap, sans filet séparateur. Le filet était le seul de l'app : deux
   colonnes en flex:1 avec un trait au milieu, là où la galerie utilise un simple
   écart. Un séparateur n'ajoute rien quand l'espace suffit à séparer. */
body.in-detail .detail-stat-row { justify-content: flex-start; gap: 28px; }
body.in-detail .detail-stat-col { flex: 0 0 auto; text-align: left; }
body.in-detail .detail-stat-sep { display: none; }

/* Le bouton Programmer suit l'axe plutôt que de flotter au centre. */
body.in-detail .detail-hero .btn-schedule,
body.in-detail .detail-schedule-line { justify-content: flex-start; }

/* ── Cartes de la galerie : les chiffres rejoignent l'axe du titre ──────────── */
body.in-gallery .cc-stats { justify-content: flex-start; gap: 28px; }
body.in-gallery .cc-stat { flex: 0 0 auto; text-align: left; }
body.in-gallery .cc-sep { display: none; }

/* ══ C41 — LA POLICE D'AFFICHAGE ATTEINT ENFIN TOUS LES TITRES ═══════════════
   Le jeu --font-display couvrait 35 endroits, très cohérents : logos, titres de
   vues, titres de modales, titre du Détail, noms de training en Prépa et dans le
   sélecteur, valeurs des Records.

   Il lui manquait pourtant les DEUX titres les plus vus de l'app — le nom dans le
   bloc du jour et le nom sur les cartes de la galerie — ainsi que tous les grands
   chiffres. Ils héritaient donc d'Inter, alors que leurs équivalents exacts en
   Prépa (.pw-name) et en Records (.pr-detail-best-value) étaient déjà en display.
   Tant que les deux jetons désignaient la même famille, l'oubli ne se voyait pas.
   Il devient visible à la seconde où une vraie police d'affichage arrive.
   ═══════════════════════════════════════════════════════════════════════════ */
body.in-gallery .dh-name,
body.in-gallery .cc-name { font-family: var(--font-display); }

/* Les grands chiffres suivent aussi : .pr-detail-best-value et .pr-hero-perf, qui
   jouent exactement le même rôle, sont en display depuis toujours. Une chasse
   étroite sert particulièrement les nombres à trois chiffres. */
body.in-gallery .dh-sv,
body.in-gallery .cc-sv,
body.in-detail .detail-stat-val { font-family: var(--font-display); }

/* ══ C41 (suite) — la police d'affichage atteint la vue Records ══════════════
   Meme oubli que pour la galerie, trouve en regardant l'ecran : dans la liste des
   records, les noms d'exercices et leurs valeurs etaient restes en Inter, alors que
   leurs equivalents exacts sont en display partout ailleurs — .pw-name en Prepa,
   .cc-name en galerie, .pr-detail-best-value dans le detail d'un record.

   La regle est simple et vaut mieux que trente exceptions : un NOM d'entrainement ou
   d'exercice, et une VALEUR chiffree mise en avant, sont de l'affichage. Le reste —
   compteurs, dates, statuts, corps de texte — reste en Inter, qui est faite pour ca.
   ═══════════════════════════════════════════════════════════════════════════ */
body.in-pr .pr-exercise-name,
body.in-pr .pr-exercise-best,
body.in-pr .pr-history-perf,
body.in-pr .pr-detail-title { font-family: var(--font-display); }

/* ══════════════════════════════════════════════════════════════════════════
   C67 — UN NOM DE TRAINING S'ECRIT TOUJOURS PAREIL

   LA REGLE : Barlow Condensed, graisse 800, MAJUSCULES. Seule la TAILLE varie
   selon la surface — c'est elle qui porte la hierarchie, pas la casse ni la
   police. Un nom doit se reconnaitre comme un nom ou qu'il apparaisse.

   AUDIT AVANT CORRECTION (mesure, pas lecture du code) :

     carte galerie       .cc-name              Barlow 800  MAJ   ok
     hero du jour        .dh-name              Barlow 800  MAJ   ok
     titre du Detail     .detail-title         Barlow 800  MAJ   ok
     carte Prepa         .pw-name              Barlow 800  MAJ   ok
     boite de reception  .inbox-item-name      Barlow 800  MAJ   ok
     Progression         .prog-seance-nom      Barlow 700  MAJ   graisse
     partage             .share-training-name  Barlow 800  min   CASSE
     selecteur Prepa     .pp-name              Barlow 800  MAJ   ok
     raccourci favori    .fav-chip-nom         heritee     min   CASSE
     appui long          #quickActionsTitle    Inter 12/400 min  CASSE
     date picker         #prepaDatePickerSubtitle Inter 13/400 min CASSE

   Six surfaces sur onze le faisaient. (Une septieme ligne de ce tableau a ete
   FAUSSE un moment : j'avais classe `.pp-name` en « minuscules » sur la foi
   d'un grep tronque avant la ligne text-transform. Corrige apres mesure — un
   audit se fait sur ce qui est CALCULE, pas sur ce qu'on croit lire.)

   Les deux pires — l'appui long et le
   selecteur de date — affichaient le nom en Inter 12px maigre gris, c'est-a-
   dire dans la typographie d'un SOUS-TITRE : ces deux blocs heritaient de
   `.prepa-sheet-subtitle`, une classe qui sert aussi a afficher une DATE. Le
   nom du training y avait exactement l'apparence d'une legende.

   ⚠ POURQUOI CE BLOC NE LISTE PAS TOUS LES SELECTEURS EN UNE REGLE.
   Le reflexe serait d'ecrire `.cc-name, .pp-name, … { … }` ici. Ca creerait des
   EGALITES DE SPECIFICITE avec les regles existantes (`.prepa-pick-item
   .pp-name` pese autant que `.pp-name` dans une liste equivalente), donc une
   victoire qui ne tiendrait qu'a l'ordre du fichier — precisement ce qui a
   produit le bug de l'etoile favori en C43. Les surfaces deja conformes ne sont
   donc PAS retouchees, et les non conformes sont corrigees LA OU ELLES SONT
   DECLAREES. Seule exception : `.nom-training`, une classe ajoutee dans le HTML
   aux deux sous-titres qui portent un nom, pour les distinguer de celui qui
   porte une date.
   ══════════════════════════════════════════════════════════════════════════ */
.nom-training {
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -.01em;
  /* 15px et non 13 : un nom n'est pas une legende. Il reste sous le titre de la
     feuille, la hierarchie tient par la taille. */
  font-size: 15px;
  color: var(--text-primary);
}
/* Le raccourci favori n'heritait d'AUCUN traitement : il affichait le nom dans
   la police du corps, en minuscules, a cote de cartes qui l'ecrivaient en
   capitales condensees. */
body.in-gallery .fav-chip-nom {
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -.01em;
}

/* C75 — LA SURFACE QUE C67 AVAIT MANQUEE, ET POURQUOI.
   C67 a corrige les deux sous-titres de feuille (`#quickActionsTitle`,
   `#prepaDatePickerSubtitle`) en leur posant `.nom-training` dans le HTML. Il a
   laisse `#prepaActionsTitle` — la feuille qui s'ouvre quand on tape une seance
   programmee dans la Prepa — parce qu'il est en `-title` et non en `-subtitle`.
   Une passe faite au grep sur un suffixe.

   ⚠ ON NE PEUT PAS LUI POSER `.nom-training` ICI. Cette classe fixe aussi
   `font-size: 15px`, calibre pour un SOUS-titre. `#prepaActionsTitle` est le
   titre de sa feuille (18px), et `.nom-training` etant declaree plus bas dans le
   fichier gagnerait le departage a specificite egale : le nom RETRECIRAIT de 18
   a 15px. On corrigerait la casse en cassant la hierarchie.

   ⚠ ON NE PEUT PAS NON PLUS TOUCHER `.prepa-sheet-title`. Cinq elements la
   portent, et QUATRE sont des libelles fixes — « Objectifs », « Journal »,
   « Programmer un training », « Programmer ce training ». Les passer en
   capitales serait un changement de ton non demande sur toute la Prepa.

   D'ou une regle d'ID qui ne declare QUE la casse : elle vise le seul element
   concerne, ne peut etre battue par aucune classe (1,0,0), et ne bloque aucune
   autre declaration — c'est exactement le piege inverse de celui documente en
   C67 plus haut, ou un ID portait une typographie complete et empechait
   `.nom-training` de s'appliquer. */
#prepaActionsTitle { text-transform: uppercase; }

/* ══════════════════════════════════════════════════════════════════════════
   C68 — L'APERCU DEVIENT UN MENU CONTEXTUEL CENTRE

   C65 avait mis l'apercu du programme dans la bottom-sheet existante. Vu sur
   l'appareil, l'emphase y etait INVERSEE : l'apercu — ce qu'on vient chercher
   en maintenant une carte — occupait un tiers de la hauteur, et les trois
   boutons d'action les deux autres tiers. On repondait « voici ce que tu peux
   faire » a quelqu'un qui demandait « c'est quoi, celui-la ».

   Le geste d'iOS (photothèque, menus contextuels) fait l'inverse : la
   prévisualisation se pose AU CENTRE, en grand, et les actions se rangent
   dessous en menu compact. C'est ce qu'on reproduit.

   ⚠ CE BLOC NE TOUCHE QUE LA FEUILLE D'APPUI LONG.
   `.prepa-sheet` sert aussi aux feuilles de la Prepa (choisir un training,
   choisir une date, actions sur une seance) qui, elles, DOIVENT rester des
   feuilles montantes : elles presentent une liste ou un formulaire, pas un
   objet. D'ou les classes dediees `apercu-overlay` / `apercu-centre` posees
   dans le HTML, et pas une redefinition de `.prepa-sheet`.
   ══════════════════════════════════════════════════════════════════════════ */

/* Le voile : centre au lieu de coller en bas, et il FLOUTE. Le flou n'est pas
   decoratif ici — c'est lui qui detache l'objet de son contexte et fait que le
   regard n'a plus qu'un endroit ou aller. */
.apercu-overlay {
  align-items: center;
  justify-content: center;
  padding: max(24px, var(--safe-top)) 18px 24px;
  backdrop-filter: blur(14px) saturate(1.1);
  -webkit-backdrop-filter: blur(14px) saturate(1.1);
}

.apercu-centre {
  /* Ni pleine largeur ni collee : une carte posee, avec de l'air autour. */
  width: 100%;
  max-width: 340px;
  border-radius: 26px;
  border: 1px solid var(--border-1);
  padding: 16px 16px 14px;
  gap: 14px;
  max-height: 88vh;
  /* L'ombre porte tout le decollement : sans elle, la carte se confond avec le
     voile floute au lieu de flotter au-dessus. */
  box-shadow: 0 24px 60px rgba(0,0,0,.55);
  /* Entree en ECHELLE et non en translation : l'objet doit sembler GRANDIR
     depuis la carte qu'on tient, pas arriver d'ailleurs. C'est toute la
     difference entre un menu contextuel et une feuille. */
  /* C112 : `.26s` -> le jeton. Seule surface a s'ouvrir plus vite que les
     dix-neuf autres, sans raison ecrite. */
  animation: apercu-in var(--dur-v2) var(--ease-v2);
}
@keyframes apercu-in {
  from { opacity: 0; transform: scale(.92); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .apercu-centre { animation: none; }
}

/* Le nom devient le SUJET de la carte, pas une legende. Il etait a 15px sous un
   titre de meme graisse ; ici il n'y a plus de titre concurrent. */
.apercu-centre .nom-training { font-size: 19px; }
.apercu-centre .prepa-sheet-head { padding: 0 2px; }

/* L'apercu prend la place : c'est l'objet. 46vh au lieu de 34, et il n'a plus
   besoin de son propre cadre puisque la carte centrale en est un. */
.apercu-centre .quick-apercu {
  margin: 0;
  border-radius: 16px;
}
.apercu-centre .quick-apercu-corps { max-height: 46vh; }

/* Les actions se compactent : rangees collees en un seul groupe, a la maniere
   d'un menu iOS — au lieu de trois gros boutons espaces qui pesaient plus lourd
   que le contenu.

   ⚠ C92 : ces rangees etaient a 42px, et c'est cette regle (spe 0-2-0, declaree
   plus tard) qui gagnait sur le plancher pose plus haut — d'ou deux cibles
   encore sous 44 apres correction, trouvees par le balayage et non a la
   relecture. Centres mesures a 43, 43 et 53px : les rangees sont COLLEES, donc
   une zone ::after de 44 chevaucherait sa voisine, exactement le refus formule
   en C81 pour les palettes. C'est donc la hauteur reelle qui monte.
   Et la reference invoquee ci-dessus tranche d'elle-meme : une rangee de menu
   contextuel iOS fait 44pt, pas 42. Le groupe gagne 6px sur trois rangees. */
.apercu-centre .prepa-sheet-actions-list { gap: 0; }
.apercu-centre .quick-act {
  min-height: 44px;
  border: none;
  border-radius: 0;
  background: none;
  padding-left: 4px;
}
.apercu-centre .quick-act + .quick-act { border-top: 1px solid var(--border-0); }
.apercu-centre .prepa-act-cancel {
  margin-top: 8px;
  min-height: 40px;
  border-radius: 12px;
}

/* ══════════════════════════════════════════════════════════════════════════
   C69 — L'APERCU N'EST PLUS UN CADRE DANS UN CADRE

   Dans la bottom-sheet de C65, le programme avait besoin de son propre cadre :
   il flottait au milieu d'une feuille qui contenait aussi un titre et trois
   boutons, rien ne disait ou il commencait. Depuis C68 il est dans une carte
   centree dont il EST le sujet — le cadre interieur ne delimite plus rien, il
   ajoute juste une boite dans une boite. On le retire : le programme se pose
   directement sur la carte, et un simple filet le separe du menu d'actions.

   ⚠ « OUVRIR » DISPARAIT — ET C'EST UN RENVERSEMENT ASSUME DE C65.
   J'avais argumente l'inverse : sans libelle, rien ne dirait que l'apercu est
   actionnable, et ce serait l'erreur corrigee en C7 sur « Ajouter un objectif ».
   Cet argument tenait dans une FEUILLE, ou l'apercu etait un bloc parmi
   d'autres. Il ne tient plus dans un MENU CONTEXTUEL : la convention y est que
   l'objet montre en haut est l'objet, et qu'on le touche pour y aller — c'est
   ce que fait la phototheque d'iOS, sans jamais ecrire « ouvrir ». Le menu
   d'actions juste dessous suffit a dire que tout ce bloc est interactif.
   La zone reste un <button> : le comportement ne change pas, seul le libelle
   part.
   ══════════════════════════════════════════════════════════════════════════ */
.apercu-centre .quick-apercu {
  margin: 0;
  padding: 0;
  border: none;
  background: none;
  border-radius: 0;
  /* Un filet, pas une boite : il separe l'objet de ses actions sans l'enfermer.
     Meme jeton que les separateurs entre rangees d'action, pour que la carte se
     lise comme une seule surface divisee, et non comme trois elements empiles. */
  border-bottom: 1px solid var(--border-0);
}
.apercu-centre .quick-apercu-ouvrir { display: none; }
/* Le programme respire : il n'a plus de cadre pour le tenir a distance des
   bords, c'est donc son propre padding qui s'en charge. */
.apercu-centre .quick-apercu-corps {
  max-height: 46vh;
  padding: 4px 2px 16px;
}
@media (hover: hover) {
  .apercu-centre .quick-apercu:hover { background: var(--bg-hover); }
}

/* ══════════════════════════════════════════════════════════════════════════
   C70 — « PROGRAMMER CE TRAINING » : LE CHAMP DATE, ET LA HIERARCHIE

   Vu sur l'appareil. Deux defauts distincts dans la meme feuille.

   1. LE CHAMP DATE NE SE VOYAIT PAS CLIQUABLE. « 4 aout 2026 » etait du texte
      centre dans un cadre nu : ni icone, ni chevron, rien qui annonce qu'un
      selecteur va s'ouvrir. C'est exactement l'erreur corrigee en C7 sur
      « Ajouter un objectif » — une commande qui ne ressemble pas a une commande
      ne se fait pas taper. L'icone de calendrier dit CE QU'ON OUVRE, le chevron
      dit QUE ca s'ouvre, et le texte repasse a gauche comme dans un champ.

   2. LE TITRE CONCURRENCAIT LE NOM. « Programmer ce training » (18px/800) et le
      nom du training (15px/800) se suivaient dans la meme graisse : deux titres
      empiles, dont aucun ne dominait. Or le SUJET de cette feuille est le
      training ; l'action n'est qu'une etiquette. On inverse : l'action devient
      une petite capitale grise, le nom passe devant.

   ⚠ TOUT EST SCOPE A `.prepa-sheet-datepicker`. `.prepa-sheet-title` et
   `.nom-training` servent a d'autres feuilles, ou la hierarchie inverse est la
   bonne (choisir un training dans une liste : c'est l'ACTION qui titre).
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Le champ ────────────────────────────────────────────────────────── */
.prepa-date-champ {
  display: flex; align-items: center; gap: 10px;
  padding: 0 12px;
  min-height: 48px;
  border-radius: 14px;
  background: var(--surface-2);
  border: 1px solid var(--border-1);
}
.prepa-date-champ:focus-within { border-color: var(--theme-color); }
.prepa-date-ico, .prepa-date-chev {
  flex-shrink: 0; fill: none; stroke: var(--text-3);
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
.prepa-date-ico  { width: 18px; height: 18px; }
.prepa-date-chev { width: 16px; height: 16px; margin-left: auto; }
/* L'input perd son cadre : c'est l'enveloppe qui le porte maintenant. Empiler
   deux surfaces donnerait un champ dans un champ, le defaut qu'on vient de
   retirer de l'apercu en C69. */
.prepa-sheet-datepicker #prepaScheduleDateInput {
  flex: 1; min-width: 0;
  background: none; border: none; box-shadow: none;
  padding: 0; min-height: 0;
  text-align: left;
}
.prepa-sheet-datepicker #prepaScheduleDateInput:focus {
  border: none; box-shadow: none; outline: none;
}
/* ⚠ DEUX CALENDRIERS, VU SUR CAPTURE. Un `input[type=date]` dessine SON PROPRE
   indicateur de calendrier, a droite. Avec l'icone ajoutee a gauche, le champ en
   affichait donc deux — celui du navigateur et le notre, encadrant la date.
   On masque le natif : son apparence et sa position changent d'une plateforme a
   l'autre (WebKit et Blink ne le rendent pas pareil, iOS pas du tout), donc
   c'est le seul des deux qu'on ne maitrise pas.
   Le champ reste ouvrable : sur iOS comme sur Android, taper n'importe ou dans
   un input date ouvre le selecteur — l'indicateur n'est pas la cible, juste une
   decoration. */
.prepa-sheet-datepicker #prepaScheduleDateInput::-webkit-calendar-picker-indicator {
  display: none;
  -webkit-appearance: none;
}

/* ── 2. La hierarchie ───────────────────────────────────────────────────── */
/* L'action devient une etiquette : petite capitale, en retrait. Meme grammaire
   que « DATE » juste dessous, ce qui les fait lire comme deux libelles de champ
   plutot que comme deux titres. */
.prepa-sheet-datepicker .prepa-sheet-title {
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-3);
  margin-bottom: 2px;
}
/* Le nom prend la place du titre : c'est lui le sujet de la feuille. */
.prepa-sheet-datepicker .nom-training { font-size: 22px; }

/* ══════════════════════════════════════════════════════════════════════════
   C113 — LES DEUX PLANCHERS DE TEXTE, POSÉS EXPLICITEMENT

   Relevé sur huit vues plus le Détail : VINGT-SEPT familles de texte à 11 px,
   la taille la plus utilisée de l'app (97 occurrences). Le problème n'était pas
   le 11 px en soi — c'est ce qu'il portait. « KCAL » à 11 px, très bien, c'est
   une unité. « Appuie pour démarrer », « Fait il y a 5 j », « Programmé » et le
   nom de la catégorie à la MÊME taille, non : ce sont des informations, et pour
   certaines des instructions.

   Deux règles groupées plutôt que vingt-cinq déclarations retouchées une par
   une : la liste se lit d'un coup, se grep, et dit à quelle famille chaque
   texte appartient.

   ⚠ ET LA LIMITE DE CE PROCÉDÉ, PAYÉE EN L'ÉCRIVANT. J'avais noté ici que des
   classes simples déclarées en fin de feuille « gagnent sur leur déclaration
   d'origine ». C'EST FAUX dès qu'un sélecteur plus spécifique existe, et il en
   existait quatre :

       body.in-detail .detail-hero-cat .detail-badge      0-3-0
       body.in-detail .detail-hero-meta2 .card-diff       0-3-0
       body.in-detail .detail-hero-meta2 .detail-date-span 0-3-0
       body.in-detail .itimer-toplabel                    0-2-0

   Ces quatre-là restaient à 11 px pendant que la règle groupée croyait les
   avoir montés. Elles sont donc corrigées LÀ OÙ ELLES VIVENT. Troisième fois
   que ce projet paie la spécificité (`.btn-icon` contre `.hdr-btn` en C92,
   `body.in-gallery .fab-new` en C109) : une règle de fin de feuille ne gagne
   que contre des sélecteurs de même poids.

   ⚠ CE QUI RESTE À 11 PX EST UN CHOIX, PAS UN OUBLI. Une unité se lit par sa
   position et son contexte, pas par sa taille : personne ne LIT « KCAL », on le
   reconnaît. Les repères « #2 » et « LUN » sont dans le même cas, et « LUN »
   vit dans une grille de sept colonnes où chaque pixel compte.
   ══════════════════════════════════════════════════════════════════════════ */

/* Unités et repères — se reconnaissent, ne se lisent pas. */
.cc-sl, .dh-sl, .detail-stat-label,
.cc-num, .dh-num,
.pw-day-abbr {
  font-size: var(--fs-micro);
}

/* Information — se lit, donc 12 px. */
.cc-last, .detail-last, .dh-last,
.dh-chip, .detail-badge, .card-diff,
.detail-date-span,
.itimer-toplabel, .sw-hint,
.pr-hero-kicker, .pr-hero-quand, .pr-delta,
.pcb-name, .pcb-week, .pcc-meta, .pcc-tag,
.pw-state, .pw-sub,
.bnav-btn span {
  font-size: var(--fs-xs);
}

/* Les deux tailles orphelines de l'échelle, ramenées dessus.
   15 px n'avait que deux usages et 17 px trois — deux valeurs inventées pour
   trois écrans, alors que `--fs-body` (15) n'avait AUCUN usage et que
   `--fs-h3` (18) est la taille des autres titres de section. */
.profile-id, .prepa-cal-nav-label { font-size: var(--fs-body); }
.modal-title { font-size: var(--fs-h3); }

/* ══════════════════════════════════════════════════════════════════════════
   C118 — PERSONNALISATION : UNE PAGE, UN APERÇU

   Ce que la mesure reprochait a l'ecran d'avant :
     - 155 px de chrome permanent avant le premier contenu, soit 18 % de
       l'ecran, pour naviguer entre quatre pages dont trois etaient cachees ;
     - sept boutons destructifs a decouvert dans la seule liste des categories ;
     - aucun apercu : on reglait la mise en forme de ses programmes sans jamais
       voir un programme.

   L'onglet Theme, lui, etait deja juste — trois cartes qui MONTRENT le resultat
   au lieu de le decrire. Cette refonte etend ce principe aux trois autres.
   ══════════════════════════════════════════════════════════════════════════ */

/* ⚠ C120 — LES REGLES `.set-apercu*` ONT ETE SUPPRIMEES ICI.
   L'apercu collant coutait 296 px mesures une fois dans « Mise en forme »,
   34 % de l'ecran, pour montrer cinq lignes dont une seule concernait
   l'interrupteur qu'on touchait. L'echantillon vit desormais DANS chaque
   ligne : voir `.dsr-ech` pres de `.default-style-row`.
   La variable `--set-entete-h`, qui n'existait que pour caler cet apercu sous
   l'en-tete collant, disparait avec lui. */

/* En-tete de section : le titre, son action a droite, la description dessous.
   L'action monte dans le titre au lieu d'occuper une bande pleine largeur en
   degrade avant la liste — « Ajouter une categorie » est une action secondaire,
   elle n'a pas a etre le premier element qu'on voit. */
.set-sec {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 4px 12px;
  margin: 28px 0 10px;
}
.set-sec-titre {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 800;
  letter-spacing: .01em;
  color: var(--text-primary);
}
/* ⚠ 44px MINIMUM, ET LA GARDE DE C92 ME L'A RAPPELE : livree a 52x28, sous le
   plancher. Un lien de texte reste un lien de texte a l'oeil, mais sa zone doit
   valoir celle d'un bouton — le padding suffit, pas besoin de zone etendue. */
.set-sec-act {
  background: none; border: none; padding: 10px 4px; min-height: 44px;
  display: inline-flex; align-items: center;
  color: var(--theme-color);
  font-size: var(--fs-sm); font-weight: 700;
  cursor: pointer;
}
.set-sec-act:active { opacity: .6; }
/* La description occupe les DEUX colonnes : sur une grille, un titre long et
   une action a droite se disputent la place et le texte se casse en escalier. */
.set-sec-desc {
  grid-column: 1 / -1;
  font-size: var(--fs-xs);
  color: var(--text-2);
  line-height: 1.5;
}
/* La premiere section est collee sous l'en-tete : pas de marge haute en plus. */
.settings-form > div:first-of-type .set-sec { margin-top: 0; }

/* ══ C119 — LA LIGNE DE CATEGORIE, ET SA FICHE ══════════════════════════════
   La ligne etait un `div` portant deux boutons ronds (crayon + croix rouge).
   Elle est devenue un `button` : toute la rangee ouvre la fiche, et il ne reste
   qu'un chevron. Sept actions destructives a decouvert ont disparu.
   Modele : les Rappels et le Calendrier d'Apple, Things, Todoist — on entre
   dans l'element avant de pouvoir le detruire. */
.category-row {
  display: flex; align-items: center; gap: 12px;
  width: 100%; min-height: 56px; padding: 12px 16px;
  background: none; border: none; text-align: left; cursor: pointer;
  font: inherit; color: inherit;
}
.category-row:active { background: rgba(var(--theme-rgb), .07); }
.category-chev {
  width: 15px; height: 15px; flex-shrink: 0;
  fill: none; stroke: var(--text-3); stroke-width: 2.3;
  stroke-linecap: round; stroke-linejoin: round;
}

.cat-fiche-lbl {
  display: block; margin-bottom: 8px;
  font-size: var(--fs-xs); font-weight: 600;
  color: var(--text-2); letter-spacing: .03em;
}
/* ⚠ SEULE, EN BAS, ET DETACHEE DU RESTE. Une action destructive collee aux
   champs se tape par ricochet ; le trait au-dessus et la marge disent qu'on
   change de registre. */
.cat-fiche-suppr {
  display: block; width: 100%; min-height: 44px; margin-top: 22px; padding: 12px;
  background: none; border: none; border-top: 1px solid var(--surface-card-brd);
  color: var(--danger, #ff4757);
  font-size: var(--fs-sm); font-weight: 700; cursor: pointer;
}
/* Desactive plutot que masque : une action qu'on ne voit pas ne s'apprend
   jamais. On la montre, eteinte, et la raison est ecrite dessous. */
.cat-fiche-suppr:disabled { color: var(--text-3); opacity: .55; cursor: default; }
.cat-fiche-note {
  margin-top: 6px; text-align: center;
  font-size: var(--fs-xs); color: var(--text-3);
}
.cat-fiche-note:empty { display: none; }

/* ══ C120 — « MES MOTS » PREND EXACTEMENT LA MEME LIGNE ═════════════════════
   Derniere croix rouge de l'ecran, et troisieme facon d'editer un element sur
   la meme page. La ligne devient le meme bouton-a-chevron que les categories,
   et la suppression descend dans la fiche.
   ⚠ CES REGLES SONT EN FIN DE FEUILLE ET DOIVENT LE RESTER : la version courte
   de `.custom-style-row` (l. ~2956) est un `display:flex` de la meme specificite
   0-1-0, elle gagnerait si elle passait apres. */
.custom-style-row {
  display: flex; align-items: center; gap: 12px;
  width: 100%; min-height: 56px; padding: 12px 16px;
  background: none; border: none; text-align: left; cursor: pointer;
  font: inherit; color: inherit;
  flex-wrap: nowrap;
}
.custom-style-row:active { background: rgba(var(--theme-rgb), .07); }
.custom-style-row .cs-preview { flex: 1; min-width: 0; }
.custom-style-row .cs-type { flex: 0 0 auto; color: var(--text-muted); }

/* ══ C123 — L'ECRAN DE BIENVENUE ════════════════════════════════════════════
   Ce que l'inscription ne demande plus est demande ici, une fois le compte
   cree. Un ecran, trois champs, tous facultatifs.
   Pas de barre du bas : on ne navigue pas ailleurs depuis un ecran d'accueil,
   « Passer » est la sortie. */
body.in-bienvenue .bottom-nav { display: none; }
body.in-bienvenue .main-content { padding-top: 0; }
/* ⚠ C129 — 106 DES 274,7 px DE VIDE ETAIENT DU PADDING FANTOME. `.main-content`
   reserve `padding-bottom: 106px` pour la barre de navigation ; or cette vue la
   masque (regle juste au-dessus). On gardait donc la place d'un meuble absent.
   Le jumeau du haut existait deja depuis C123 — le bas avait ete oublie, et
   c'est exactement le genre d'asymetrie qui ne se voit pas en lisant le CSS. */
body.in-bienvenue .main-content { padding-bottom: 0; }
/* Le contenu occupe le haut, les actions sont poussees en bas par le `flex: 1`
   de `.bienv-defile`. Pas de `position: fixed` : le flux normal suit le clavier
   tout seul, et si le contenu depasse la hauteur, la page defile au lieu de
   glisser sous les boutons. */
.bienv-wrap {
  display: flex; flex-direction: column;
  min-height: 100dvh;
  padding: calc(var(--safe-top) + 26px) 4px calc(var(--safe-bottom) + 20px);
  max-width: 480px; margin: 0 auto;
}
/* ⚠ `flex: 1 0 auto` ET PAS `flex: 1`. Le raccourci vaut `1 1 0%`, donc un
   `flex-shrink: 1` qui COMPRIME le contenu au lieu de laisser la page defiler —
   c'est le bug jumeau de l'inbox (862fc88), rejoue sur `.prepa-cal-content` puis
   `.prepa-goals-content`. Troisieme rechute evitee. */
.bienv-defile { flex: 1 0 auto; }
.bienv-actions { flex: 0 0 auto; padding-top: 22px; }
/* Le logo reprend les memes `<span>` que l'ecran d'auth, donc la meme pulsation
   sur SET. Il manquait : la marque disparaissait entre deux ecrans consecutifs. */
.bienv-logo {
  display: flex; align-items: center; margin-bottom: 14px;
  font-family: var(--font-display); font-weight: 900;
  font-size: 1.5rem; letter-spacing: 0.06em;
}
/* La carte parle la grammaire V2 commune, celle des 34 surfaces tokenisees en
   C119 — pas une surface inventee pour l'occasion. */
.bienv-carte {
  background: linear-gradient(165deg, var(--surface-card-a), var(--surface-card-b));
  border: 1px solid var(--surface-card-brd);
  border-radius: var(--radius-xl);
  padding: 20px 18px 4px;
}
.bienv-tete { margin-bottom: 26px; }
.bienv-titre {
  font-family: var(--font-display);
  font-size: 2rem; font-weight: 800; letter-spacing: -.02em;
  color: var(--text-primary); margin: 0 0 8px;
}
/* Le trait degrade des titres de vue, meme grammaire que partout ailleurs. */
.bienv-titre::after {
  content: ''; display: block; width: 34px; height: 3px; margin-top: 9px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--theme-color), var(--theme-secondary));
}
.bienv-sous {
  margin: 14px 0 0; font-size: var(--fs-sm); line-height: 1.55; color: var(--text-2);
}
.bienv-champ { margin-bottom: 20px; }
.bienv-champ label {
  display: block; margin-bottom: 8px;
  font-size: var(--fs-xs); font-weight: 600; color: var(--text-2); letter-spacing: .03em;
}
.bienv-champ .form-input { width: 100%; }
.bienv-aide {
  margin: 7px 0 0; font-size: var(--fs-xs); line-height: 1.5; color: var(--text-3);
}
.bienv-err {
  min-height: 0; margin-bottom: 10px;
  font-size: var(--fs-sm); color: var(--danger, #ff4757);
}
.bienv-err:empty { display: none; }
.bienv-ok { width: 100%; justify-content: center; margin-top: 6px; }
/* « Passer » reste un vrai bouton lisible, pas un lien fantome : quelqu'un qui
   ne veut pas repondre ne doit pas avoir a chercher la sortie. Plancher 44px. */
.bienv-passer {
  display: block; width: 100%; min-height: 44px; margin-top: 6px; padding: 12px;
  background: none; border: none;
  color: var(--text-2); font-size: var(--fs-sm); font-weight: 600; cursor: pointer;
}
.bienv-passer:active { opacity: .6; }
/* C123 — le mot qui remplace le mur de quatre tirets, sur un compte neuf. */
.stats-vide {
  margin: -6px 0 4px; text-align: center;
  font-size: var(--fs-xs); line-height: 1.5; color: var(--text-3);
}
.stats-vide[hidden] { display: none; }

/* ══ C125 — L'ATTENUATION PAR `opacity` EST AVEUGLE AU THEME ════════════════
   Le renderer attenue plusieurs tokens avec une `opacity` posee PAR-DESSUS la
   couleur de theme. Or l'opacite ne fond pas vers une teinte, elle fond vers le
   FOND : sur le noir elle assombrit (l'effet voulu), sur le blanc elle delave.
   Un jeton corrige pour le contraste est donc defait par l'opacite qui le suit.

   Mesure au pixel, en clair, avant :

       .fw-distance   opacity .55  ->  2,50:1
       .fw-amrap-dur  opacity .75  ->  2,28:1
       .fw-emom-dur   opacity .80  ->  3,59:1
       .fw-sep        opacity .50  ->  2,25:1

   ⚠ LE THEME SOMBRE N'EST PAS TOUCHE. Ces regles sont scopees `[data-theme=
   "light"]` : en sombre l'opacite fonctionne, et la changer ferait bouger un
   rendu que personne n'a demande de changer.

   L'ATTENUATION RESTE — elle passe par la COULEUR au lieu de l'opacite. Cible
   4,6:1 : juste au-dessus du plancher AA, donc nettement plus doux que le texte
   principal (17:1) et que les repetitions (6,2:1). On lit la hierarchie, on lit
   aussi les mots.

   ⚠ REGLE DURABLE : pas d'`opacity` sur du texte colore. La hierarchie se dit
   avec un ton, qui peut differer par theme ; l'opacite ne le peut pas. */
/* `.fw-distance` n_a plus besoin d_override : l_opacite a 0.9 suffit dans les
   deux themes. Une regle en moins vaut mieux qu_une regle scopee. */
[data-theme="light"] .fw-amrap-dur { opacity: 1; color: #b16000; }   /* 2,28:1 -> 4,62:1 */
[data-theme="light"] .fw-emom-dur  { opacity: 1; color: #a049e2; }   /* 3,59:1 -> 4,62:1 */
[data-theme="light"] .fw-sep       { color: #68749a; }               /* 2,25:1 -> 4,61:1 */

/* ══ C126 — LE MESSAGE VIT SOUS SON CHAMP ═══════════════════════════════════
   « Remplis tous les champs » ne disait pas lequel. Le message est desormais
   sous le champ fautif, et le champ lui-meme se signale — la couleur seule ne
   suffirait pas (daltonisme), mais ici elle DOUBLE un texte explicite, elle ne
   le remplace pas. */
.champ-err {
  margin: 6px 0 0;
  font-size: var(--fs-xs);
  line-height: 1.45;
  color: var(--danger, #ff4757);
}
.champ-err[hidden] { display: none; }
.form-input.champ-fautif {
  border-color: var(--danger, #ff4757);
}

/* C128 — la liste de desambiguisation, meme grammaire que les contacts
   recents juste en dessous. */
.choix-destinataire { margin-top: 10px; display: block; }
.choix-destinataire[hidden] { display: none; }
