  :root{
    /* Gamme du thème (2026-10-11, GO de Thierry ; règle « Thème » de
       CLAUDE.md) : tailles de texte nommées et couleurs nommées. Les
       écrans les utilisent par var(--…) (ou color-mix() pour une
       transparence), jamais une valeur écrite en dur. */
    --fs-2xs: 10px; --fs-xs: 11px; --fs-sm: 12px; --fs-md: 13px; --fs-lg: 14px; --fs-xl: 15px; --fs-2xl: 16px; --fs-3xl: 18px; --fs-4xl: 24px;
    /* Compléments de la gamme (2026-10-11, décision T2 : grandes et très
       petites tailles nommées telles quelles, rien ne bouge). Rôles des
       tailles hors échelle : icônes de la visionneuse et de N0, nom d'une
       carte de voyage, titre de fin de voyage, titres fluides (h1). */
    --fs-4xs: 8px; --fs-3xs: 9px; --fs-5xl: 26px; --fs-6xl: 30px; --fs-7xl: 46px;
    --fs-icon: 17px; --fs-icon-md: 19px; --fs-icon-lg: 20px;
    --fs-card-title: 23px; --fs-end-title: 27px;
    --fs-h1: clamp(26px, 3.6vw, 44px); --fs-h1-trip: clamp(42px, 6vw, 84px); --fs-h1-home: clamp(44px, 8vw, 96px); --fs-h1-n0: min(clamp(52px, 9.4vw, 104px), calc((min(100vw, var(--text-max)) - 32px) / 8.3));
    --black-pure: #000000;
    --c-bleu-clair: #8fbfdc;
    --c-bleu-clair-2: #a9c7f2;
    --c-bleu-moyen: #6a9fc0;
    --c-bleu-moyen-2: #6ea0e6;
    --c-gris-moyen: #a0a0a0;
    --c-gris-moyen-2: #8a7f6a;
    --c-gris-moyen-3: #7f7f7f;
    --c-gris-sombre: #2a2620;
    --c-or-clair: #f5ecd0;
    --c-or-clair-2: #ded6c4;
    --c-or-clair-3: #efc777;
    --c-or-moyen: #f5d90a;
    --c-or-moyen-2: #f2b632;
    --c-or-moyen-3: #b9a26a;
    --c-or-moyen-4: #d2bd85;
    --c-orange-moyen: #e2b152;
    --c-orange-moyen-10: #d9a53f;
    --c-orange-moyen-11: #e0b07a;
    --c-orange-moyen-12: #c9a26a;
    --c-orange-moyen-13: #e0a43a;
    --c-orange-moyen-2: #c4622d;
    --c-orange-moyen-3: #e2a530;
    --c-orange-moyen-4: #e08a6b;
    --c-orange-moyen-5: #a97418;
    --c-orange-moyen-6: #d9a441;
    --c-orange-moyen-7: #d99a2b;
    --c-orange-moyen-8: #e08a5a;
    --c-orange-moyen-9: #b8944d;
    --c-orange-sombre: #3d2a0a;
    --c-orange-sombre-2: #493d29;
    --c-orange-sombre-3: #6b5d44;
    --c-orange-sombre-4: #0a0805;
    --c-rouge-moyen: #b33a3a;
    --c-rouge-moyen-2: #d9614f;
    --c-rouge-moyen-3: #c0392b;
    --c-rouge-moyen-4: #c0493a;
    --c-rouge-moyen-5: #c0584a;
    --c-rouge-moyen-6: #e07a6a;
    --c-rouge-moyen-7: #e06666;
    --c-rouge-sombre: #7a1f1f;
    --c-vert-moyen: #6bb36b;
    --c-vert-moyen-2: #5fb56a;
    --c-vert-moyen-3: #78c878;
    --c-vert-moyen-4: #6f9b4e;
    --c-vert-moyen-5: #a9c98a;
    --c-vert-moyen-6: #7fae6a;
    --c-vert-moyen-7: #9cc887;
    --c-vert-moyen-8: #8fd19a;
    --c-vert-moyen-9: #7fbf6a;
    --c-vert-sombre: #38492e;
    --c-vert-sombre-2: #3f5232;
    --c-vert-sombre-3: #2f3d25;
    --c-vert-sombre-4: #10180c;
    --ink: #141009;
    --night: #1a150d;
    --white: #ffffff;
    /* Couleurs nommées du thème (2026-10-11, point 14) : succès (Actif) et
       attention (Inactif : état normal pendant le travail, pas une erreur). */
    --color-success: #5fa84a;
    --color-warning: #d98c2b;
    --sable: #e9dcc0;
    --canvas-bg: #ded6c4;
    /* Hiérarchie de fond à 5 niveaux (voir projet/journal/INSTRUCTIONS_CLAUDE_CODE.md) :
       0 site, 1 voyage (--sable, pas de variable dédiée), 2 activité,
       3 visionneuse normale, 4 plein écran natif — crescendo de contraste
       du plus clair au plus sombre, tons safari (beige/brun) pour 1-3. */
    --activity-bg: #d2c6ad; /* Niveau 2 : ~10% plus foncé que --sable. */
    /* Niveau 3 : revenu (2026-08-15) à la teinte très sombre d'origine,
       l'éclaircissement du 2026-08-14 étant allé trop loin, puis éclairci
       d'un cran très léger (2026-08-16, rgb 20,16,9 -> 30,25,17) — la
       teinte d'origine restait un tout petit peu trop sombre. Voir
       projet/journal/INSTRUCTIONS_CLAUDE_CODE.md. */
    /* Bug corrigé (2026-08-16) : alpha 0.97 (pas 1) laissait passer un
       léger résidu de la page derrière (grille de vignettes visible en
       transparence/assombrie) — reproduit systématiquement, fenêtre
       rétrécie ou non. #lightbox couvre tout le viewport (inset:0), donc
       rien ne justifiait une transparence partielle ici. --fullscreen-bg
       (niveau 4, juste en dessous) est un hex plein sans canal alpha,
       jamais concerné par ce bug. */
    --lightbox-bg: rgb(30,25,17);
    /* Niveau 4, dérivé de --lightbox-bg (2026-08-31, système de thèmes) :
       le plein écran natif a TOUJOURS été délibérément plus sombre que la
       fenêtre (voir historique ci-dessus) — plutôt qu'une 2e couleur de
       thème à stocker/gérer séparément, un seul réglage ("fond N3/N4")
       suffit : color-mix() assombrit automatiquement, pour n'importe quel
       thème futur, dans les mêmes proportions qu'aujourd'hui (70% de la
       couleur d'origine mélangés à du noir — reproduit #14100d à partir de
       rgb(30,25,17) à quelques unités près, imperceptible). */
    --fullscreen-bg: color-mix(in srgb, var(--lightbox-bg) 70%, black);
    --dusk: #241f16;
    --dusk-soft: #362d1e;
    --dusk-softer: #493d29; /* Titre voyage (h1) — valeur intermédiaire (2026-08-31, un cran plus foncé que l'essai précédent #5c4d34), toujours plus clair que --dusk-soft (#362d1e). */
    --acacia: #4a5d3a;
    --ember: #c4622d;
    --dust-gold: #b8944d;
    --bone: #f6f1e6;
    /* Système de thèmes graphiques (2026-08-31) — voir site/auth/themes.php
       et projet/journal/INSTRUCTIONS_CLAUDE_CODE.md. Ces 13 variables n'existaient pas
       avant ce chantier (les couleurs qu'elles remplacent étaient soit en
       dur, soit fusionnées avec --ember/--bone) ; leurs valeurs ci-dessous
       sont celles du thème "Thème par défaut" (`themes.is_default`, voir
       themes_default_split_migration.sql, 2026-09-23 — thème SÉPARÉ
       d'"Afrique", pas un simple renommage comme la première tentative du
       2026-09-16 l'avait fait par erreur ; les deux thèmes partagent
       aujourd'hui la même palette par construction, mais sont désormais
       deux lignes distinctes, modifiables indépendamment) — le thème
       réellement assigné au voyage affiché les écrase en JS au chargement
       (applyTheme() dans app.js), via setProperty() sur :root, jamais une
       feuille de style dupliquée par thème. Ce bloc reste néanmoins un
       second exemplaire statique de ces valeurs (nécessaire : le CSS doit
       peindre quelque chose AVANT que le JS n'ait eu le temps de charger/
       appliquer le vrai thème) — à tenir manuellement synchronisé si
       "Thème par défaut" est un jour modifié en base, sous peine d'un bref
       flash différent au tout premier chargement de la page (jamais après,
       une fois applyTheme() passé). */
    --feed-bg: var(--canvas-bg); /* Niveau 5 (fil continu mobile) — partageait --canvas-bg jusqu'ici sans variable dédiée. */
    /* 3 zones de fond par niveau, pas une seule (2026-08-31, bug corrigé
       après le premier lot du système de thèmes — voir .canvas/
       .crumbbar/body.diaporama-page dans le CSS plus bas pour le détail des /* renommage-ok */
       sélecteurs concernés par chaque zone) : A = page (en dehors de la
       zone principale), B = bandeaux (en-tête/pied de page), C = zone
       défilante centrale. --sable (couleur de base du site, partagée avec
       des contextes non themés) servait jusqu'ici de valeur par défaut
       pour B et C aux 2 niveaux — remplacée par des variables dédiées,
       toutes initialisées à la même valeur (apparence "Afrique" inchangée). */
    --n1-banner-bg: #e9dcc0;
    --n1-content-bg: #e9dcc0;
    --n2-page-bg: #ded6c4;
    --n2-banner-bg: #e9dcc0;
    --card-label-color: #c4622d; /* Libellé des vignettes, niveau 1 (.activity-title) — même valeur que --ember aujourd'hui, mais un réglage de thème distinct et indépendant. */
    --card-count-color: #362d1e; /* Icône + nombre de photos, niveau 1 (.activity-count) — même valeur que --dusk-soft aujourd'hui. */
    --card-num-bg: #141009; /* Fond de la pastille numérotant chaque activité, niveau 1 (.activity-num) — appliqué avec une opacité fixe via color-mix, comme les autres cas de transparence de ce fichier. */
    --settings-bg: #1a150d; /* Fond de l'écran de paramétrage (Compte/Réglages) — remplace rgba(26,21,13,0.97) en dur. */
    --settings-tab-active-color: #c4622d; /* Onglet sélectionné (2026-09-02, nouveau paramètre) — décorrélé de --ember : même valeur par défaut ("Afrique"), mais réglable indépendamment de la couleur signature du thème. */
    --settings-label-color: #b8944d; /* Libellé de rubrique (ex. "Date", "Focale") — même valeur que --dust-gold aujourd'hui. */
    --settings-value-color: #f6f1e6; /* Valeur de rubrique — même valeur que --bone aujourd'hui. */
    --secondary-text-color: #f6f1e6; /* Texte secondaire de l'écran de paramétrage — même principe. */
    /* Couleur de texte PAR DÉFAUT de tout l'écran de paramétrage (2026-09-03,
       nouveau paramètre "Couleur des textes et légendes") : posée sur
       .account-modal (voir plus bas), héritée par tout texte qui ne
       redéfinit pas déjà sa propre couleur — .settings-label/.settings-tab
       (base, hors état actif) migrés dessus au passage, ainsi que tout ce
       qui n'avait encore AUCUNE couleur explicite (ex. Passkeys). Même
       valeur que --bone aujourd'hui (c'était déjà la couleur réellement
       utilisée, en dur, par ces éléments) : zéro changement visuel avant
       personnalisation. */
    --settings-default-text-color: #f6f1e6;
    /* Couleur du TEXTE des boutons d'action (2026-09-03, nouveau
       paramètre "Libellés des boutons d'action") : Modifier/Créer/
       Supprimer, Importer VCF, Activer/Désactiver, Enregistrer/Annuler/
       Appliquer... Même valeur que --bone aujourd'hui (déjà la couleur en
       dur de tous ces boutons) : zéro changement visuel avant
       personnalisation. */
    --action-button-text-color: #f6f1e6;
    /* Polices de thème (2026-09-08, 2 nouveaux paramètres) — famille +
       gras/italique, choisies parmi une liste blanche (jamais de saisie
       libre, voir THEME_FONT_FAMILIES côté serveur). "Titres voyage et
       activités" (title_font) : pilote À LA FOIS h1 (titre du voyage) ET
       .activity-title (libellé de vignette), unifiées à dessein — ce
       dernier changeait jusqu'ici de police indépendamment. Valeurs
       ci-dessous = apparence ACTUELLE de h1 (Fraunces italique), reprise
       telle quelle comme point de départ ("Afrique") ; .activity-title
       change donc bien d'aspect au premier chargement de cette variable,
       changement de conception assumé (demande explicite). */
    --title-font-family: 'Fraunces', serif;
    /* Logo zemb.com (13/10, décision Y1 : Jost 400). Un seul endroit : outils/logo.py
       relit ces deux valeurs et refait tous les fichiers du logo. */
    --logo-font-family: 'Jost', sans-serif;
    --logo-font-weight: 300; /* 13/10, M : léger, jamais en gras (ne concurrence pas « Où partons-nous ? ») */
    --title-font-weight: 400;
    --title-font-style: italic;
    /* "Textes, légendes et données" (text_font) : même périmètre que
       default_text_color ci-dessus (tout ce qui hérite de sa couleur ET
       fixait IBM Plex Mono en dur) — PAS étendu au reste du site (grilles
       admin, galerie publique, visionneuse), portée volontairement
       inchangée (demande explicite). Valeurs ci-dessous = apparence
       actuelle (IBM Plex Mono) : zéro changement visuel avant
       personnalisation. */
    --text-font-family: 'IBM Plex Mono', monospace;
    --text-font-weight: 400;
    --text-font-style: normal;
    /* border_color RETIRÉ comme réglage de thème (2026-09-01, demande
       explicite) : les bordures/séparateurs ci-dessous sont désormais
       dérivées automatiquement de --settings-bg (color-mix avec du blanc,
       même dosage qu'avant), jamais un réglage stocké séparément — plus
       cohérent quel que soit le thème, sans intervention manuelle. */
    --text-max: 1600px; /* R57/R73 : valeur de départ ; app.js et home.js posent min(% de l'écran, maximum) en pixels */
    --grid-max: 2000px;
    --cell-min-voyage: 260px;
    --cell-min-grid: 165px;
    --bar-h: 54px;
    --strip-h: 44px;
    /* Titre du niveau 2, rendu fixe (2026-08-31) : valeur de repli avant la
       première mesure réelle en JS (updatePageHeaderHeight(), voir app.js —
       h1{font-size:clamp(...)} dépend de la largeur de la fenêtre, jamais
       une hauteur fixe fiable). */
    --title-h: 78px;
    /* Deux curseurs de taille de texte (2026-08-21, Réglages → Affichage) :
       multiplicateurs (1 = 100%, taille d'origine), jamais des tailles
       absolues — appliqués via calc() sur chaque sélecteur concerné plutôt
       que sur :root/body entier, pour ne toucher QUE la prose (commentaires,
       titres/légendes de photo, notes) ou QUE l'interface (fil d'Ariane,
       libellés d'activité, badge compte, boutons, badges, résolution de la
       visionneuse) — jamais la mise en page/les espacements eux-mêmes. Voir
       effectiveReadingTextScale()/effectiveUiScale() dans app.js. */
    --reading-text-scale: 1;
    --ui-scale: 1;
    /* Taille du texte des champs de saisie des écrans d'administration
       (2026-10-11) : une seule valeur, partagée par les champs et les
       valeurs en lecture seule (.ro-value). */
    --field-font-size: 13px;
  }
  *{box-sizing:border-box; margin:0; padding:0;}
  /* overscroll-behavior-x sur #lightbox (voir plus bas) s'est révélé sans
     effet (2026-08-19, testé en conditions réelles) : cette propriété n'a
     de prise que sur un vrai conteneur de défilement (overflow:auto/scroll)
     — #lightbox, en position:fixed sans overflow, n'en est pas un ; Chrome
     regarde la racine RÉELLE de défilement de la page (html/body) pour
     décider d'intercepter ou non le glissé deux doigts comme navigation
     arrière/avant native, au-dessus de la page, avant même qu'un événement
     'wheel' n'atteigne le DOM. Posée ici (racine réelle), et non plus
     seulement sur #lightbox. */
  html{scroll-behavior:smooth; overscroll-behavior: none;}
  body{ background: var(--sable); color: var(--dusk); font-family: 'Inter', sans-serif; -webkit-font-smoothing: antialiased; }
  /* Bandeau "voyage désactivé", admin uniquement (2026-09-02/03) — PARTAGÉ
     entre deux origines : injecté tel quel côté SERVEUR par
     photos-gate/_gate.php (admin arrivant directement sur un voyage déjà
     désactivé) ET créé/retiré côté CLIENT par updateVoyageDisabledBanner()
     (app.js) pour une mise à jour en direct sans recharger la page. Même
     id/classe dans les deux cas — jamais deux styles différents à
     maintenir. z-index au-dessus de tout, y compris la visionneuse plein
     écran (voir #lightbox:fullscreen, z-index bien inférieur). */
  /* Semi-transparent (2026-09-03, demande explicite — cachait complètement
     les boutons dessous) : fond en rgba plutôt qu'opaque. "utilisables"
     (pas seulement visibles) exigeait AUSSI pointer-events:none — un fond
     translucide seul aurait laissé ce bandeau capter les clics destinés aux
     boutons en dessous (aucun élément interactif dans ce bandeau lui-même,
     rien à perdre à laisser les clics le traverser). Opacité 82% -> 55%
     (2026-09-27, décision explicite : alignement sur projet/standards/BANNER_STANDARD.md §2,
     aucune exception). */
  /* 2026-10-03 (demande de Thierry) : le bandeau ne recouvre plus l'en-tête —
     sa hauteur (--dis-h, mesurée par app.js) décale la barre du haut, le
     titre, le bandeau des activités, la visionneuse et le contenu. Fond
     plein. Croix : réduit en pastille « Désactivé » pour la session. */
  :root{ --dis-h: 0px; }
  .voyage-disabled-banner{ position:fixed; top:0; left:0; right:0; z-index:2147483647; background: var(--c-orange-moyen); color:var(--night); text-align:center; padding:8px 40px; font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif; font-size:var(--fs-md); font-weight:600; box-sizing:border-box; }
  .voyage-disabled-close{ position:absolute; right:8px; top:50%; transform:translateY(-50%); border:0; background:none; color:inherit; font-size:var(--fs-xl); line-height:1; padding:4px 6px; cursor:pointer; }
  .voyage-disabled-banner.is-pill{ left:12px; right:auto; top:auto; bottom:calc(var(--bar-h, 48px) + 8px); padding:4px 12px; border-radius:999px; font-size:var(--fs-sm); cursor:pointer; box-shadow:0 2px 8px color-mix(in srgb, var(--ink) 30%, transparent); }
  .voyage-disabled-banner.is-pill .voyage-disabled-text, .voyage-disabled-banner:not(.is-pill) .voyage-disabled-pill{ display:none; }
  .voyage-disabled-banner.is-pill .voyage-disabled-close{ display:none; }
  #lightbox.lightbox{ top: var(--dis-h); }
  #lightbox.lightbox:fullscreen{ top: 0; }
  /* Titre du niveau 2 rendu fixe (2026-08-31, demande explicite — "il doit
     rester fixe, ancré à l'en-tête, comme le reste des éléments de
     navigation déjà figés") : padding-top réserve maintenant AUSSI
     --title-h, en plus de --bar-h/--strip-h (voir header.hero plus bas
     pour le passage en position:fixed lui-même, niveau 2 uniquement). Le
     bouton "Lire depuis le début" (.controls, juste après header.hero
     dans le DOM) continue de défiler avec la grille (demande explicite) —
     seul header.hero quitte le flux. */
  /* 3 zones de fond distinctes par niveau (2026-08-31, système de thèmes,
     bug corrigé — "il existe en réalité 3 zones, pas une seule") : A =
     fond de page (ici, --canvas-bg = zone A du niveau 1) ; B = bandeaux
     (en-tête/pied de page, voir .crumbbar/.bottombar/.stripbar/
     header.hero plus bas) ; C = zone défilante centrale (voir .canvas
     plus bas). Niveau 1 = valeur PAR DÉFAUT de cette règle (aucune
     surcharge) ; niveau 2 surchargé juste en dessous
     (body.diaporama-page:not(.voyage-level)), même patron que header.hero déjà /* renommage-ok */
     en place. */
  body.diaporama-page{ background: var(--canvas-bg); padding-top: calc(var(--bar-h) + var(--title-h) + var(--strip-h) + var(--dis-h)); padding-bottom: 34px; } /* lot B (2026-10-10) : plus de barre en bas, seul le bandeau défilant (34 px) est réservé */ /* renommage-ok */
  body.diaporama-page:not(.voyage-level){ background: var(--n2-page-bg); } /* renommage-ok */
  /* Espace du bandeau ET du titre retirés du flux au niveau 1 (2026-08-25,
     bug corrigé, étendu le 2026-08-31 avec --title-h) : ni .stripbar
     (masquée, display:none) ni header.hero (jamais fixe à ce niveau, voir
     plus bas) n'occupent quoi que ce soit en position fixe ici — seul
     --bar-h (crumbbar) reste réservé. */
  /* header.hero fixe désormais aux DEUX niveaux (2026-08-31, demande
     explicite — "comme c'est déjà le cas pour l'en-tête du niveau 2") :
     --title-h reste réservé ici aussi ; --strip-h ne l'est PAS (bandeau
     d'activités masqué au niveau 1, rien à réserver pour lui). */
  body.diaporama-page.voyage-level{ padding-top: calc(var(--bar-h) + var(--title-h) + var(--dis-h)); } /* renommage-ok */
  /* Zone C (centrale, défilante) du niveau 1 — --sable (couleur de base du
     site, partagée avec bien d'autres contextes non themés) remplacée par
     une variable dédiée (2026-08-31, système de thèmes) : .canvas.
     activity-level (niveau 2, juste en dessous) porte déjà sa propre
     variable (--activity-bg), .canvas seul (niveau 1) n'en avait pas. */
  .canvas{ max-width: var(--text-max); margin: 0 auto; min-height: calc(100vh - var(--bar-h) - 34px - var(--title-h) - var(--strip-h)); background: var(--n1-content-bg); box-shadow: 0 24px 60px color-mix(in srgb, var(--ink) 25%, transparent); }
  .wrap-text{ max-width: var(--text-max); margin: 0 auto; padding: 0 32px; }
  .wrap-grid{ max-width: var(--grid-max); margin: 0 auto; padding: 0 32px; }
  .view{ display:none; }
  .view.active{ display:block; }
  /* Niveau 1 (voyage) : pas de règle dédiée, hérite de --sable via .canvas. */
  /* Niveau 2 (activité) : fond dédié, posé sur .canvas lui-même (pas sur
     #view-activity seul) pour couvrir aussi l'en-tête partagé (.wrap-text,
     hors de #view-activity dans le DOM) — sinon le fond ne commençait
     qu'en dessous de l'en-tête, pas uniforme sur toute la page (bug
     corrigé 2026-08-15). Classe posée par showActivity()/showVoyage(). */
  .canvas.activity-level{ background: var(--activity-bg); }

  /* Réglage "Autoriser le téléchargement des photos" (décision du voyage, pas
     une préférence personnelle — voir app.js). Simple frein pour un visiteur
     ordinaire, PAS une vraie protection : n'importe qui d'un peu technique
     contourne ça via les outils de développement du navigateur. */
  body.no-download img{ -webkit-user-drag: none; }

  /* Flag "Partage de photos" (2026-08-20, par utilisateur — voir app.js) :
     masque le partage natif et l'enregistrement au niveau photo (lightbox).
     Jamais les likes/commentaires (.lb-counter-group), qui restent toujours
     visibles quel que soit ce flag. */
  body.no-share .lb-share-widget, body.no-share .lb-export-widget{ display:none; }

  /* ---------- FIL D'ARIANE FIXE + BARRE BASSE ---------- */
  /* Zone B (bandeaux, en-tête/pied de page) — --sable remplacée par une
     variable de niveau dédiée (2026-08-31, système de thèmes) : niveau 1
     par défaut, niveau 2 surchargé juste en dessous (même patron que
     .canvas/body.diaporama-page ci-dessus). */ /* renommage-ok */
  .crumbbar, .bottombar{ position: fixed; left: 50%; transform: translateX(-50%); width: 100%; max-width: var(--text-max); z-index: 50; height: var(--bar-h); background: var(--n1-banner-bg); display:flex; align-items:center; }
  body.diaporama-page:not(.voyage-level) .crumbbar, /* renommage-ok */
  body.diaporama-page:not(.voyage-level) .bottombar{ background: var(--n2-banner-bg); } /* renommage-ok */
  /* Revenu à align-items:center (2026-08-30, demande explicite — annule le
     passage à flex-start du 2026-08-29) : .crumbs, .lang-switcher et
     .account-widget (ajouté en vrai comme enfant de .crumbbar, voir
     renderAccountWidget() dans app.js) sont recentrés sur la ligne comme
     les pastilles à droite. L'alignement des LIBELLÉS du fil d'Ariane
     entre eux par leur haut respectif reste assuré à l'intérieur de
     .crumbs (align-items:flex-start, voir plus bas) — un souci d'alignement
     interne, indépendant du centrage de la ligne entière. */
  .crumbbar{ top:var(--dis-h); border-bottom: 1px solid color-mix(in srgb, var(--dusk) 18%, transparent); }
  /* justify-content:flex-end (2026-08-25, bug corrigé) : .zoom-slider est
     désormais le SEUL enfant de .bottombar (l'ancien lien "Tous les
     voyages", #allVoyagesLink, a été retiré — voir renderCrumbs() dans
     app.js, ce lien vit maintenant dans le fil d'Ariane). L'ancien
     space-between (pensé pour DEUX enfants, lien à gauche/curseur à
     droite) traitait un enfant unique comme flex-start — donc à GAUCHE —
     dès que ce lien a été masqué puis retiré : flex-end fixe ça
     définitivement, quel que soit le nombre d'enfants présents. */
  .bottombar{ bottom:0; border-top: 1px solid color-mix(in srgb, var(--dusk) 18%, transparent); justify-content: flex-end; }
  /* align-items:flex-start, PAS center (2026-08-25, bug persistant) : le
     soulignement du premier maillon ("Zambie 2022"/"Tous mes voyages",
     cliquable) n'existe pas sur les suivants (texte courant, non
     cliquable) — aligner par le bas/la ligne de base inclut cet artefact
     dans le calcul et désaligne le rendu. Par le HAUT, la présence ou non
     du soulignement (qui se dessine sous la ligne de base, jamais
     au-dessus) n'entre plus en jeu. */
  /* align-items:center (2026-08-31, bug corrigé — mesure réelle via
     l'extension Chrome) : flex-start calait le TEXTE sur le haut de la
     ligne (centre mesuré à 18px) alors que l'icône Home (34px, elle-même
     centrée par ce même align-items) et l'avatar compte (34px, centré dans
     .account-btn) avaient tous deux leur centre à 27px — le texte, bien
     plus fin (17.5px), ne suivait donc PAS le centrage puisque flex-start
     l'ancrait à son propre haut plutôt qu'au centre de la ligne. Sans
     incidence sur l'alignement des libellés entre eux (tous de même
     hauteur, un centrage les garde alignés tout autant qu'un flex-start
     l'aurait fait). */
  .crumbs{ flex: 1 1 auto; min-width: 0; padding: 0 32px; font-family:var(--text-font-family); font-size: calc(var(--fs-lg) * var(--ui-scale)); letter-spacing: 0.02em; display:flex; align-items:center; }
  /* padding retiré (2026-08-24, bug corrigé — décalage vertical avec .here) :
     un <button> a un line-height par défaut différent d'un <span>, et
     6px 0 de padding l'agrandissait encore, le désalignant visuellement du
     "/" et du nom d'activité malgré align-items:center sur .crumbs (qui
     centre chaque enfant dans SA propre boîte, pas d'après leur texte).
     line-height:inherit aligne les deux éléments sur une base commune. */
  .crumbs button{ background:none; border:none; font-family:inherit; font-size:inherit; line-height:inherit; color: var(--ember); cursor:pointer; padding: 0; font-weight: 500; text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--c-orange-moyen-2) 40%, transparent); text-underline-offset: 3px; transition: text-decoration-color .15s ease; }
  .crumbs button:hover{ text-decoration-color: var(--ember); }
  .crumbs .sep{ margin: 0 10px; opacity: 0.45; }
  .crumbs .here{ color: var(--dusk); font-weight: 500; }
  /* Icône Home réintroduite (2026-08-26, demande explicite) devant "Tous
     mes voyages" uniquement (voir renderCrumbs() dans app.js) — même
     pastille terracotta qu'avant son retrait du 2026-08-25, réduite en
     proportion de la taille réduite du fil d'Ariane (17px -> 13.5px à
     l'époque) : 30px de pastille/24px d'icône étaient surdimensionnés
     face à un texte devenu plus petit. */
  .home-icon-badge{ display:inline-flex; align-items:center; justify-content:center; width:30px; height:30px; border-radius:50%; background: var(--ember); color: var(--bone); flex: 0 0 auto; }
  /* Agrandi (2026-08-28, demande explicite — annule le rétrécissement du
     2026-08-27) : même taille que l'avatar compte (.account-avatar, 34px),
     niveaux 1 et 2 (tous deux rendus par renderCrumbs(), voir app.js). */
  .crumbs .home-icon-badge{ width:34px; height:34px; }
  .crumbs .home-icon-badge svg{ width:22px; height:22px; }
  /* Bouton icône séparé (2026-08-29, bug corrigé, voir renderCrumbs() dans
     app.js) : align-self:center le centre verticalement sur la ligne du
     fil d'Ariane (dont il définit désormais la hauteur, 34px) SANS
     affecter l'alignement flex-start (par le haut) des autres éléments de
     .crumbs — l'ancien vertical-align sur l'icône imbriquée dans le texte
     décalait le texte lui-même dès que l'icône dépassait sa hauteur de
     ligne. */
  .crumb-home-icon{ background:none; border:none; padding:0; margin-right:8px; cursor:pointer; align-self:center; flex:0 0 auto; }
  /* Verrou de navigation pendant que Réglages est ouvert (2026-09-01,
     demande explicite) : Accueil/"Tous mes voyages" (#crumbAllVoyagesIcon/
     #crumbAllVoyages, voir renderCrumbs() dans app.js) font une VRAIE
     navigation (location.href='/'), qui détruirait tout l'état de la
     fenêtre Réglages (brouillon de thème en cours, sous-onglet visité...)
     — pointer-events:none bloque le clic AVANT même que son gestionnaire
     (goAllVoyages) ne s'exécute, pas besoin de le modifier lui-même.
     .settings-modal-open posée/retirée par openAccountModal()/
     closeAccountModal(). */
  body.settings-modal-open #crumbAllVoyages, body.settings-modal-open #crumbAllVoyagesIcon{ pointer-events:none; opacity:0.35; cursor:not-allowed; }
  /* Bouton Compte accessible même lightbox ouverte (2026-08-13) : .crumbbar
     est en z-index:50 contre 100 pour .lightbox — sans ça, "Infos
     techniques" est inatteignable pendant qu'on regarde une photo en
     plein écran. Plutôt que de faire remonter toute la barre (fil
     d'Ariane compris, qui n'a pas sa place flottant sur la photo), la
     barre passe en "passthrough" : transparente, clics ignorés, sauf sur
     le widget compte lui-même qui reste seul visible et cliquable. :has()
     évite un état JS à synchroniser en plus de la classe déjà posée sur
     #lightbox. */
  body:has(#lightbox.open) .crumbbar{ background: transparent; border-bottom: none; box-shadow: none; z-index: 101; pointer-events: none; }
  body:has(#lightbox.open) .crumbbar .crumbs{ visibility: hidden; }
  /* Bug corrigé (2026-08-16) : positionner le widget compte en
     position:fixed avec un top/right estimés à la main (ancienne approche)
     ne le mettait pas fiablement "tout à droite, en bout de ligne" — un
     décalage qui ne se voit qu'en vrai navigateur. Remplacé par un
     déplacement RÉEL du nœud DOM dans #lbTopbarRight à l'ouverture de la
     lightbox (voir openLightbox()/closeLightbox() dans app.js) : dernier
     enfant d'un flexbox, sa position "tout à droite" et son espacement
     (gap régulier avec les autres icônes) sont garantis par construction,
     pas par une estimation de pixels. Il n'a donc plus besoin d'être ciblé
     via .crumbbar ici — seulement annuler la marge pensée pour son
     emplacement d'origine dans .crumbbar (margin-left:auto etc., inutile/
     fausse une fois dans .lb-topbar-right). */
  #lbTopbarRight .account-widget{ margin: 0; }
  /* Widget compte simplifié niveaux 3/4 (pastille seule, plus de nom/email)
     — cohérent avec une rangée d'icônes fine. Le menu déroulant lui-même
     (.account-menu, ouvert au clic) n'est pas concerné, toujours complet.
     Scopé par body:has(#lightbox.open), pas #lbTopbarRight : reste correct
     que le widget soit ou non déjà déplacé au moment où cette règle
     s'applique. */
  body:has(#lightbox.open) .account-names{ display: none; }
  /* Absent des niveaux 3/4 (2026-08-16), pas seulement repositionné : reste
     uniquement présent aux niveaux 1 (Voyage) et 2 (Activité). */
  body:has(#lightbox.open) .crumbbar .lang-switcher{ display: none; }
  /* Fond propre retiré (2026-08-27) : le bouton compte vit maintenant dans
     .lb-topbar-right, qui a déjà sa propre pastille de groupe (voir plus
     bas) — un second fond ici aurait fait une pastille dans la pastille. */
  /* ---------- WIDGET COMPTE (avatar + nom, menu déroulant) ----------
     Seul point d'entrée vers le panneau de réglages désormais (l'ancienne
     icône ⚙️ autonome a été retirée) : margin-left:auto pousse ce widget
     à droite de la crumbbar, rôle qu'assumait auparavant le gear-btn. */
  .account-widget{ position:relative; flex: 0 0 auto; margin-left: auto; margin-right: 28px; }
  /* Bug corrigé (2026-08-31, "info-bulle avatar pas alignée avec le drapeau
     de langue") : hauteur du bouton laissée au contenu (avatar 34px +
     padding) jusqu'ici, jamais strictement égale au .lang-btn voisin
     (40x40 fixe) — l'info-bulle de chacun (ancrée sur son propre bas via
     top:calc(100% + 10px)) se retrouvait donc à 2 hauteurs différentes.
     Hauteur fixée à 40px, identique à .lang-btn, pour partager la même
     ligne de référence. */
  .account-btn{ display:flex; align-items:center; gap:8px; height:40px; background:none; border:none; cursor:pointer; padding:4px 6px; border-radius:20px; transition: background .15s ease; }
  .account-btn:hover{ background: color-mix(in srgb, var(--dusk) 6%, transparent); }
  /* Tailles augmentées (2026-08-24, demande explicite) : pastille 28px->
     34px, nom 12.5px->14.5px, email 10px->11.5px. */
  .account-avatar-wrap{ position:relative; display:inline-flex; flex:0 0 auto; }
  .account-avatar{ flex:0 0 auto; width:34px; height:34px; border-radius:50%; overflow:hidden; background: var(--ember); color: var(--bone); display:flex; align-items:center; justify-content:center; font-family:var(--text-font-family); font-size:calc(var(--fs-lg) * var(--ui-scale)); font-weight:normal; }
  /* Badge admin (2026-08-29) : même patron que .lb-like-count/.lb-comment-count
     (pastille superposée au coin bas-droit) — masqué par défaut, affiché en
     JS (renderAccountWidget()) seulement pour une session administrateur.
     Étoile de shérif (2026-08-30, remplace le bouclier initial) : cercle
     INCHANGÉ (15px), icône agrandie 10px→12px→14px pour un rendu plus
     visible sans changer l'encombrement du badge — voir le SVG dans
     renderAccountWidget() (fill #2a2620, sans contour propre — seul le
     cercle du badge en porte un). Fond du cercle éclairci vers un doré
     très clair, proche du blanc (2026-08-30, l'ancien #d4af37 jugé trop
     sombre) : l'étoile sombre par-dessus garde un bon contraste. */
  .account-admin-badge{ display:none; position:absolute; bottom:-2px; right:-2px; width:15px; height:15px; border-radius:50%; background: var(--c-or-clair); align-items:center; justify-content:center; box-sizing:border-box; border:1.5px solid var(--c-gris-sombre); }
  /* Photo de profil (2026-08-28) : remplit le même cercle que les
     initiales — voir renderAccountWidget() dans app.js pour le choix
     entre les deux. object-fit:cover plutôt que contain : l'image est
     déjà recadrée en carré côté client (cropAndResizeAvatarFile()), cover
     évite juste tout écart d'arrondi/sous-pixel de laisser une frange de
     fond visible au bord du cercle. */
  .account-avatar-img{ display:block; width:100%; height:100%; object-fit:cover; }
  .account-names{ display:flex; flex-direction:column; align-items:flex-start; line-height:1.2; }
  .account-name{ font-family:var(--text-font-family); font-size:calc(var(--fs-xl) * var(--ui-scale)); color: var(--dusk); }
  .account-email{ font-family:var(--text-font-family); font-size:calc(var(--fs-sm) * var(--ui-scale)); color: var(--dusk-soft); opacity:0.75; }
  /* overflow:hidden RETIRÉ (2026-09-20, bug corrigé — "le sous-menu ne se
     déclenche jamais") : c'est la vraie cause, pas le sens de la flèche.
     .account-submenu (sous-menu "Simulation") est un DESCENDANT de
     #accountMenu — même position:absolute par rapport à son propre parent
     direct (.account-submenu-trigger), un overflow:hidden sur un ANCÊTRE
     plus haut (ici #accountMenu) clippe quand même tout ce qui dépasse sa
     propre boîte, quel que soit le sens d'ouverture du sous-menu. Coins
     arrondis (la raison d'être de cet overflow:hidden — clipper le fond au
     survol des boutons aux coins du menu) repris différemment ci-dessous :
     arrondis posés directement sur le premier/dernier enfant plutôt que sur
     le conteneur. */
  .account-menu{ display:none; position:absolute; top:calc(100% + 8px); right:0; min-width:180px; background: color-mix(in srgb, var(--night) 97%, transparent); border-radius:8px; box-shadow:0 20px 45px color-mix(in srgb, var(--ink) 35%, transparent); z-index:70; flex-direction:column; }
  .account-menu > *:first-child{ border-radius: 8px 8px 0 0; }
  .account-menu > *:last-child{ border-radius: 0 0 8px 8px; }
  .account-widget.open .account-menu{ display:flex; }
  .account-menu button{ display:block; width:100%; text-align:left; background:none; border:none; color:var(--bone); font-family:var(--text-font-family); font-size:var(--fs-md); padding:11px 16px; cursor:pointer; }
  .account-menu button:hover{ background: color-mix(in srgb, var(--bone) 10%, transparent); }
  /* Couleur signature du thème (2026-09-08, demande explicite), plus une
     teinte fixe #e08a6b indépendante du thème choisi. */
  .account-menu button.danger{ color: var(--ember); border-top:1px solid color-mix(in srgb, var(--bone) 12%, transparent); }
  /* Sous-menu "Simulation" (2026-09-15, refondu en vrai menu en cascade au
     survol le 2026-09-20, demande explicite — voir accountMenuButtonsHtml()/
     ensureImpersonateSubmenuLoaded() dans app.js) : comportement classique
     de menu d'application — survol de la ligne (.account-submenu-trigger)
     révèle la flèche (opacity 0 -> 1), survol de la flèche OU du sous-menu
     lui-même le déploie (:has(), déjà utilisé ailleurs sur le site — voir
     body:has(#lightbox.open)). Entièrement en CSS, aucun état JS à ouvrir/
     fermer/nettoyer (contrairement aux deux versions précédentes, chacune
     avec son propre bug d'état résiduel). */
  /* Bug corrigé (2026-09-20, "texte noir sur fond noir") : .account-menu
     button{color:var(--bone)} ne cible que les vrais <button> — ce
     déclencheur est un <div> (.account-submenu-trigger), jamais visé par
     cette règle, donc sans couleur explicite il héritait la couleur de
     texte par défaut de la page (sombre) sur le fond sombre du menu.
     color posé ICI, hérité par .account-submenu-label/.account-submenu-arrow. */
  .account-submenu-trigger{ position:relative; display:flex; align-items:center; justify-content:space-between; gap:10px; padding:11px 16px; color: var(--bone); font-family:var(--text-font-family); font-size:var(--fs-md); }
  .account-submenu-trigger:hover{ background: color-mix(in srgb, var(--bone) 10%, transparent); }
  .account-submenu-label{ flex:1; }
  .account-submenu-arrow{ opacity:0; transition:opacity .1s ease; font-size:var(--fs-xs); }
  .account-submenu-trigger:hover .account-submenu-arrow{ opacity:1; }
  /* Bug corrigé (2026-09-20, "le sous-menu ne se déclenche jamais") : le
     précédent mécanisme purement CSS (:has(.arrow:hover, .submenu:hover))
     ne se déclenchait pas de façon fiable en pratique. Remplacé par un
     mécanisme JS explicite, standard pour un menu en cascade classique :
     mouseenter/mouseleave sur .account-submenu-trigger (voir
     renderAccountWidget() dans app.js) posent/retirent une classe
     .open — mouseenter/mouseleave ne se déclenchent QUE en franchissant la
     frontière de l'élément qui les porte (jamais en circulant vers un
     descendant, comme .account-submenu ici), donc déplacer la souris de la
     ligne vers le sous-menu déployé ne le referme jamais par erreur. */
  /* Hauteur maximale robuste (2026-09-20, demande explicite — "jamais
     risquer qu'un nom déborde hors de l'écran") : min(260px, ...) plutôt
     qu'une valeur fixe seule — 260px normalement, mais se réduit sur une
     fenêtre basse (100vh - 100px, marge approximative pour la position de
     ce sous-menu, ancré à une ligne du menu compte plutôt qu'au sommet de
     l'écran) pour ne jamais dépasser la fenêtre quel que soit le nombre de
     comptes à afficher. */
  .account-submenu{ display:none; position:absolute; top:0; right:100%; min-width:180px; max-height:min(520px, calc(100vh - 100px)); overflow-y:auto; background: color-mix(in srgb, var(--night) 97%, transparent); border-radius:8px; box-shadow:0 20px 45px color-mix(in srgb, var(--ink) 35%, transparent); flex-direction:column; }
  .account-submenu-trigger.open .account-submenu{ display:flex; }
  /* 3ᵉ niveau du menu Compte (2026-10-11, point 16) : la ligne est le bouton
     de l'onglet lui-même (clic = onglet, comme avant), la flèche et le
     sous-menu des sous-onglets s'ajoutent au survol. */
  .account-screen-trigger{ padding:0; }
  .account-screen-trigger > .account-menu-item{ flex:1; }
  .account-screen-trigger > .account-menu-item:hover{ background:none; }
  .account-screen-trigger > .account-submenu-arrow{ padding-right:16px; }
  .account-submenu .modal-note{ padding:11px 16px; white-space:nowrap; }
  .account-recent-trigger .account-submenu button{ white-space:nowrap; }
  .account-submenu button{ display:block; width:100%; text-align:left; background:none; border:none; color:var(--bone); font-family:var(--text-font-family); font-size:var(--fs-md); padding:11px 16px; cursor:pointer; }
  .account-submenu button:hover{ background: color-mix(in srgb, var(--bone) 18%, transparent); color: var(--ember); }
  /* Bandeau permanent de simulation (2026-09-15, demande explicite) :
     rappel visuel constant, jamais masquable autrement qu'en arrêtant
     réellement la simulation (voir renderImpersonationBanner() dans
     app.js — pas de croix ✕ ici, à dessein). z-index au-dessus de tout
     (au-dessus même de .lb-topbar/#lightbox, voir plus loin dans ce
     fichier) : ce rappel ne doit jamais pouvoir être recouvert.
     Retour à un VRAI overlay (2026-09-20, demande explicite — annule le
     correctif du même jour plus tôt qui décalait .crumbbar/header.hero/
     .stripbar vers le bas pour réserver sa hauteur) : même mécanisme déjà
     utilisé ailleurs sur le site pour ce cas exact (voir
     .voyage-disabled-banner plus haut) — pointer-events:none sur le
     bandeau lui-même laisse tous les clics passer vers ce qu'il y a en
     dessous (widget compte, menu...), RÉACTIVÉ explicitement (pointer-events:auto)
     sur le seul bouton "Revenir en mode administrateur" pour qu'il reste
     cliquable. Fond semi-transparent conservé (texte/bouton en couleurs
     pleines pour la lisibilité) puisque plus rien n'est masqué en dur en
     dessous — juste un rappel visuel, pas un vrai obstacle. */
  .impersonation-banner{ position:fixed; top:0; left:0; right:0; z-index:9999; display:flex; align-items:center; justify-content:center; gap:14px; padding:8px 16px; background: color-mix(in srgb, var(--c-orange-moyen-2) 55%, transparent); color:var(--c-gris-sombre); font-family:var(--text-font-family); font-size:var(--fs-xl); font-weight:600; box-shadow:0 2px 10px color-mix(in srgb, var(--ink) 35%, transparent); pointer-events:none; }
  /* Intégration de voyage : bandeau "ne pas fermer" (2026-09-15, demande
     explicite) — même patron d'overlay que .impersonation-banner (pointer-
     events:none sur le bandeau, :auto sur son seul bouton), posé au niveau
     de la PAGE pour rester visible même si "Mon compte" est fermé pendant
     qu'un scan/génération continue en arrière-plan. Couleur ambre (alerte),
     distincte de l'ember de .impersonation-banner. */
  .integration-busy-banner{ position:fixed; top:0; left:0; right:0; z-index:9998; display:flex; align-items:center; justify-content:center; gap:14px; padding:8px 16px; background: color-mix(in srgb, var(--c-orange-moyen-3) 85%, transparent); color:var(--c-orange-sombre); font-family:var(--text-font-family); font-size:var(--fs-lg); font-weight:600; box-shadow:0 2px 10px color-mix(in srgb, var(--ink) 35%, transparent); pointer-events:none; }
  .integration-busy-banner button{ pointer-events:auto; font-family:var(--text-font-family); font-size:var(--fs-sm); font-weight:600; background:var(--c-orange-sombre); color:var(--bone); border:none; border-radius:14px; padding:6px 14px; cursor:pointer; }
  .integration-busy-banner button:hover{ opacity:0.85; }
  .impersonation-banner button{ pointer-events:auto; background: color-mix(in srgb, var(--ink) 15%, transparent); border: 1px solid color-mix(in srgb, var(--ink) 30%, transparent); border-radius:6px; padding:4px 10px; color:var(--c-gris-sombre); font-family:inherit; font-size:inherit; font-weight:600; cursor:pointer; }
  .impersonation-banner button:hover{ background: color-mix(in srgb, var(--ink) 25%, transparent); }
  /* Sécurité anti-perte, validation explicite (2026-09-26, demande
     explicite — point 6/7 : reprendre le patron de .impersonation-banner,
     couleur signature du thème actif (var(--ember)) plutôt qu'une teinte
     figée, voir updateGridUnsavedGuard()/checkGridUnsavedPendingOnce() côté
     app.js). Posé/retiré du DOM à la demande (jamais présent par défaut),
     donc pas besoin d'un display:none par défaut ici. */
  /* Transparent + clic-traversant (2026-09-26, demande explicite — "comme
     pour les Thèmes", même patron visuel que .impersonation-banner déjà
     existant) : purement informatif, ne doit JAMAIS empêcher de cliquer
     sur ce qu'il y a en dessous — seul un éventuel bouton à l'intérieur
     (voir #gridUnsavedPendingDismiss) redevient cliquable explicitement. */
  .grid-unsaved-banner{ position:fixed; top:0; left:0; right:0; z-index:999998; display:flex; align-items:center; justify-content:center; gap:14px; padding:8px 16px; background: color-mix(in srgb, var(--ember) 55%, transparent); color:var(--c-gris-sombre); font-family:var(--text-font-family); font-size:var(--fs-lg); font-weight:600; box-shadow:0 2px 10px color-mix(in srgb, var(--ink) 35%, transparent); text-align:center; pointer-events:none; }
  .grid-unsaved-banner-pending{ background: color-mix(in srgb, var(--ember) 40%, color-mix(in srgb, var(--ink) 40%, transparent)); }
  .grid-unsaved-banner button{ pointer-events:auto; background: color-mix(in srgb, var(--ink) 15%, transparent); border: 1px solid color-mix(in srgb, var(--ink) 30%, transparent); border-radius:6px; padding:4px 10px; color:var(--c-gris-sombre); font-family:inherit; font-size:var(--fs-md); font-weight:600; cursor:pointer; }
  .grid-unsaved-banner button:hover{ background: color-mix(in srgb, var(--ink) 25%, transparent); }
  /* Bandeau ticker (2026-09-26, demande explicite) — voir assets/ticker.js
     pour la logique de montage/résolution des messages.
     - N0/N1/N2 uniquement (2026-09-26, ajusté — retiré de N3) : caché dès
       que la visionneuse est ouverte, même fenêtrée, voir body:has(#lightbox.open)
       plus bas — N4 (plein écran natif) reste hors de propos par construction
       (voir l'en-tête de ticker.js), déjà couvert par cette même règle
       puisque le plein écran natif suppose la visionneuse ouverte.
     - bottom: var(--bar-h) quand .bottombar existe (N1/N2, curseur de zoom) :
       évite le chevauchement remonté (bug confirmé) — .bottombar occupe déjà
       le bas de l'écran sur ces pages ; N0 n'a pas de .bottombar, le ticker y
       reste donc collé tout en bas (bottom:0, valeur par défaut ci-dessous).
     - pointer-events:none sur le conteneur / auto sur l'icône (même patron
       que .impersonation-banner/.grid-unsaved-banner ci-dessus, voir
       projet/standards/BANNER_STANDARD.md) : un bandeau purement informatif ne doit jamais
       bloquer un clic sur ce qu'il y a en dessous, sauf sur son propre lien. */
  .site-ticker{
    position:fixed; left:0; right:0; bottom:0; z-index:60; height:34px;
    overflow:hidden; background: color-mix(in srgb, var(--dusk, var(--night)) 82%, transparent);
    display:flex; align-items:center;
  }
  /* Lot B (2026-10-10) : la barre du bas (.bottombar) est supprimée — le
     bandeau défilant reste tout en bas sur toutes les pages. Les pages déjà
     publiées la gardent dans leur HTML : masquée ici, retirée par app.js. */
  body.diaporama-page .bottombar{ display:none; } /* renommage-ok */
  body:has(#lightbox.open) .site-ticker{ display:none; }
  /* Piste dupliquée UNE fois (voir buildTrackHtml() côté ticker.js) :
     translateX(0 -> -50%) déplace exactement UNE séquence vers la gauche —
     au moment où la 2e copie arrive à la position de départ de la 1re, la
     boucle repart sans à-coup visible. animation-duration posée en JS
     (largeur réelle / vitesse constante), jamais une valeur fixe ici. */
  .site-ticker-track{
    display:inline-flex; align-items:center; gap:44px; white-space:nowrap; padding-left:44px;
    animation-name: site-ticker-scroll; animation-timing-function: linear; animation-iteration-count: infinite;
  }
  /* Pause au survol (2026-09-19, demande explicite — pouvoir cliquer sur le
     lien maison sans qu'il défile sous le curseur) : nécessite que .site-
     ticker reçoive les événements pointeur (pointer-events:none retiré
     ci-dessus, sinon :hover ne se déclenche jamais dessus). */
  .site-ticker:hover .site-ticker-track{ animation-play-state: paused; }
  @keyframes site-ticker-scroll{ from{ transform:translateX(0); } to{ transform:translateX(-50%); } }
  @media (prefers-reduced-motion: reduce){
    .site-ticker-track{ animation:none; }
  }
  /* Taille nettement augmentée (2026-09-26, demande explicite — "doit se
     faire remarquer, sans devenir déconcentrant") : 12.5px -> 16px + gras,
     tout en gardant la police de texte courant (jamais la police display du
     titre) et la même bande sombre discrète en fond. */
  .site-ticker-item{
    font-family:var(--text-font-family); font-weight:600; font-style:var(--text-font-style);
    font-size:var(--fs-2xl); color:var(--ember);
  }
  .site-ticker-icon{ pointer-events:auto; color:var(--ember); text-decoration:none; font-size:var(--fs-3xl); opacity:0.85; }
  .site-ticker-icon:hover{ opacity:1; }
  /* Sélecteur de langue autonome (voir i18n.js) — sorti du menu Compte
     utilisateur, visible en permanence (crumbbar, niveaux 1-2) ET sur
     l'écran de connexion (_layout.php), avant toute session. */
  .lang-switcher{ display:flex; flex:0 0 auto; }
  /* Plein écran de la page (13/10, AN ; i18n.js initPageFs) : bouton
     discret à gauche du drapeau, N0, N1, N2. */
  .lang-switcher{ align-items:center; gap:6px; }
  .page-fs-btn{ display:inline-flex; align-items:center; justify-content:center; width:32px; height:32px; padding:0; background:none; border:none; border-radius:50%; color:inherit; opacity:.6; cursor:pointer; transition:opacity .15s ease; }
  .page-fs-btn:hover, .page-fs-btn.is-on{ opacity:1; }
  /* Menu personnalisé (2026-08-16, remplace le <select> natif) — repli
     replié compact (juste le drapeau, agrandi) impossible à obtenir avec
     un <select> natif tout en gardant les libellés complets dans le menu
     ouvert (la boîte fermée d'un <select> affiche toujours le texte de
     l'option choisie, aucun moyen natif de dissocier affichage replié et
     déroulé). Repris du même schéma que .account-widget/.account-menu
     ci-dessus, un cran plus petit. */
  .lang-widget{ position:relative; flex:0 0 auto; }
  /* Drapeau agrandi (2026-08-19, 17px→24px, bouton 32px→40px pour garder
     une marge propre autour) — la version précédente restait trop discrète
     pour un contrôle destiné à rester visible en permanence. */
  .lang-btn{ display:flex; align-items:center; justify-content:center; width:40px; height:40px; background:none; border:1px solid color-mix(in srgb, var(--dusk) 25%, transparent); border-radius:50%; font-size:var(--fs-4xl); cursor:pointer; transition: border-color .15s ease; }
  .lang-btn:hover{ border-color: color-mix(in srgb, var(--dusk) 45%, transparent); }
  .lang-menu{ display:none; position:absolute; top:calc(100% + 8px); left:0; min-width:140px; background: color-mix(in srgb, var(--night) 97%, transparent); border-radius:8px; box-shadow:0 20px 45px color-mix(in srgb, var(--ink) 35%, transparent); overflow:hidden; z-index:70; flex-direction:column; }
  .lang-widget.open .lang-menu{ display:flex; }
  .lang-menu button{ display:flex; align-items:center; gap:8px; width:100%; text-align:left; background:none; border:none; color:var(--bone); font-family:var(--text-font-family); font-size:var(--fs-md); padding:10px 14px; cursor:pointer; }
  .lang-menu button:hover{ background: color-mix(in srgb, var(--bone) 10%, transparent); }
  .lang-menu button.active{ color: var(--dust-gold); }
  /* Drapeau du menu déroulant, agrandi par rapport au texte environnant
     (même logique que .lang-btn ci-dessus) — flag et libellé étaient un
     seul texte avant (2026-08-19), séparés pour permettre cet écart de
     taille. */
  .lang-menu .lang-flag-icon{ font-size: var(--fs-3xl); }
  .account-info-row{ display:flex; justify-content:space-between; align-items:center; gap:12px; padding:9px 0; border-bottom:1px solid color-mix(in srgb, var(--bone) 10%, transparent); font-family:var(--text-font-family); font-size:var(--fs-md); }
  .account-info-row span{ opacity:0.6; }
  /* Contrepoids (2026-09-03, retrait du gras — <strong> remplacés par
     <span> dans Tech Info/Messages) : ces spans de VALEUR (pas le libellé
     à gauche, qui doit garder son opacité 0.6 habituelle) héritaient sinon
     à tort de la règle générique juste au-dessus, les faisant paraître
     estompés alors qu'ils n'avaient jusqu'ici jamais cette opacité réduite
     (seul le poids de police doit changer, pas l'opacité). */
  /* :not(.tech-info-estimated) retiré (2026-09-05, nettoyage — cette
     classe ne vit plus jamais dans .account-info-row depuis que Système/
     Environnement a basculé sur .tech-info-value/.settings-row, l'exclusion
     était devenue sans effet, jamais nettoyée). */
  .account-info-row .account-info-value{ opacity: 1; }
  .account-info-row:last-of-type{ border-bottom:none; }
  /* Italique retiré (2026-09-05, décision finale explicite — malgré
     l'usage volontaire initial signalant une valeur "estimée", Thierry
     préfère un style normal partout sur cet écran). */
  .tech-info-estimated{ opacity:0.6; }
  .tech-info-detail{ font-size:var(--fs-xs); }
  /* Format de cellule unifié pour tout Système/Environnement (2026-09-05,
     demande explicite — écran jugé "bâclé", disparate) : ni fond ni
     bordure, même police que les valeurs en lecture seule, que ce soit un
     <span>, un <select> ou un <input> — un seul format visuel pour toute
     la page. */
  .tech-info-value{ font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size:var(--fs-md); color: var(--settings-default-text-color); }
  .tech-info-select{ background:none; border:none; padding:0; cursor:pointer; }
  /* Largeur réduite au contenu (2026-09-05, "menu Mode trop large") : pas
     de .admin-select-grow ici, contrairement aux champs URL juste en
     dessous (qui, eux, doivent rester larges pour une adresse complète). */
  .tech-info-input{ background:none; border:none; border-bottom:1px solid color-mix(in srgb, var(--bone) 20%, transparent); border-radius:0; padding:2px 0; flex:1; max-width:420px; }
  .tech-info-input:focus{ border-bottom-color: var(--ember); outline:none; }
  .tech-info-input:disabled{ opacity:0.45; }
  /* Champs alignés entre eux (2026-09-05, "mieux aligner les champs de
     données") : largeur de libellé fixe, commune à toute la page — sans
     ça, "Résolution retenue" vs "URL Local" (longueurs différentes)
     décalait le début de chaque valeur d'une ligne à l'autre, comme déjà
     rencontré pour les curseurs d'Affichage. */
  #techInfoTabPanel .settings-row-text{ flex: 0 0 180px; }
  /* Modale "Commentaires" (2026-08-16, voir openCommentsModal() dans app.js) */
  .comments-list{ max-height:280px; overflow-y:auto; overscroll-behavior: none; margin-bottom:14px; }
  .comments-empty{ font-family:var(--text-font-family); font-size:var(--fs-sm); color:var(--bone); opacity:0.6; padding:6px 0; }
  .comment-row{ padding:10px 0; border-bottom:1px solid color-mix(in srgb, var(--bone) 10%, transparent); }
  .comment-row:last-child{ border-bottom:none; }
  .comment-row-head{ display:flex; align-items:center; gap:8px; font-family:var(--text-font-family); font-size:calc(var(--fs-sm) * var(--reading-text-scale)); color:var(--bone); }
  .comment-date{ opacity:0.5; font-size:var(--fs-xs); margin-left:auto; white-space:nowrap; }
  .comment-delete-btn{ position:relative; background:none; border:none; color:var(--bone); opacity:0.4; cursor:pointer; font-size:var(--fs-sm); padding:0; line-height:1; }
  .comment-delete-btn:hover{ opacity:1; color:var(--c-orange-moyen-4); }
  /* Info-bulle (2026-08-31, inventaire complet — mécanisme natif jusqu'ici) :
     ligne d'un commentaire, jamais collée à un bord — centrée, sous le
     bouton. */
  .comment-delete-btn[data-tooltip]::after{ top: calc(100% + 10px); left: 50%; transform: translateX(-50%); }
  .comment-body{ margin:4px 0 0; font-size:calc(var(--fs-md) * var(--reading-text-scale)); color:var(--bone); opacity:0.9; line-height:1.4; white-space:pre-wrap; }
  .comment-form{ display:flex; gap:8px; align-items:flex-end; }
  /* resize:none (2026-08-31) : la zone s'agrandit désormais TOUTE SEULE
     avec la saisie (voir autoGrowTextarea() dans app.js) — un
     redimensionnement manuel entrerait en conflit avec ce calcul, qui
     réécrit la hauteur à chaque frappe. line-height explicite : sans lui,
     getComputedStyle(...).lineHeight renvoie le mot-clé "normal" (pas un
     nombre), la limite en lignes ne pourrait pas se calculer. */
  .comment-form textarea{ flex:1; resize:none; line-height:1.4; min-height:40px; box-sizing:border-box; background:color-mix(in srgb, var(--bone) 8%, transparent); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius:6px; padding:8px 10px; color:var(--bone); font-family:inherit; font-size:var(--fs-md); }
  .comment-form button{ flex:0 0 auto; width:auto; }
  /* --settings-default-text-color plutôt que --bone en dur (2026-09-03,
     extension de portée explicite) : couvre d'un coup Aide/Aide (corps),
     Aide/FAQ (réponses), Aide/Spécifications, Aide/À propos et Aide/
     Résultats de recherche, qui partagent tous cette même classe. */
  .modal-note{ font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size:calc(var(--fs-sm) * var(--reading-text-scale)); color:var(--settings-default-text-color); opacity:0.6; }
  /* Séparateur entre les deux sections de la modale "Aide et Support"
     (2026-08-19, fusion Aide + Contact) — .users-modal-body est déjà en
     colonne flex avec gap, donc juste un trait, pas de marge propre à gérer. */
  .modal-sep{ border: none; border-top: 1px solid color-mix(in srgb, var(--bone) 15%, transparent); margin: 0; }
  /* Ligne avatar de l'onglet "Mon Profil" (2026-08-28) : aperçu (même
     cercle que .account-avatar, agrandi) + actions à côté. Le bouton
     "Changer la photo" est un <label> stylé comme .settings-save-btn
     (input file cible, display:none) plutôt qu'un vrai bouton : évite un
     clic intermédiaire en JS pour déclencher le sélecteur de fichiers. */
  .profile-avatar-row{ display:flex; align-items:center; gap:16px; margin-bottom:18px; }
  .profile-avatar-preview{ width:80px; height:80px; font-size:calc(var(--fs-5xl) * var(--ui-scale)); flex:0 0 auto; }
  .profile-avatar-actions{ display:flex; flex-direction:column; gap:8px; align-items:flex-start; }
  .profile-avatar-choose{ display:inline-block; }
  .profile-email-pending{ background:color-mix(in srgb, var(--c-orange-moyen-2) 12%, transparent); border:1px solid color-mix(in srgb, var(--c-orange-moyen-2) 35%, transparent); border-radius:8px; padding:14px; margin:-4px 0 14px; }
  .profile-email-pending-hint{ margin:0 0 10px; font-size:var(--fs-md); line-height:1.4; }
  .modal-field{ margin-bottom:14px; }
  /* Fiche de détail Voyages (2026-09-16) : ligne cliquable de la grille
     réduite (ID+Libellé), et champ "Actif" (case à cocher) dans la fenêtre
     de détail, à côté d'un <select>/<input> classique dans la même rangée —
     alignement vertical propre sans reprendre toute la largeur. */
  .voyages-row-clickable{ cursor:pointer; }
  .voyages-row-clickable:hover{ background:color-mix(in srgb, var(--bone) 5%, transparent); }
  /* Ligne sélectionnée — surlignage générique (2026-09-18, "par cohérence",
     étendu à TOUTES les grilles — auparavant réservé à Voyages, seule à
     avoir un panneau de détail). Sur Voyages : ligne actuellement
     affichée dans le panneau de détail (2026-09-23, "comme Outlook"),
     reposée à chaque rendu — voir renderVoyageDetailPanel()/
     voyageRowHtml(). Sur les 6 autres grilles : ligne contenant
     actuellement le focus clavier, voir wireGenericRowSelectionHighlight()
     côté app.js — pas de persistance au-delà du focus, ces grilles n'ayant
     pas de panneau à garder synchronisé.
     Renforcé (2026-09-24, signalé "pas surligné" — l'opacité de 0.14
     précédente était réellement appliquée mais trop discrète pour se lire
     comme un vrai surlignage) : bande verticale + fond nettement plus
     marqué, même esprit qu'une ligne sélectionnée dans un client mail.
     Généralisé (2026-09-23) à toutes les grilles (plus seulement les 6
     `<table>` d'origine) ET à Sessions connectées (`<div
     class="users-import-item">`, pas une vraie ligne de tableau) — pas de
     qualificatif `tr` sur ce sélecteur de base, pour matcher les deux. */
  .users-row-selected{ background:color-mix(in srgb, var(--ember) 28%, transparent); box-shadow: inset 3px 0 0 var(--ember); }
  .voyages-row-clickable.users-row-selected:hover{ background:color-mix(in srgb, var(--ember) 34%, transparent); }
  /* Cause RÉELLE trouvée (2026-09-25, "toujours pas surligné" malgré le
     renforcement ci-dessus, répété à chaque round) : la grille Voyages
     n'a QUE des colonnes figées (case + Identifiant + Nom,
     .users-frozen-1/2/3) — chacune pose SON PROPRE fond opaque (voir
     .users-frozen-1, ..., plus bas dans ce fichier), qui peint PAR-DESSUS
     le fond du <tr> lui-même. Le fond ci-dessus n'a donc jamais été
     visible nulle part sur cette grille précise (100% de sa largeur est
     couverte par des cellules figées) — spécificité plus élevée ici pour
     reprendre la même couleur directement sur chaque cellule figée. Les 6
     autres grilles ont aussi des colonnes figées en tête (même convention,
     voir .users-frozen-1/2/3 plus bas) : ces overrides les couvrent déjà
     toutes, pas seulement Voyages. */
  .voyages-row-clickable:hover .users-frozen-1, .voyages-row-clickable:hover .users-frozen-2, .voyages-row-clickable:hover .users-frozen-3{
    background: color-mix(in srgb, var(--bone) 5%, transparent);
  }
  tr.users-row-selected .users-frozen-1, tr.users-row-selected .users-frozen-2, tr.users-row-selected .users-frozen-3, tr.users-row-selected .users-frozen-4{
    background: color-mix(in srgb, var(--ember) 28%, transparent);
  }
  .voyages-row-clickable.users-row-selected:hover .users-frozen-1, .voyages-row-clickable.users-row-selected:hover .users-frozen-2, .voyages-row-clickable.users-row-selected:hover .users-frozen-3{
    background: color-mix(in srgb, var(--ember) 34%, transparent);
  }
  .voyage-detail-active-field{ display:flex; flex-direction:column; justify-content:flex-end; }
  .voyage-detail-active-field input[type="checkbox"]{ width:18px; height:18px; margin:4px 0 0; }
  /* Panneau de détail intégré (2026-09-23, demande explicite — "comme
     Outlook", remplace la fenêtre flottante du 2026-09-16 ; ré-architecturé
     2026-09-25 [en-tête pleine largeur], puis à nouveau 2026-09-25 [grille
     et fiche CÔTE À CÔTE sous cette en-tête, pas empilées — l'essai
     précédent en colonne ne correspondait pas à la demande] — voir
     .users-toolbar-filters/.users-action-bar directement dans
     .settings-tabpanel pour l'en-tête pleine largeur, ce bloc-ci reprend un
     axe horizontal pour la grille + le séparateur + la fiche) : contenu de
     la fiche reconstruit instantanément au clic sur une autre ligne (voir
     renderVoyageDetailPanel()). */
  /* Largeur automatique (2026-10-01, décision de Thierry — remplace la
     poignée et la largeur mémorisée) : la grille prend sa largeur
     NATURELLE (table width:auto + table-layout:fixed = somme des colonnes,
     ✓/✕ compris), le panneau prend tout le reste avec 380 px minimum ;
     quand la place manque, la grille rétrécit et défile horizontalement
     (overflow:auto). Élargir une colonne rétrécit le panneau en direct, sans
     JS. Plus d'espace pour le panneau : élargir la fenêtre. */
  .voyages-below-header{ display:flex; flex-direction:row; align-items:stretch; min-height:0; flex:1 1 auto; }
  .voyages-below-header .users-table-scroll{ flex:0 1 auto; min-width:0; }
  .voyages-split-sep{ flex:0 0 1px; margin:0 6px; background:color-mix(in srgb, var(--bone) 12%, transparent); }
  .voyages-split-detail{
    flex:1 1 0; min-width:380px; align-self:stretch;
    overflow-y:auto;
  }
  .voyages-split-detail-head{
    font-family:var(--text-font-family); font-size:var(--fs-md); letter-spacing:0.06em;
    text-transform:uppercase; color:var(--settings-default-text-color);
    padding-bottom:12px; margin-bottom:14px; border-bottom:1px solid color-mix(in srgb, var(--bone) 12%, transparent);
  }
  .voyages-split-detail-body{ display:flex; flex-direction:column; gap:12px; }
  /* Mon Profil uniquement (2026-09-04, demande explicite) : répartit les
     champs sur une ligne au lieu du plein-largeur par défaut ci-dessus —
     .modal-field garde sa largeur 100% par défaut PARTOUT ailleurs
     (Partage en masse/Contact/Système), volontairement inchangé. */
  .modal-field-row{ display:flex; gap:14px; flex-wrap:wrap; }
  .modal-field-row .modal-field{ flex:1; min-width:140px; }
  /* Échanges/Partage en masse en étapes numérotées (2026-09-04, demande
     explicite — "le déroulement n'est pas guidé") : un badge numéroté à
     gauche, le contenu de l'étape à droite, même idiome que les groupes
     .settings-group-label pour l'espacement entre étapes. */
  .share-bulk-step{ display:flex; gap:12px; margin-top:18px; }
  .share-bulk-step:first-of-type{ margin-top:10px; }
  .share-bulk-step-num{ flex:0 0 auto; width:22px; height:22px; border-radius:50%; background:var(--ember); color:var(--bone); font-family:var(--text-font-family); font-size:var(--fs-sm); display:flex; align-items:center; justify-content:center; }
  .share-bulk-step-body{ flex:1; min-width:0; }
  /* Sélecteur de fichier natif remplacé par une zone de glisser-déposer
     stylée (2026-09-04, demande explicite — convention Gmail/Dropbox) :
     l'<input type="file"> natif reste présent mais masqué (voir markup),
     déclenché par le <label> stylé comme .settings-save-btn. */
  /* Cadre en pointillés retiré (2026-09-04, retour explicite) : plus de
     bordure, juste le bouton + l'indice texte/icône ; le retour visuel du
     survol pendant un glisser-déposer se fait maintenant par un fond
     teinté sur toute la ligne, pas une bordure. */
  /* Retrait à gauche corrigé (2026-09-05, "léger retrait par rapport aux
     autres éléments") : padding horizontal retiré (n'avait plus lieu
     d'être depuis le retrait du cadre en pointillés), ne garde que le
     vertical pour l'espace de survol pendant un glisser-déposer. */
  .share-dropzone{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; padding:8px 0; border-radius:10px; transition: background .15s ease; }
  .share-dropzone.drag-over{ background: color-mix(in srgb, var(--ember) 10%, transparent); }
  .share-dropzone-hint{ font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size:var(--fs-sm); opacity:0.6; color: var(--settings-default-text-color); display:flex; align-items:center; gap:6px; }
  /* Icône cible (2026-09-04, remplace le cadre en pointillés) : taille
     d'un bouton standard, pas une simple puce de texte. */
  /* Agrandie, façon vrai bouton (2026-09-05, retour explicite) : même
     finition que .users-add-row-btn (cercle, fond léger) plutôt qu'une
     simple puce de texte — cliquable, ouvre aussi le sélecteur de
     fichiers (même <label for="shareBulkInput">). */
  .share-dropzone-icon{ width:40px; height:40px; border-radius:50%; background: color-mix(in srgb, var(--bone) 8%, transparent); display:flex; align-items:center; justify-content:center; color: var(--settings-default-text-color); cursor:pointer; flex:0 0 auto; }
  .share-dropzone-icon:hover{ background: color-mix(in srgb, var(--bone) 15%, transparent); }
  /* Espace le bouton "Partager" de son titre d'étape (2026-09-04, demande
     explicite). */
  .share-bulk-send-btn{ margin-top:10px; }
  .share-bulk-file-list{ margin-top:10px; display:flex; flex-direction:column; gap:2px; max-height:120px; overflow-y:auto; }
  .share-bulk-file-item{ display:flex; align-items:center; gap:8px; font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size:var(--fs-sm); color: var(--settings-default-text-color); }
  .share-bulk-file-item span{ flex:0 0 auto; }
  /* Vrai bouton (2026-09-05, retour explicite — "c'est un lien
     hypertexte, pas un vrai bouton") : même famille visuelle que
     Supprimer/Révoquer (.users-action-bar button), pas un lien souligné. */
  .share-bulk-file-remove{ margin-left:auto; flex:0 0 auto; font-family:var(--text-font-family); font-size: var(--fs-sm); letter-spacing: 0.02em; color: var(--action-button-text-color); background: color-mix(in srgb, var(--bone) 8%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 25%, transparent); padding: 6px 12px; border-radius: 16px; cursor:pointer; white-space:nowrap; }
  .share-bulk-file-remove:hover{ background: color-mix(in srgb, var(--bone) 15%, transparent); }
  .share-bulk-file-remove:disabled{ opacity: 0.35; cursor:not-allowed; }
  .share-bulk-file-remove:disabled:hover{ background: color-mix(in srgb, var(--bone) 8%, transparent); }
  /* --settings-default-text-color plutôt que --bone en dur (2026-09-03,
     extension de portée explicite du paramètre "Couleur des textes et
     légendes") : couvre Mon Profil ET Échanges/Partage en masse d'un seul
     coup, .modal-field étant partagée par ces deux écrans. */
  .modal-field label{ display:block; font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size:var(--fs-xs); text-transform:uppercase; letter-spacing:0.05em; color:var(--settings-default-text-color); opacity:0.7; margin-bottom:6px; }
  /* Bug corrigé (2026-09-23, "champs Année/N° externe en blanc, non
     formatés") : input[type="number"] (Fiche détail Voyages) absent de
     cette liste — repli sur le rendu blanc natif du navigateur au lieu du
     style commun à tous les autres champs de .modal-field. */
  .modal-field input[type="text"], .modal-field input[type="email"], .modal-field input[type="tel"], .modal-field input[type="number"], .modal-field textarea, .modal-field select{ width:100%; box-sizing:border-box; background:color-mix(in srgb, var(--bone) 8%, transparent); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius:6px; padding:10px; color:var(--settings-default-text-color); font-family:inherit; font-size:var(--field-font-size); }
  /* resize:none + line-height explicite (2026-08-31) : voir le commentaire
     de .comment-form textarea, même raison exacte. */
  .modal-field textarea{ resize:none; line-height:1.4; }
  .modal-field input[type="text"]:disabled{ opacity:0.45; cursor:not-allowed; }
  /* Pas de popup d'incrément +/- sur les champs numériques ici non plus
     (2026-09-23, confirmé explicitement — même convention que
     .users-modal-table input[type="number"] plus bas) : saisie directe au
     clavier uniquement. */
  .modal-field input[type="number"]{ -moz-appearance:textfield; }
  .modal-field input[type="number"]::-webkit-inner-spin-button,
  .modal-field input[type="number"]::-webkit-outer-spin-button{ -webkit-appearance:none; margin:0; }

  /* ---------- PANNEAU DE RÉGLAGES ---------- */
  /* .settings-panel/.settings-panel--tabs/.settings-head (le panneau Réglages
     autonome, avec sa propre tête/croix de fermeture) ont disparu le
     2026-08-28 avec la fusion du menu Compte : Réglages est maintenant un
     onglet de la fenêtre Compte unifiée (.account-modal), qui a sa PROPRE
     tête/fermeture uniques pour toute la fenêtre — voir ensureAccountModal()
     dans app.js. Le reste de ce bloc (.settings-layout/.settings-sidebar/
     .settings-tab/.settings-body/.settings-tabpanel) reste le patron
     d'onglets partagé, réutilisé aux deux niveaux (horizontal ET vertical)
     de cette fenêtre. */
  /* flex:1 1 auto + min-height:0 (2026-08-30, bug corrigé) : sans ça, ce
     bloc gardait un plancher de 280px et ne pouvait jamais réellement
     RÉTRÉCIR en dessous — dès que la fenêtre Compte est redimensionnée
     plus petite que ce plancher, .settings-layout débordait de
     .users-modal-body, dont le PROPRE overflow-y:auto (voir plus bas)
     prenait alors le relais et faisait défiler tout le bloc d'un seul
     tenant, .settings-sidebar (Droits d'accès/Statistiques, etc.) compris
     — pourtant censée rester fixe pendant que seul .settings-body défile
     (voir son propre overflow-y:auto). Avec min-height:0, ce bloc se cale
     enfin exactement sur la hauteur réellement disponible : le
     débordement ne se produit plus qu'À L'INTÉRIEUR de .settings-body,
     jamais plus au niveau de .users-modal-body — .settings-sidebar, elle,
     n'a rien à défiler (align-items:stretch la cale sur cette même
     hauteur), donc reste visuellement fixe. */
  /* Cause RÉELLE du "ne suit pas le redimensionnement MANUEL de la
     fenêtre" trouvée (2026-09-03, après plusieurs tentatives infructueuses
     sur d'autres causes) : max-height: calc(85vh - 54px) — un plafond
     ABSOLU basé sur le VIEWPORT du navigateur, totalement indépendant de
     la hauteur RÉELLE de la fenêtre Compte (qu'elle vienne du 640px par
     défaut ou d'un redimensionnement manuel via les poignées). Cette
     classe s'applique ICI à sa toute première utilisation, la plus
     englobante de toutes (.settings-layout.account-toplevel, voir
     ensureAccountModal() dans app.js — TOUS les onglets de la fenêtre en
     dépendent, pas seulement Thèmes/Utilisateurs/Voyages) : dès que la
     fenêtre est agrandie par une poignée au-delà de ce plafond fixe (assez
     vite atteint sur un écran courant), tout son contenu s'arrêtait de
     grandir, laissant un vide grandissant en bas de la fenêtre pourtant
     réellement plus grande — .users-modal-body (l'AUTRE flex:1 1 auto/
     min-height:0 ancêtre, sans ce plafond, lui) se cale correctement sur
     CETTE hauteur réelle. Aucune justification retrouvée pour ce plafond
     au-delà de flex:1 1 auto/min-height:0 (déjà suffisants, voir le
     commentaire juste au-dessus sur leur propre correctif) — supprimé
     purement et simplement : ce bloc se cale désormais sur l'espace
     RÉELLEMENT alloué par son parent, qu'il grandisse ou rétrécisse, par
     poignée ou par redimensionnement du navigateur. */
  .settings-layout{ display:flex; align-items:stretch; flex: 1 1 auto; min-height: 0; }
  /* Bordures dérivées de --settings-bg (2026-09-01, plus un réglage de
     thème séparé — voir le commentaire sur --border-color, retiré, dans le
     bloc :root) : color-mix avec du blanc reproduit le même effet de
     bordure claire qu'avant sur un fond sombre, mais s'adapte désormais
     automatiquement à N'IMPORTE QUEL fond de thème, sans intervention
     manuelle — même technique partout où une bordure de cet écran utilisait
     du rgba en dur (voir aussi .settings-sep/.exif-row/.users-modal plus
     bas). */
  .settings-sidebar{ display:flex; flex-direction:column; flex: 0 0 128px; padding: 10px 0; border-right: 1px solid color-mix(in srgb, var(--settings-bg), white 12%); overflow-y:auto; overscroll-behavior: none; }
  /* Couleur des onglets inactifs (2026-09-01, corrigé — restait en dur sur
     --bone, jamais thémable, contrairement à .settings-tab.active déjà lié
     à --ember) : --secondary-text-color d'abord essayé, puis retiré
     (2026-09-03, demande explicite — "texte secondaire" ne doit pas
     s'appliquer aux onglets, ce paramètre restant pour l'instant non
     rattaché à quoi que ce soit) au profit de --settings-default-text-color,
     le nouveau réglage "Couleur des textes et légendes". */
  /* font-weight normal (2026-09-03, demande explicite — "retirer le gras
     partout, sauf l'onglet sélectionné") : 500 -> normal, seul
     .settings-tab.active (juste en dessous) garde son gras, seule
     exception explicitement demandée. */
  .settings-tab{ text-align:left; background:none; border:none; border-left: 2px solid transparent; color: var(--settings-default-text-color); opacity: 0.6; font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size: var(--fs-md); letter-spacing: 0.03em; padding: 10px 16px; cursor:pointer; transition: opacity .15s ease, background .15s ease; }
  .settings-tab:hover{ opacity: 0.85; }
  /* Mis en valeur (2026-08-31, demande explicite) : gras + un cran plus
     grand, pas seulement le changement de couleur/opacité déjà en place —
     l'onglet actif doit se voir avant même de lire sa couleur. */
  /* --ember (accent) plutôt que --dust-gold en dur (2026-08-31, bug
     corrigé — "reste toujours en jaune, peu importe le thème") : l'onglet
     actif doit suivre l'accent du thème (demande explicite, dérivé
     automatiquement, pas un réglage séparé) — le texte restait sur
     --dust-gold, seule la bordure suivait déjà --ember. */
  .settings-tab.active{ opacity: 1; color: var(--settings-tab-active-color); border-left-color: var(--settings-tab-active-color); background: color-mix(in srgb, var(--bone) 5%, transparent); font-weight: 700; font-size: var(--fs-lg); }
  /* Rien ne bouge au choix d'un onglet (2026-10-12) : largeur du libellé en
     gras réservée (data-text, voir reserveTabLabels() dans app.js) et même
     hauteur de ligne dans les deux états. */
  .settings-tab{ line-height: calc(var(--fs-lg) * 1.3); }
  .settings-tab[data-text]::after{ content: attr(data-text); display:block; height:0; overflow:hidden; visibility:hidden; pointer-events:none; font-weight:700; font-size:var(--fs-lg); }
  .settings-tab.hidden{ display:none; }
  /* overflow-y:auto (2026-08-21) : voir le même commentaire sur
     .users-modal-body — le panneau peut désormais être rétréci en dessous
     de la hauteur naturelle de son contenu. */
  .settings-body{ flex: 1 1 auto; min-width: 0; min-height: 0; padding: 16px 18px 18px; overflow-y:auto; overscroll-behavior: none; }
  .settings-tabpanel{ display:none; flex-direction:column; gap: 16px; }
  /* Chaîne de scroll revue en entier (2026-08-30, correctif précédent
     insuffisant — voir aussi la suppression des min-height EN LIGNE sur
     .settings-layout dans app.js, qui l'annulaient silencieusement).
     height:100%/min-height:0 ICI, sur TOUT panneau actif (pas seulement
     "access") : un panneau de niveau supérieur qui contient LUI-MÊME un
     .settings-layout imbriqué (ex. data-tabpanel="users", qui enveloppe
     Droits d'accès/Statistiques) doit transmettre une hauteur RÉELLEMENT
     bornée à cet enfant pour que la contrainte se propage jusqu'en bas —
     sans ça, ce panneau intermédiaire grandissait à la taille naturelle
     de tout son contenu (aucune hauteur propre), et c'est .settings-body
     (l'ancêtre commun, overflow-y:auto) qui absorbait tout le
     débordement, faisant défiler l'ensemble (sidebar de navigation
     comprise) comme UN seul bloc. Sans effet indésirable sur les
     panneaux plus simples (Mon Profil...) : height:100% y ajoute au pire
     un peu d'espace vide en bas, jamais de contenu tronqué. */
  .settings-tabpanel.active{ display:flex; height:100%; min-height:0; }
  /* Fenêtre Compte unifiée (2026-08-27) : niveau 1 (horizontal, les 6
     entrées) réoriente le patron .settings-sidebar/.settings-tab existant
     (pensé pour une colonne verticale) en rangée du haut — le niveau 2
     (sous-sections, imbriqué dans chaque .settings-tabpanel de niveau 1)
     réutilise ce même patron SANS aucune surcharge, à la verticale comme
     d'habitude (voir wireTabSwitcher() dans app.js, appelé une fois par
     niveau). .account-modal réutilise directement la taille par défaut
     déjà éprouvée de .users-modal--resizable (820×640, la plus généreuse
     du site) plutôt qu'une nouvelle valeur — c'est déjà la fenêtre la
     plus dense du site (table Utilisateurs) qui définit cette taille. */
  .account-toplevel{ flex-direction: column; }
  .account-toplevel > .settings-sidebar{ flex-direction: row; flex-wrap: wrap; flex: 0 0 auto; padding: 4px 8px; border-right: none; border-bottom: 1px solid color-mix(in srgb, var(--settings-bg), white 12%); }
  .account-toplevel > .settings-sidebar .settings-tab{ border-left: none; border-bottom: 2px solid transparent; }
  .account-toplevel > .settings-sidebar .settings-tab.active{ border-left-color: transparent; border-bottom-color: var(--settings-tab-active-color); }
  .settings-row{ display:flex; align-items:center; justify-content:space-between; gap: 14px; }
  /* Combos autonomes rapprochés de leur libellé (2026-09-04, demande
     explicite — "actuellement trop à droite") : justify-content:flex-start
     au lieu de space-between, le combo suit directement son libellé au
     lieu d'être poussé à l'extrémité opposée de la ligne. */
  .settings-row.settings-row-inline{ justify-content: flex-start; }
  /* Aperçu email en lecture seule (2026-09-16) : même famille/taille que
     .admin-select à côté duquel il s'aligne, mais texte simple (pas de
     champ éditable — email_from_address vient de SMTP_FROM, config.php,
     jamais modifiable depuis cette fenêtre). */
  .settings-email-preview{ font-family:var(--text-font-family); font-size:var(--fs-sm); color:var(--settings-default-text-color); opacity:0.85; white-space:nowrap; }
  /* Système/Environnement (2026-09-04, demande explicite — "rapprocher du
     libellé ET élargir") : le champ suit son libellé de près (gap 14px,
     hérité) tout en profitant de la largeur restante de la ligne. */
  .admin-select-grow{ flex: 1; max-width: 420px; }
  /* Curseurs de taille de texte (2026-08-21) : même ligne .settings-row que
     les interrupteurs, largeur fixe modeste plutôt que flex:1 (le libellé/
     description à gauche doit rester le principal élément qui respire).
     Repris au même patron visuel que .zoom-slider (piste + repères "A" +
     bouton reset), sauf pour les couleurs : .zoom-slider est pensé pour un
     fond clair (--dusk-soft), alors que .settings-panel est sombre (texte
     --bone) — d'où .settings-slider plutôt qu'une réutilisation directe. */
  /* Cadré à droite comme les boutons On/Off de cet écran (2026-09-04,
     précision explicite — le 1er correctif n'alignait que le DÉBUT de la
     piste entre curseurs, pas le bloc entier par rapport aux .switch
     voisins) : margin-left:auto garantit ce cadrage à droite quel que
     soit le contenu, plutôt que de dépendre seulement de
     .settings-row{justify-content:space-between}. */
  .settings-slider{ display:flex; align-items:center; gap: 8px; flex: 0 0 auto; margin-left: auto; }
  /* Couleur des repères min/max du curseur (2026-09-01, corrigé — restait
     en dur sur --bone, jamais thémable) : --settings-label-color, même
     mapping que l'équivalent en plein écran (.lb-zoom .zi plus bas). */
  /* Largeur fixe + texte cadré à droite (2026-09-04, demande explicite —
     "cadrer à droite les 3 curseurs") : "A" et "0s" n'ont pas la même
     largeur de texte, ce qui décalait le début de piste d'un curseur à
     l'autre malgré .settings-row{justify-content:space-between} — les 3
     pistes démarrent maintenant à la même position. */
  .settings-slider .zi{ font-size: var(--fs-xs); color: var(--settings-label-color); opacity: 0.55; font-family:var(--text-font-family); min-width: 18px; text-align: right; }
  .settings-slider .zi.big{ font-size: var(--fs-xl); opacity: 0.85; }
  .settings-slider-track{ position:relative; display:flex; align-items:center; }
  /* background surchargé (2026-08-22, bug corrigé le jour même) : la règle
     partagée input[type="range"] (plus bas dans ce fichier) est un brun
     sombre à 30% d'opacité, pensé pour un fond clair — invisible sur le fond
     sombre de .settings-panel, exactement le même problème déjà réglé pour
     .lb-zoom-track (visionneuse, voir son propre commentaire) : même
     correctif ici, couleur claire à faible opacité. */
  .settings-slider-track input[type="range"]{ position:relative; z-index:1; width: 110px; accent-color: var(--ember); background: color-mix(in srgb, var(--bone) 35%, transparent); }
  /* Repère central (2026-08-22) : marque la valeur par défaut (100%) au
     milieu exact de la piste — n'a de sens que parce que min/max (50/150)
     sont symétriques autour de 100, voir readingTextScaleRange/uiScaleRange
     dans index.html. Ramener le curseur pile dessus déclenche une
     réinitialisation (voir le listener 'input' correspondant dans app.js).
     Hauteur (22px) DÉLIBÉRÉMENT supérieure au diamètre visuel du rond
     (14px + 2×2px de bordure = 18px, voir input[type="range"]::...-thumb) —
     bug corrigé le jour même : à hauteur égale ou inférieure, le rond
     recouvre entièrement le trait dès qu'il s'en approche, le rendant
     invisible précisément au moment où il sert de repère. Dépasser de
     quelques pixels en haut/bas garantit qu'il reste visible même quand le
     rond est exactement dessus. */
  .settings-slider-tick{ position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); width:2px; height:22px; background: color-mix(in srgb, var(--bone) 40%, transparent); pointer-events:none; z-index:0; }
  /* visibility (PAS display) pour le bouton reset (2026-08-22, bug corrigé
     le jour même) : .cell-reset-btn{display:none}/.visible{display:inline-
     block} (règle de base, partagée avec .zoom-slider) fait varier la
     largeur réelle de .settings-slider selon que le bouton est montré ou
     non. Or .settings-slider vit dans .settings-row, qui est en
     justify-content:space-between — un changement de largeur y déplace
     TOUT le bloc .settings-slider (donc la piste et les repères "A" avec
     lui) exactement au moment où la valeur traverse 100, c'est-à-dire
     pile quand l'utilisateur tente de viser le centre. D'où l'impression
     de repères qui « sautent » et un glissé impossible à stabiliser.
     visibility:hidden réserve la place du bouton en permanence : plus
     aucune variation de largeur, donc plus aucun déplacement du reste de
     la rangée pendant le glissé. */
  /* Cause réelle trouvée (2026-09-05, capture d'écran à l'appui — "écart
     net entre la fin du curseur et le bord droit réel de la colonne") :
     .cell-reset-btn est le DERNIER enfant flex de .settings-slider, donc
     celui qui définit le bord droit réel du bloc (poussé par
     .settings-row{justify-content:space-between}) — mais il reste
     invisible (visibility:hidden) tant que rien n'a été modifié, si bien
     que le bord droit RÉEL (celui du bouton cité) ne coïncide jamais avec
     le bord droit VISIBLE (le label "A"/"4s" juste avant), créant l'écart
     visuel signalé, identique sur les 3 curseurs. order:-1 déplace le
     bouton EN PREMIER visuellement (sans changer l'ordre DOM/tabulation) :
     le label "A"/"4s" devient le dernier enfant visible, son bord droit
     coïncide enfin avec celui des .switch/<select> de la même colonne. */
  .settings-slider .cell-reset-btn{ position:relative; display:inline-block; visibility:hidden; color: var(--bone); order: -1; }
  .settings-slider .cell-reset-btn.visible{ visibility:visible; }
  .settings-slider .cell-reset-btn:hover{ color: var(--ember); }
  /* Info-bulle (2026-08-31, inventaire complet — mécanisme natif jusqu'ici,
     seul bouton de ce panneau à ne pas encore utiliser data-tooltip). Ni
     bord gauche ni droit ici (rangée de réglages dans une fenêtre modale,
     jamais collée à un bord d'écran) : centrée, sous le bouton. */
  .settings-slider [data-tooltip]::after{ top: calc(100% + 10px); left: 50%; transform: translateX(-50%); }
  /* --settings-default-text-color plutôt que --bone en dur (2026-09-03,
     nouveau paramètre "Couleur des textes et légendes" — une "légende" au
     sens littéral du nom de ce réglage). Même valeur par défaut, donc
     aucun changement visuel avant personnalisation. */
  .settings-label{ font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size: var(--fs-md); letter-spacing: 0.03em; color: var(--settings-default-text-color); }
  /* Agrandi 10.5px -> 12.5px + opacité 0.55 -> 0.65 (2026-08-30, bug
     corrigé, texte jugé illisible) et rattaché à --ui-scale : ce texte
     n'était lié à AUCUN des deux curseurs de taille (Réglages → Affichage),
     alors qu'il s'agit clairement d'un texte d'INTERFACE (aide contextuelle
     d'un réglage), pas de contenu de lecture — même catégorie que le fil
     d'Ariane/les libellés/boutons déjà couverts par --ui-scale. */
  /* Agrandi 12.5px -> 13px (2026-09-03, demande explicite "texte secondaire
     trop petit"). */
  /* --settings-default-text-color plutôt que --secondary-text-color
     (2026-09-03, extension de portée explicite — "Réglages > Affichage :
     descriptions des curseurs... ne reflètent pas encore ce paramètre") :
     secondary_text_color reste pour l'instant volontairement NON rattaché
     (futur chantier séparé, demande explicite précédente). */
  .settings-desc{ font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size: calc(var(--fs-md) * var(--ui-scale)); color: var(--settings-default-text-color); opacity: 0.65; margin-top: 3px; }
  /* Bannière "brouillon de thème en attente" (2026-09-08, demande explicite
     — prompt visible plutôt que reprise automatique silencieuse) : opacité
     pleine (contrairement à .settings-desc ci-dessus, une simple légende)
     et bordure signature pour signaler un vrai choix à faire, pas une
     information passive. */
  .theme-pending-draft-banner{ display:flex; align-items:center; flex-wrap:wrap; gap:10px; opacity:1; margin: 0 0 14px; padding: 10px 12px; border: 1px solid var(--ember); border-radius: 6px; }
  .theme-pending-draft-banner span{ flex: 1 1 220px; }
  /* .settings-save-btn porte align-self:flex-start (pensé pour un bouton
     seul dans une colonne) — surchargé ici pour rester centré avec le
     texte sur cette ligne. */
  .theme-pending-draft-banner button{ align-self: center; }
  .switch{ position:relative; display:inline-block; width: 38px; height: 22px; flex: 0 0 auto; }
  .switch input{ position:absolute; opacity:0; width:0; height:0; }
  .switch-track{ position:absolute; inset:0; background: color-mix(in srgb, var(--bone) 20%, transparent); border-radius: 22px; cursor:pointer; transition: background .15s ease; }
  .switch-track::before{ content:''; position:absolute; left:3px; top:3px; width:16px; height:16px; border-radius:50%; background: var(--bone); transition: transform .15s ease; }
  .switch input:checked + .switch-track{ background: var(--ember); }
  .switch input:checked + .switch-track::before{ transform: translateX(16px); }
  /* --action-button-text-color plutôt que --bone en dur (2026-09-03,
     nouveau paramètre "Libellés des boutons d'action") : Enregistrer/
     Annuler/Appliquer notamment.
     Pilule + casse normale (2026-09-03, demande explicite — "Enregistrer"/
     "Annuler" restaient les seuls boutons du site hors de la convention
     pilule dominante) : majuscules retirées, rayon aligné sur .theme-edit-
     btn/.users-action-bar button (16px). Partagée par 17 boutons du site
     (Enregistrer, Annuler, Backup, Envoyer...) — uniformisés d'un coup. */
  /* Largeur ajustée au contenu, pas pleine largeur (2026-09-04, demande
     explicite — "certains boutons font toute la largeur de l'écran") :
     cause racine, .settings-tabpanel/.modal-field (flex-direction:column)
     ont un align-items:stretch implicite, qui étire tout enfant direct —
     y compris un <button>, blockifié par la mise en page flex — sur toute
     la largeur du panneau, sans qu'aucune règle de largeur explicite ne
     soit posée sur le bouton lui-même. align-self:flex-start annule cet
     étirement pour CE type de bouton uniquement, où qu'il soit placé. */
  .settings-save-btn{ align-self: flex-start; font-family:var(--text-font-family); font-size: calc(var(--fs-sm) * var(--ui-scale)); letter-spacing: 0.03em; color: var(--action-button-text-color); background: color-mix(in srgb, var(--bone) 8%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 25%, transparent); padding: 10px 14px; border-radius: 16px; cursor:pointer; transition: background .15s ease, border-color .15s ease; }
  .settings-save-btn:hover{ background: color-mix(in srgb, var(--bone) 15%, transparent); border-color: color-mix(in srgb, var(--bone) 40%, transparent); }
  /* Mode "Appliquer" permanent actif (2026-09-04) : fond signature du
     thème, pour distinguer visuellement l'état "chaque modification part
     en direct" de l'état normal. */
  #themeApplyBtn.is-live{ background: var(--ember); border-color: var(--ember); color: var(--bone); }
  #themeApplyBtn.is-live:hover{ background: var(--ember); border-color: var(--ember); }
  /* Agrandi 10px -> 11px (2026-09-03, même demande que .settings-desc). */
  .settings-note{ font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size: var(--fs-xs); line-height: 1.5; color: var(--settings-default-text-color); opacity: 0.5; }
  .settings-admin-inner{ display:flex; flex-direction:column; gap: 10px; padding-top: 14px; border-top: 1px solid color-mix(in srgb, var(--bone) 12%, transparent); }
  /* Onglet "Compte" (2026-08-19) : le bloc admin y est le SEUL contenu du
     panneau, pas une extension sous d'autres réglages — pas de séparateur
     flottant sans rien au-dessus. */
  .settings-tabpanel > .settings-admin:first-child .settings-admin-inner{ padding-top: 0; border-top: none; }
  /* SCOPÉ à #themesTabPanel (2026-09-03), pas à la classe de base : celle-ci
     est partagée par ~8 écrans (Droits d'accès, Statistiques, Tech Info,
     Backup, Affichage, Thèmes, Aide...) où plusieurs blocs peuvent
     coexister empilés dans un même onglet — un flex:1 auto généralisé à
     TOUS les aurait fait rivaliser pour le même espace au lieu de
     simplement s'empiler. Seul le contenu de l'éditeur de thème a besoin
     de s'étirer jusqu'au bas de la fenêtre (voir .theme-editor-rows-scroll
     plus bas). */
  /* Même exclusion que la règle :first-child ci-dessus, mais #themesTabPanel
     EST directement le .settings-tabpanel (pas de .settings-admin
     intermédiaire ici) : le sélecteur générique ne le couvre donc pas,
     d'où padding-top/border-top redéclarés ici (2026-09-04, "la fenêtre
     commence par un séparateur inutile"). */
  #themesTabPanel > .settings-admin-inner{ flex: 1 1 auto; min-height: 0; padding-top: 0; border-top: none; }
  /* Même exclusion, même raison (2026-09-13, Sécurité fusionnée en un seul
     écran — voir ensureAccountModal()) : #settingsAdminSecurity n'est pas
     :first-child de son .settings-tabpanel (précédé de la section Passkey),
     donc la règle générique ci-dessus (825) ne s'applique pas — sans cette
     exception, son .settings-admin-label "Politique" + le .settings-sep
     manuel juste avant se retrouvent doublés par la bordure automatique de
     .settings-admin-inner, juste en dessous du libellé. */
  #settingsAdminSecurity > .settings-admin-inner{ padding-top: 0; border-top: none; }
  /* --settings-label-color plutôt que --dust-gold en dur (2026-08-31,
     système de thèmes) : même valeur aujourd'hui — ce libellé de section
     est de la même famille que .exif-row .k (voir plus bas), un seul
     réglage de thème partagé. */
  /* Style de référence UNIQUE pour tout "libellé de groupe" des fenêtres de
     paramétrage (2026-09-02, généralisé depuis .theme-group-label — jugé
     trop petit à 10px ; promu ici pour profiter à TOUS les écrans qui
     partagent déjà cette classe : Droits d'accès, Statistiques, Tech Info,
     Backup, Affichage, liste/éditeur/association de thèmes — voir aussi
     .help-theme-label plus bas, migré sur cette même base pour Aide/Aide
     et Aide/Spécifications). `font-weight:600` RETIRÉ le 2026-09-03 (retour
     direct après déploiement — "gras excessif, nuit à la lisibilité" une
     fois vu sur tous ces écrans à la fois) : seule la taille (13px) reste
     du traitement d'origine, le poids repasse à la normale du navigateur. */
  .settings-admin-label{ font-family:var(--text-font-family); font-size: var(--fs-md); letter-spacing: 0.04em; text-transform: uppercase; color: var(--settings-label-color); opacity: 0.9; }
  .settings-admin:empty{ display:none; }
  .settings-sep{ height: 1px; background: color-mix(in srgb, var(--settings-bg), white 12%); margin: -4px 0; }
  .admin-select{ background: color-mix(in srgb, var(--bone) 8%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius: 6px; padding: 6px 8px; color: var(--settings-default-text-color); font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size: var(--fs-sm); }
  /* Menus déroulants natifs = données, pas des boutons (2026-09-03, demande
     explicite) : règle UNIQUE, volontairement plus spécifique que
     .admin-select/.users-toolbar-filters select/.modal-field select
     (0,2,1 contre 0,1,0 ou 0,1,1 pour ces dernières) pour les emporter
     sans dépendre de l'ordre des règles — couvre TOUS les <select> natifs
     de la fenêtre Compte en un seul endroit (sélecteur de thème dans la
     grille Voyages, filtres de recherche Utilisateurs/Voyages, résolution
     de Partage en masse, réglages Affichage...), plutôt qu'une classe à
     ajouter partout dans le markup. Fond/contour "bouton" retirés — RIEN
     d'autre ajouté à la place (2026-09-03, retour "retirer aussi le
     soulignement" — le pointillé du 1er essai retiré) : uniquement le
     texte et la petite flèche, comme demandé. */
  .users-modal.account-modal select{
    appearance: none; -webkit-appearance: none; -moz-appearance: none;
    background-color: transparent;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6'%3E%3Cpath d='M1 1l3.5 3.5L8 1' stroke='%23f6f1e6' stroke-width='1.4' fill='none' stroke-linecap='round' stroke-linejoin='round' opacity='0.6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 2px center;
    border: none;
    border-radius: 0;
    padding: 4px 16px 5px 2px;
    color: var(--settings-default-text-color);
  }
  /* Combos AUTONOMES (2026-09-04, retour explicite — "sans contour, on ne
     voit pas qu'il faut cliquer dedans") : contour/cadre réintroduit pour
     tout <select class="admin-select"> qui n'est PAS le sélecteur de thème
     de la grille Voyages (.voyages-theme-select, qui doit lui rester
     borderless — même combo que la grille, demande explicite d'un tour
     précédent). Spécificité (0,3,1) > la règle borderless ci-dessus
     (0,2,1) : l'emporte sans dépendre de l'ordre. Portée : tous les combos
     autonomes du site (Partage en masse, Système/Environnement,
     Sécurité, Affichage) — leur libellé fixe vient de .settings-row/
     .settings-label|.settings-desc déjà en place à leur gauche. */
  .users-modal.account-modal select.admin-select:not(.voyages-theme-select){
    appearance: none; -webkit-appearance: none; -moz-appearance: none;
    background-color: color-mix(in srgb, var(--bone) 8%, transparent);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6'%3E%3Cpath d='M1 1l3.5 3.5L8 1' stroke='%23f6f1e6' stroke-width='1.4' fill='none' stroke-linecap='round' stroke-linejoin='round' opacity='0.6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    border: 1px solid color-mix(in srgb, var(--bone) 25%, transparent);
    border-radius: 6px;
    padding: 6px 28px 6px 10px;
    color: var(--settings-default-text-color);
  }
  /* Cases à cocher de la fenêtre paramètres : une seule règle, comme les
     <select> ci-dessus, couvre toutes les cases (sélection groupée
     Utilisateurs/Voyages/Sessions/Passkeys, switches exceptés — voir
     .switch, déjà son propre style dédié). Paramètre de thème séparé
     "Case à cocher" RETIRÉ (2026-09-05, demande explicite — "géré
     automatiquement, dérivé de la couleur signature") : dérivée
     directement de --ember (couleur signature), jamais stockée
     séparément. Case personnalisée (appearance:none) plutôt que le rendu
     natif du navigateur : accent-color seul ne permet de teinter QUE
     l'état coché, jamais le fond de la case décochée. Décochée = fond
     très clair dérivé via color-mix(), pas de bordure visible. */
  .users-modal.account-modal input[type="checkbox"]{
    appearance: none; -webkit-appearance: none; -moz-appearance: none;
    width: 15px; height: 15px; margin: 0; flex: 0 0 auto;
    /* border:1px solid transparent plutôt que border:none (2026-09-05,
       "toujours l'ancien contour sur l'état décoché, malgré border:none")
       — même taille de boîte, mais certains navigateurs retombent sur un
       reliquat de chrome natif quand la bordure est totalement absente
       plutôt que transparente. outline supprimé aussi : "contour" pouvait
       désigner l'anneau de focus natif (visible après un clic sur "Tout
       sélectionner", jamais traité jusqu'ici), pas seulement une bordure. */
    border: 1px solid transparent;
    outline: none;
    border-radius: 4px;
    /* Teinte renforcée (2026-09-05, retour explicite — "trop blanc/
       brillant à l'état décoché") : 10% -> 28%, plus visiblement teinté
       tout en restant nettement plus clair que l'état coché. */
    background: color-mix(in srgb, var(--ember) 28%, white 72%);
    cursor: pointer; position: relative; vertical-align: middle;
  }
  .users-modal.account-modal input[type="checkbox"]:focus-visible{
    outline: 2px solid var(--ember);
    outline-offset: 1px;
  }
  /* Coché ET indéterminé (2026-09-05, demande explicite — "convention
     standard macOS/Windows/Gmail") : même fond plein que coché, tiret
     horizontal au centre au lieu de la coche pour indéterminé. */
  .users-modal.account-modal input[type="checkbox"]:checked,
  .users-modal.account-modal input[type="checkbox"]:indeterminate{
    background: var(--ember);
    border-color: var(--ember);
  }
  .users-modal.account-modal input[type="checkbox"]:checked::after{
    content: ''; position: absolute; left: 4px; top: 1px;
    width: 4px; height: 8px;
    border: solid var(--white); border-width: 0 2px 2px 0;
    transform: rotate(45deg);
  }
  .users-modal.account-modal input[type="checkbox"]:indeterminate::after{
    content: ''; position: absolute; left: 3px; top: 6px;
    width: 7px; height: 2px;
    background: var(--white);
  }
  /* Onglet Réglages/Affichage/Thèmes (2026-08-31) : réutilise au maximum
     les idiomes déjà en place (.settings-row/.settings-save-btn/
     .admin-select/.cell-reset-btn) — seuls les éléments propres au
     système de thèmes (liste, pastille couleur, saisie couleur, actions
     de l'éditeur) ont une classe dédiée. */
  .theme-list-swatch{ flex:0 0 auto; width: 18px; height: 18px; border-radius: 50%; border: 1px solid color-mix(in srgb, var(--bone) 30%, transparent); }
  /* Ligne compacte (2026-08-31, remplace la liste avec un bouton
     Modifier/Supprimer par thème) : pastille de la couleur d'accent du
     thème sélectionné + menu déroulant personnalisé (même schéma
     bouton+popup que .account-widget/.lang-widget — un <select> natif ne
     peut pas afficher une pastille de couleur par option) + 2 boutons
     agissant sur la sélection du menu, jamais sur une ligne. */
  .theme-selector-row{ display:flex; align-items:center; gap: 10px; padding: 8px 0; }
  /* Uniformisé avec le <select> natif de la grille Voyages (2026-09-04,
     demande explicite — remplace l'ancien widget bouton+popup, qui
     existait pour afficher une pastille de couleur par option, impossible
     dans un <select> natif) : hérite déjà du style "donnée, pas un bouton"
     via .users-modal.account-modal select. Largeur réduite d'environ 2/3
     (retour explicite — "trop large", rapprochant les 3 boutons vers la
     gauche) : flex:1 (qui le forçait à remplir tout l'espace restant)
     retiré au profit de flex:0 1 auto — largeur au contenu réel (nom du
     thème), capable de s'élargir automatiquement pour un nom plus long. */
  .theme-select-native{ flex: 0 1 auto; width: auto; min-width: 80px; max-width: 220px; }
  /* font-size/border-radius alignés sur .users-action-bar button (2026-09-03,
     demande explicite — "Supprimer" jugé visuellement particulier) : ce
     trio Modifier/Créer/Supprimer avait été stylé indépendamment lors de
     la création de l'éditeur de thème (11px/14px), légèrement différent du
     style DÉJÀ établi et partagé par les barres d'action Utilisateurs ET
     Voyages (11.5px/16px) — uniformisé sur ce dernier, le standard déjà en
     place sur plusieurs écrans plutôt qu'un cas isolé. */
  /* "Supprimer" strictement identique aux autres (2026-09-03, demande
     explicite reformulée — la couleur "danger" du tour précédent n'était
     PAS ce qui était demandé) : plus AUCUNE distinction visuelle avec
     Modifier/Créer (classe "danger" retirée du markup ET de son style ici
     — voir aussi .users-action-bar button, même traitement) — seul le
     libellé/contenu du bouton diffère désormais. */
  /* --action-button-text-color plutôt que --bone en dur (2026-09-03,
     nouveau paramètre "Libellés des boutons d'action") : Modifier/Créer/
     Supprimer, même traitement que .settings-save-btn/.users-action-bar
     button plus bas. */
  .theme-edit-btn, .theme-delete-btn{ flex:0 0 auto; font-family:var(--text-font-family); font-size: var(--fs-sm); letter-spacing: 0.03em; color: var(--action-button-text-color); background: color-mix(in srgb, var(--bone) 8%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 25%, transparent); padding: 8px 12px; border-radius: 16px; cursor:pointer; }
  .theme-edit-btn:hover, .theme-delete-btn:hover{ background: color-mix(in srgb, var(--bone) 15%, transparent); }
  /* 3 états Modifier/Créer/Supprimer (2026-09-03, demande explicite) :
     "en cours d'utilisation" (édition/création active sur CE bouton),
     "désactivé" (les 2 autres, via l'attribut natif disabled — jamais
     cliquables, pas seulement grisés visuellement). :disabled AVANT .in-use
     dans l'ordre des règles : sans effet ici (sélecteurs disjoints, un
     bouton n'a jamais les deux à la fois), gardé dans un ordre logique. */
  .theme-edit-btn.in-use{ background: var(--ember); border-color: var(--ember); color: var(--action-button-text-color); }
  .theme-edit-btn.in-use:hover{ background: var(--ember); }
  .theme-edit-btn:disabled, .theme-delete-btn:disabled{ opacity: 0.35; cursor: not-allowed; }
  .theme-edit-btn:disabled:hover, .theme-delete-btn:disabled:hover{ background: color-mix(in srgb, var(--bone) 8%, transparent); }
  /* En-tête condensé, 2 lignes fixes (2026-09-03, refonte — remplace
     l'ancien pied de page "Association au voyage" + le message "Modification
     en cours..." + le nom du thème en ligne séparée) : ligne 2, sous le
     sélecteur de thème — raccourci d'association à gauche, actions
     Appliquer/Enregistrer/Annuler à droite. Seule la LISTE de paramètres en
     dessous défile (voir .theme-editor-rows-scroll) ; ces 2 lignes restent
     visibles en permanence. */
  .theme-toolbar-row{ display:flex; align-items:center; justify-content:space-between; gap: 10px; padding: 4px 0 8px; flex-wrap:wrap; }
  .theme-assign-shortcut{ display:flex; align-items:center; gap: 10px; }
  /* --ember (Signature du thème) plutôt que --bone en dur (2026-09-03,
     demande explicite) : ce libellé identifie le voyage habillé par CE
     thème précis, cohérent avec la couleur d'accent plutôt qu'un texte
     générique. */
  .theme-assign-voyage-name{ font-family:var(--text-font-family); font-size: var(--fs-md); color: var(--ember); opacity: 0.85; }
  /* Boutons d'action désormais toujours présents, mais désactivés hors
     édition (2026-09-03) : "2 lignes FIXES" — plus de saut de mise en page
     à l'ouverture/fermeture de l'éditeur, contrairement à l'ancien
     affichage conditionnel de ce bloc entier. */
  .settings-save-btn:disabled{ opacity: 0.35; cursor: not-allowed; }
  .settings-save-btn:disabled:hover{ background: color-mix(in srgb, var(--bone) 8%, transparent); border-color: color-mix(in srgb, var(--bone) 25%, transparent); }
  /* Saisie couleur : sélecteur natif (Apple/macOS déclenché nativement par
     le navigateur pour <input type="color">) + code hex en repli/aperçu
     rapide (demande explicite). appearance:none + padding réduit sur le
     picker natif : sinon un cadre/fond par défaut du navigateur, jamais
     cohérent avec le reste du site. */
  .theme-color-controls{ display:flex; align-items:center; gap: 8px; }
  /* Infobulle du ↺ repositionnée (2026-09-16, demande explicite, maquette
     validée) : jusqu'ici centrée au-dessus du bouton (règle générique
     [data-tooltip]::after), elle empiétait sur l'icône voisine dès que 2
     paramètres consécutifs avaient tous deux leur ↺ visible. position:
     relative posé ICI sur le bouton lui-même (jamais positionné avant) :
     ::after se cadre alors directement contre SON PROPRE bord gauche
     (right:100%) — reste sur la même ligne (centré verticalement sur le
     bouton, lui-même centré sur .settings-row), s'étend dans l'espace vide
     à gauche vers le libellé, jamais vers l'icône ni vers une autre ligne.
     Aucune largeur fixe supposée : robuste si la disposition change. */
  .theme-color-controls .cell-reset-btn{ position:relative; }
  /* Repositionnée AU-DESSUS (2026-09-25, l'extension à gauche persistait à
     empiéter sur les icônes voisines) : .theme-color-controls porte 2
     boutons ↺/⊘ ("cell-reset-btn"), pas un seul — l'extension à gauche
     (right:100%) ne laissait de vide que pour le PREMIER (↺, tout à
     gauche, rien d'autre à sa gauche que le libellé) ; pour le second (⊘,
     après le champ hex), la même règle traversait TOUT le groupe d'icônes
     à sa gauche (↺, rond de couleur, champ hex). Centrée au-dessus, comme
     le mécanisme générique [data-tooltip]::after par défaut : jamais de
     collision horizontale possible avec un voisin de la même ligne, quel
     que soit le bouton concerné. */
  .theme-color-controls .cell-reset-btn[data-tooltip]::after{
    top:auto; bottom:calc(100% + 8px); left:50%; right:auto; transform:translateX(-50%); margin-right:0;
  }
  /* Polices de thème (2026-09-08) — même ligne .settings-row que les
     couleurs ci-dessus, mais un <select> (largeur fixe pour aligner
     visuellement les 2 lignes title_font/text_font) + 2 cases à cocher au
     lieu d'un sélecteur de couleur. */
  .theme-font-controls{ display:flex; align-items:center; gap: 12px; flex-wrap: wrap; }
  .theme-font-toggle{ display:flex; align-items:center; gap: 5px; font-family:var(--text-font-family); font-size: var(--fs-sm); color: var(--settings-default-text-color); cursor:pointer; white-space:nowrap; }
  /* Menu déroulant "maison" (2026-09-08) — remplace un <select> natif pour
     CE seul contrôle : sur macOS, Chrome/Safari rendent la liste d'un
     <select> via le menu natif du système, qui ignore le style par
     <option> — aucun moyen d'y garantir un aperçu par police. Même patron
     visuel que .account-menu/.lang-menu (popup déjà existants ailleurs sur
     le site) pour rester cohérent. */
  .theme-font-picker{ position:relative; width: 170px; }
  /* "Nom du thème"/"Description" élargis (2026-09-20, demande explicite) :
     .theme-color-controls/.theme-font-controls sont des blocs ANCRÉS À
     DROITE de leur ligne (.settings-row en justify-content:space-between)
     — le bord GAUCHE du combo "Titres voyage et activités" (170px) n'est
     donc PAS à 170px du bord droit de la ligne : il faut aussi compter tout
     ce qui le SUIT sur cette même ligne (Gras/Italique/⊘), qui pousse son
     bord gauche d'autant plus vers la gauche. 350px ≈ 170 (combo) + ses
     éléments suivants + les espacements — aligne "Nom"/"Description" sur ce
     même bord gauche, puisque ces deux champs n'ont eux-mêmes rien après. */
  #themeNameInput{ width: 350px; }
  #themeDescriptionInput{ width: 350px; resize: vertical; font-family: inherit; }
  /* Chevron repris tel quel de select.admin-select (style.css, règle
     "tous les combos autonomes du site") : ce déclencheur est un <button>,
     jamais ciblé par ce sélecteur élément-`select`. */
  .theme-font-picker-trigger{
    width:100%; text-align:left; box-sizing:border-box;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6'%3E%3Cpath d='M1 1l3.5 3.5L8 1' stroke='%23f6f1e6' stroke-width='1.4' fill='none' stroke-linecap='round' stroke-linejoin='round' opacity='0.6'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right 8px center; padding-right: 24px;
  }
  .theme-font-picker-menu{ display:none; position:absolute; top:calc(100% + 4px); left:0; width: 240px; max-height: 320px; overflow-y:auto; background: color-mix(in srgb, var(--night) 97%, transparent); border-radius:8px; box-shadow:0 20px 45px color-mix(in srgb, var(--ink) 35%, transparent); z-index:70; flex-direction:column; padding: 4px 0; }
  .theme-font-picker.open .theme-font-picker-menu{ display:flex; }
  /* Agrandi (2026-09-08, demande explicite — "lignes trop petites") : 13px/
     8px de rembourrage jugés trop denses pour une liste de ~35 polices. */
  .theme-font-picker-option{ display:block; width:100%; text-align:left; background:none; border:none; color: var(--bone); font-size:var(--fs-2xl); padding:11px 16px; cursor:pointer; }
  .theme-font-picker-option:hover{ background: color-mix(in srgb, var(--bone) 10%, transparent); }
  .theme-font-picker-option.is-selected{ color: var(--ember); }
  .theme-color-picker{ appearance:none; -webkit-appearance:none; width: 30px; height: 30px; padding: 0; border: 1px solid color-mix(in srgb, var(--bone) 30%, transparent); border-radius: 6px; background: none; cursor:pointer; }
  .theme-color-picker::-webkit-color-swatch-wrapper{ padding: 2px; }
  .theme-color-picker::-webkit-color-swatch{ border: none; border-radius: 4px; }
  /* Indicateur "valeur par défaut du système" (2026-09-05, nouveau —
     distinct du ↺ qui revient à la dernière valeur ENREGISTRÉE) : signe
     visuel directement dans le carré de couleur, pas un texte à côté. */
  .theme-color-swatch-wrap{ position:relative; display:inline-flex; }
  .theme-color-swatch-wrap.is-system-default::after{
    content: '✕'; position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
    font-size: var(--fs-md); color: var(--white); text-shadow: 0 0 3px color-mix(in srgb, var(--black-pure) 70%, transparent); pointer-events:none;
  }
  /* État distinct "aucun défaut système défini" (2026-09-05, cas validé
     explicitement — jamais confondu avec le "✕" ci-dessus, qui signale au
     contraire que la valeur par défaut EXISTE et s'applique) : hachures +
     "—" au centre, .theme-color-picker masqué en dessous (rien à afficher
     puisqu'il n'y a pas de défaut vers quoi comparer). */
  .theme-color-swatch-wrap.no-system-default .theme-color-picker{ visibility:hidden; }
  .theme-color-swatch-wrap.no-system-default::before{
    content: ''; position:absolute; inset:0; border-radius:6px;
    background: repeating-linear-gradient(45deg, color-mix(in srgb, var(--bone) 25%, transparent) 0 4px, transparent 4px 8px);
    border: 1px solid color-mix(in srgb, var(--bone) 30%, transparent);
  }
  .theme-color-swatch-wrap.no-system-default::after{
    content: '—'; position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
    font-size: var(--fs-xl); color: var(--settings-default-text-color); pointer-events:none;
  }
  /* RAZ vers le défaut système (2026-09-05, nouveau, distinct de
     .theme-color-reset ↺) : TOUJOURS visible (contrairement au ↺, réservé
     à un champ modifié depuis l'enregistrement) — icône différente pour
     ne jamais confondre les deux actions. */
  /* Sélecteur qualifié par le type (2026-09-05, leçon du bug ".+"/
     .users-action-bar button déjà rencontré) : la règle de base
     .cell-reset-btn{display:none} (plus bas dans ce fichier) a la MÊME
     spécificité qu'une classe seule et la remporterait par ordre de
     déclaration — button.theme-color-system-reset (0,1,1) l'emporte sans
     ambiguïté, quel que soit l'ordre. */
  /* visibility:hidden par défaut (2026-09-05, corrigé — un essai précédent
     retirait ce bouton du DOM selon les lignes, décalant la colonne du
     champ hex) : même mécanisme que .theme-color-reset (↺) juste à côté,
     l'espace reste réservé que le bouton soit pertinent ou non sur cette
     ligne, la colonne des champs hex reste alignée sur toutes les lignes. */
  button.theme-color-system-reset{ display:inline-block; visibility:hidden; font-size: var(--fs-2xl); color: var(--settings-default-text-color); opacity: 0.5; }
  button.theme-color-system-reset.visible{ visibility:visible; }
  button.theme-color-system-reset:hover{ opacity: 1; color: var(--c-orange-moyen-4); }
  .theme-color-hex{ width: 84px; background: color-mix(in srgb, var(--bone) 8%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius: 6px; padding: 6px 8px; color: var(--settings-default-text-color); font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size: var(--fs-sm); text-transform: uppercase; }
  .theme-editor-actions{ display:flex; gap: 8px; flex-wrap:wrap; padding-top: 4px; }
  /* Zone défilante des paramètres SEULS (2026-09-01, demande explicite) :
     nom du thème (désormais un champ DANS cette liste, groupe "Global",
     2026-09-03), en-têtes de groupe visibles au survol du défilement, et
     les 2 lignes fixes au-dessus (sélecteur + raccourci/actions) restent
     hors de cette boîte — plus toute la fenêtre qui défile d'un bloc,
     perdant la sélection du thème et les boutons d'action hors champ.
     Vrai flex:1 auto (2026-09-03, correction d'une analyse erronée du tour
     précédent — un max-height fixe de 480px avait été posé à la place, en
     pensant qu'aucun ancêtre borné n'existait pour justifier un flex-fill
     réel) : #themesTabPanel EST déjà un .settings-tabpanel, qui reçoit
     height:100%/min-height:0 quand .active (voir plus haut), et vit dans
     le .settings-body IMBRIQUÉ propre aux sous-onglets de Réglages
     (Affichage/Thèmes/Système) — exactement le même patron déjà validé et
     FONCTIONNEL pour .users-table-scroll (Utilisateurs > Droits d'accès,
     voir plus bas) : la chaîne de hauteurs bornées existe réellement,
     jusqu'ici simplement pas exploitée à cet endroit précis. */
  .theme-editor-rows-scroll{ flex: 1 1 auto; min-height: 120px; overflow-y:auto; overscroll-behavior: none; display:flex; flex-direction:column; gap: 2px; padding-right: 4px; scrollbar-width: thin; scrollbar-color: rgba(246,241,230,0.18) transparent; }
  /* Barre de défilement adoucie (2026-09-25, demande explicite — "trop
     voyante au survol") : la barre native macOS/Chrome (overlay épais,
     contrasté au survol) remplacée par une fine barre discrète et STABLE
     (même apparence survolée ou non — scrollbar-width/-color ci-dessus
     couvrent Firefox, ces 3 règles Chrome/Safari). */
  .theme-editor-rows-scroll::-webkit-scrollbar{ width:6px; }
  .theme-editor-rows-scroll::-webkit-scrollbar-track{ background:transparent; }
  .theme-editor-rows-scroll::-webkit-scrollbar-thumb{ background: color-mix(in srgb, var(--bone) 18%, transparent); border-radius:3px; }
  /* Taille/graisse retirées d'ici (2026-09-02) : .settings-admin-label (la
     classe de base) porte désormais directement ce même traitement,
     généralisé à tous les écrans qui la partagent — cette classe ne garde
     plus que son rôle réel, l'espacement entre groupes de couleurs. */
  /* Séparateur ENTRE chaque groupe, jamais avant le premier (2026-09-04,
     demande explicite — la zone défilante ne doit plus commencer par un
     séparateur inutile). Même formule de couleur que .settings-sep. */
  .theme-group-label, .settings-group-label{ margin-top: 10px; padding-top: 10px; border-top: 1px solid color-mix(in srgb, var(--settings-bg), white 12%); }
  .theme-group-label:first-child, .settings-group-label:first-child{ margin-top: 0; padding-top: 0; border-top: none; }

  /* ---------- MODALE "GÉRER LES ACCÈS" (même esprit visuel que le panneau ⚙️) ----------
     Layout en colonne flex sur toute la hauteur de la modale : en-tête,
     filtres et barre d'actions gardent leur hauteur naturelle (jamais de
     scroll dessus) ; seule .users-table-scroll défile, avec son propre
     thead sticky — évite le calcul fragile d'un empilement de plusieurs
     éléments sticky à offsets différents. */
  .users-modal-backdrop{ position:fixed; inset:0; background: color-mix(in srgb, var(--ink) 55%, transparent); z-index: 200; display:none; align-items:center; justify-content:center; padding: 24px; }
  .users-modal-backdrop.open{ display:flex; }
  /* Onglet "Thèmes" (2026-08-31, demande explicite) : le fond estompé
     laisse passer les clics (pointer-events:none) et disparaît (aucun
     assombrissement) pour que le site derrière reste consultable/cliquable
     pendant que la fenêtre Réglages > Thèmes reste ouverte au-dessus —
     voir updateModalClickThrough() dans app.js, qui bascule cette classe.
     .users-modal doit reprendre pointer-events:auto explicitement : sinon
     il hériterait de none et deviendrait lui-même incliquable. */
  .users-modal-backdrop.click-through{ background:none; pointer-events:none; }
  .users-modal-backdrop.click-through .users-modal{ pointer-events:auto; }
  /* position:relative (2026-08-21, chantier fenêtres communes) : point
     d'ancrage pour les 8 poignées .users-modal-resizer (position:absolute),
     posées sur TOUTE modale désormais — sans ça elles s'ancreraient sur
     .users-modal-backdrop (le prochain ancêtre positionné), pas sur les
     bords de la modale elle-même, tant qu'aucun glissé n'a encore basculé
     l'élément en position:fixed (voir initFloatingModal() dans app.js). */
  /* Transparence résiduelle retirée (2026-09-04, demande explicite — "on
     devine les vignettes de la page en arrière-plan") : 97% opaque -> 100%
     (var(--settings-bg) directement, plus de color-mix avec transparent). */
  .users-modal{ position:relative; width:100%; max-width: 820px; max-height: 85vh; display:flex; flex-direction:column; background: var(--settings-bg); color: var(--bone); border-radius: 8px; box-shadow: 0 20px 45px color-mix(in srgb, var(--ink) 35%, transparent); overflow:hidden; }
  /* Couleur de texte par défaut themable (2026-09-03, "Couleur des textes
     et légendes") : SCOPÉ à .account-modal (pas la classe .users-modal de
     base, partagée par d'autres fenêtres flottantes sans rapport avec un
     thème de voyage — commentaires/likers/Passkeys en détail) — seule la
     fenêtre Compte/Réglages doit suivre le thème du voyage affiché.
     .users-modal.account-modal (specificité 0,2,0) l'emporte sur
     .users-modal seule (0,1,0) ci-dessus, quel que soit l'ordre des règles.
     `color` étant hérité, tout texte à l'intérieur qui ne fixe pas déjà la
     sienne (ex. Passkeys, jamais stylé explicitement) suit désormais cette
     valeur automatiquement. */
  .users-modal.account-modal{ color: var(--settings-default-text-color); }
  /* Taille de PREMIÈRE ouverture propre à "Accès utilisateurs" (plus grande
     que les autres modales, vu le tableau) — le redimensionnement lui-même
     n'est plus spécifique à cette modale depuis le 2026-08-21 (chantier
     "comportement commun aux fenêtres de paramétrage") : TOUTE modale est
     désormais redimensionnable via les mêmes 8 poignées .users-modal-
     resizer + initFloatingModal()/makeModalResizable() dans app.js. Cette
     classe ne fixe donc plus qu'un point de départ ; au-delà, chaque
     modale déclare le sien via son propre max-width en ligne (Contact,
     About, Passkeys, Messages, Statistiques...) ou cette classe (Accès
     utilisateurs). Largeur/hauteur fixées explicitement (pas juste
     width:100% plafonné par max-width) pour qu'une poignée puisse agrandir
     librement au-delà de la taille d'origine, pas seulement la réduire.
     CSS `resize:both` abandonné (2026-08-21, essayé la veille) : AUCUN
     effet dans Safari, qui n'implémente cette propriété que sur <textarea>
     — jamais sur un élément quelconque (limite WebKit de longue date, pas
     un bug de ce site). Remplacé par une poignée maison, HUIT poignées (4
     bords + 4 coins, un seul coin ne suffisait pas) — même patron que
     .users-col-resizer déjà utilisé pour les colonnes du tableau, mais
     bascule la modale en position:fixed dès le premier glissé (centrée par
     flex sur .users-modal-backdrop jusque-là) : sans ce changement, ne
     toucher qu'à width/height depuis un bord gauche/haut ferait bouger la
     fenêtre deux fois plus vite que le curseur (le centrage flex
     compenserait la moitié du mouvement de l'autre côté). Taille ET
     position persistées PAR modale (localStorage, clé dérivée de son id —
     voir modalSizeKey()/modalPosKey() dans app.js). */
  .users-modal--resizable{ width:820px; max-width: 95vw; min-width: 480px; height:640px; max-height: 95vh; min-height: 320px; }
  /* Poignées ENTIÈREMENT à l'intérieur de la boîte de la modale (2026-08-21,
     bug corrigé — auparavant à cheval sur le bord, moitié en dehors via un
     décalage négatif) : quand la modale touche un bord du viewport (fenêtre
     Chrome non maximisée, ou après un agrandissement volontaire), une
     poignée qui déborde AU-DELÀ de ce bord tombe dans la zone que l'OS/le
     navigateur réserve à SON PROPRE redimensionnement de fenêtre — le clic
     n'atteint alors jamais notre gestionnaire mousedown, capté avant par ce
     niveau-là (constaté en conditions réelles : plus aucune prise possible
     dès que la modale touchait les deux bords à la fois). Rester
     strictement dans [0, largeur/hauteur de la modale] garantit que la
     poignée reste cliquable quelle que soit la position de la modale. */
  .users-modal-resizer{ position:absolute; z-index:6; }
  .users-modal-resizer[data-dir="n"]{ top:0; left:12px; right:12px; height:8px; cursor:ns-resize; }
  .users-modal-resizer[data-dir="s"]{ bottom:0; left:12px; right:12px; height:8px; cursor:ns-resize; }
  .users-modal-resizer[data-dir="e"]{ right:0; top:12px; bottom:12px; width:8px; cursor:ew-resize; }
  .users-modal-resizer[data-dir="w"]{ left:0; top:12px; bottom:12px; width:8px; cursor:ew-resize; }
  .users-modal-resizer[data-dir="ne"]{ top:0; right:0; width:16px; height:16px; cursor:nesw-resize; z-index:7; }
  .users-modal-resizer[data-dir="nw"]{ top:0; left:0; width:16px; height:16px; cursor:nwse-resize; z-index:7; }
  .users-modal-resizer[data-dir="sw"]{ bottom:0; left:0; width:16px; height:16px; cursor:nesw-resize; z-index:7; }
  .users-modal-resizer[data-dir="se"]{ bottom:0; right:0; width:16px; height:16px; cursor:nwse-resize; z-index:7; }
  /* Grip visuel uniquement sur le coin bas-droit (le plus attendu, voir
     l'ancienne poignée unique) — les 7 autres restent invisibles, juste un
     changement de curseur au survol, comme la plupart des fenêtres natives. */
  .users-modal-resizer[data-dir="se"]::before{ content:''; position:absolute; right:5px; bottom:5px; width:9px; height:9px; border-right:2px solid color-mix(in srgb, var(--bone) 40%, transparent); border-bottom:2px solid color-mix(in srgb, var(--bone) 40%, transparent); }
  .users-modal-resizer[data-dir="se"]:hover::before{ border-color: color-mix(in srgb, var(--bone) 70%, transparent); }
  .users-modal-head{ flex: 0 0 auto; display:flex; align-items:center; justify-content:space-between; padding: 16px 20px 14px; font-family:var(--text-font-family); font-size: var(--fs-md); letter-spacing: 0.06em; text-transform: uppercase; border-bottom: 1px solid color-mix(in srgb, var(--bone) 12%, transparent); cursor:grab; }
  .users-modal-head button{ position:relative; background:none; border:none; color: var(--bone); opacity:0.6; cursor:pointer; font-size: var(--fs-md); width:40px; height:40px; margin: -13px; display:inline-flex; align-items:center; justify-content:center; }
  .users-modal-head button:hover{ opacity:1; }
  /* Bouton "Détacher" (2026-09-08) : groupé avec ✕ dans un même conteneur
     flex — la marge négative de .users-modal-head button ci-dessus (pensée
     pour un bouton seul, agrandit sa zone cliquable vers l'extérieur du
     bandeau) ferait se chevaucher 2 boutons adjacents ; neutralisée
     horizontalement ici seulement, jamais verticalement (hauteur de clic
     du bandeau inchangée). */
  .users-modal-head-actions{ display:flex; align-items:center; }
  .users-modal-head-actions button{ margin-left:0; margin-right:0; }
  /* Info-bulle "Fermer" (2026-08-31, inventaire complet — mécanisme natif
     jusqu'ici, comme .comment-editor-head button ci-dessous, même règle) :
     sous le bouton, centrée dessus (fenêtre modale, jamais collée à un
     bord d'écran par elle-même). */
  .users-modal-head button[data-tooltip]::after, .comment-editor-head button[data-tooltip]::after{ top: calc(100% + 10px); left: 50%; transform: translateX(-50%); }
  /* overflow-y:auto (2026-08-21) : maintenant que TOUTE modale est
     redimensionnable (voir initFloatingModal() dans app.js), un
     rétrécissement manuel en dessous de la hauteur naturelle du contenu ne
     doit jamais le tronquer (.users-modal garde overflow:hidden) — le corps
     défile désormais lui-même le cas échéant. Sans effet visuel quand le
     contenu tient déjà (immense majorité des cas jusqu'ici). */
  .users-modal-body{ flex: 1 1 auto; min-height:0; padding: 14px 20px 18px; display:flex; flex-direction:column; gap: 12px; overflow-y:auto; overscroll-behavior: none; }
  /* ---------- Modale "Aide et Support" (onglets Aide/FAQ/Résultats/
     Contact/À propos, 2026-08-31, "À propos" passé en onglet le
     2026-08-27) ---------- */
  .help-theme{ margin-bottom: 18px; padding-top: 18px; border-top: 1px solid color-mix(in srgb, var(--settings-bg), white 12%); }
  .help-theme:last-child{ margin-bottom: 0; }
  /* Séparateur ENTRE chaque groupe/thème, jamais avant le premier
     (2026-09-04, demande explicite — Aide/Aide et Aide/Spécifications). */
  .help-theme:first-child{ padding-top: 0; border-top: none; }
  /* Réduite à son seul rôle réel (2026-09-02) : la typographie/couleur
     viennent maintenant de .settings-admin-label (toujours posée en
     COMPLÉMENT dans le markup, jamais seule) — un seul style de "libellé
     de groupe" dans toute l'app, Aide comprise (qui devient au passage
     sensible au thème actif, via --settings-label-color, au lieu de
     --dust-gold figé). Seul l'espacement sous le libellé reste propre à
     ce contexte (liste d'articles, jamais de paramètres de couleur). */
  .help-theme-label{ margin-bottom: 8px; }
  .help-article{ margin-bottom: 14px; }
  .help-article:last-child{ margin-bottom: 0; }
  /* Italique retiré + --settings-default-text-color (2026-09-03, demande
     explicite — "textes de niveau 2, sans police italique") : ex.
     "Les 5 niveaux de navigation" (Aide/Aide, Aide/Spécifications). */
  /* Police "bizarre" signalée (2026-09-03, demande explicite) : 'Fraunces'
     (serif éditorial, réservée à l'identité visuelle du site public — h1,
     légendes photo) détonnait dans l'écran de paramétrage, qui n'utilise
     QUE 'IBM Plex Mono' partout ailleurs (.settings-label/.settings-desc/
     etc.) — remplacée par cette même police, cohérente avec le reste de
     cet écran. Taille légèrement augmentée (16px -> 17px) au passage,
     comme suggéré. font-weight normal (voir demande "retirer le gras
     partout"). */
  /* Taille alignée sur .help-faq-question (2026-09-03, retour "trop
     grande" après le passage 16px -> 17px du tour précédent) : 17px ->
     13.5px, EXACTEMENT la même taille que les questions FAQ, comme
     demandé explicitement. */
  /* Encore réduite (2026-09-04, retour "toujours trop grand" — alignée sur
     .settings-label, la taille de texte courante de l'écran) : 13.5px ->
     12.5px, Aide/Aide et Aide/Spécifications comme Aide/FAQ. */
  .help-article-title{ font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size: var(--fs-md); color: var(--settings-default-text-color); margin: 0 0 6px; }
  .help-article .modal-note{ margin-bottom: 6px; }
  .help-article .modal-note:last-child{ margin-bottom: 0; }
  .help-faq-item{ margin-bottom: 14px; padding-bottom: 14px; border-bottom: 1px solid color-mix(in srgb, var(--bone) 10%, transparent); }
  .help-faq-item:last-child{ margin-bottom: 0; padding-bottom: 0; border-bottom: none; }
  /* --settings-default-text-color plutôt que --dust-gold en dur (2026-09-03,
     demande explicite — "Aide/FAQ : TOUS les textes", question comprise). */
  .help-faq-question{ font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size: var(--fs-md); color: var(--settings-default-text-color); margin-bottom: 6px; }
  .help-faq-answer{ margin: 0; }
  /* Barre de recherche (2026-08-31, refonte) : déplacée hors des onglets,
     toujours visible entre l'en-tête de la modale et les onglets eux-mêmes
     — filtre désormais "Résultats" quel que soit l'onglet affiché au
     moment de la saisie, plutôt que de vivre DANS l'onglet Aide. */
  .help-search-bar{ padding: 10px 20px; border-bottom: 1px solid color-mix(in srgb, var(--bone) 12%, transparent); }
  .help-search-bar .users-search-solo{ margin-bottom: 0; }
  /* Surlignage des termes recherchés (2026-08-31, demande explicite) —
     teinte dust-gold déjà utilisée pour les repères/accents du site,
     texte en --dusk pour un contraste net sur ce fond clair. */
  #helpResultsPanel mark{ background: var(--dust-gold); color: var(--dusk); border-radius: 2px; padding: 0 1px; }
  .users-modal-msg{ flex:0 0 auto; display:none; font-family:var(--text-font-family); font-size: var(--fs-sm); background: color-mix(in srgb, var(--bone) 8%, transparent); border-radius: 6px; padding: 8px 12px; position: relative; }
  .users-modal-msg.visible{ display:block; }
  /* Fermeture manuelle (2026-09-12, remplace la disparition automatique
     après 4s — voir showSettingsMsg() dans app.js) : bouton injecté en JS,
     couleur héritée du texte du message (orange/vert selon le niveau). */
  .users-modal-msg.dismissible{ padding-right: 34px; }
  .settings-msg-close{ position:absolute; top:4px; right:6px; background:none; border:none; color:inherit; font-size:var(--fs-lg); line-height:1; cursor:pointer; padding:4px; opacity:0.75; }
  .settings-msg-close:hover{ opacity:1; }
  /* Écran Logs (2026-09-12) : badge de sévérité, 4 niveaux distincts. */
  .logs-level-badge{ display:inline-block; padding:2px 8px; border-radius:10px; font-size:var(--fs-xs); font-weight:600; text-transform:uppercase; letter-spacing:0.03em; }
  .logs-level-ok{ background:var(--acacia); color:var(--bone); }
  .logs-level-warning{ background:var(--c-orange-moyen-3); color:var(--c-orange-sombre); }
  .logs-level-error{ background:var(--c-rouge-moyen); color:var(--white); }
  .logs-level-fatal{ background:var(--c-rouge-sombre); color:var(--white); }
  /* Message long/multi-ligne (2026-09-14, demande explicite ; bug corrigé le
     2026-09-15 — "la limite de hauteur n'est pas respectée du tout, la zone
     déborde sur les autres lignes") : cause réelle — overflow/max-height posés
     directement sur le <td> (.logs-message-cell) ne clippent pas de façon
     fiable sur une cellule de tableau (comportement de rendu des tables,
     Safari notamment) : le contenu déborde visuellement PAR-DESSUS les
     lignes suivantes au lieu d'être plafonné/défilé. Corrigé en déplaçant
     tronquage/plafond/défilement sur un <div> INTERMÉDIAIRE
     (.logs-message-inner, un vrai bloc, jamais une cellule) — le <td>
     lui-même n'a plus qu'à envelopper ce bloc normalement, donc la ligne du
     tableau grandit juste ce qu'il faut pour la hauteur PLAFONNÉE du bloc,
     jamais plus : les lignes suivantes sont repoussées normalement, jamais
     recouvertes. */
  /* Bouton plier/déplier (2026-09-15, demande explicite — remplace
     l'infobulle initiale) : position:absolute (PAS display:flex sur le
     <td> — changerait son type d'affichage et casserait la résolution de
     largeur de colonne de table-layout:fixed) ancré au coin supérieur droit
     de la cellule, qui reste un vrai <td> normal. position:relative sur
     .logs-message-cell (déjà le patron du [data-tooltip]::after générique
     ailleurs dans ce fichier) sert de repère de positionnement. Marge
     droite sur .logs-message-inner : réserve la place du bouton pour que le
     texte tronqué ne passe jamais dessous.
     Bug corrigé (2026-09-15, "le texte déplié recouvre à nouveau les autres
     lignes" — régression) : `.users-modal-table td` pose overflow:hidden en
     base (générique, toutes colonnes) — jamais neutralisé ici jusqu'à
     présent. Un <td> qui clippe LUI-MÊME, combiné à son enfant
     .logs-message-inner qui gère SA PROPRE zone de défilement
     (overflow-y:auto), crée un double contexte de clipping/défilement
     imbriqué sur une cellule de tableau — précisément la combinaison que le
     rendu des tables gère mal (Safari notamment), d'où le débordement.
     overflow:visible explicite ici : le <td> ne clippe plus RIEN lui-même,
     toute la responsabilité de plafonnement/défilement appartient
     UNIQUEMENT à .logs-message-inner — la cellule se contente d'envelopper
     normalement sa hauteur réelle (plafonnée), comme n'importe quel autre
     contenu de tableau. */
  .logs-message-cell{ position:relative; padding:0; overflow:visible; }
  .logs-message-inner{ padding:3px 30px 3px 8px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  /* Dépli EN PLACE, dans sa propre colonne (2026-09-23, retour en arrière
     explicite après l'essai "ligne pleine largeur" du 2026-09-19, non
     souhaité) : au-delà de 15 lignes (relevé de 10 le même jour), barre de
     défilement verticale propre à ce bloc — line-height posé explicitement
     ici (1.4) pour que le calc() en em corresponde réellement à 15 lignes
     visuelles. Un bloc normal qui grandit dans le flux de la cellule pousse
     naturellement les lignes suivantes du tableau vers le bas — pas besoin
     d'une <tr> séparée pour ça. */
  .logs-message-inner.expanded{ white-space:pre-wrap; line-height:1.4; max-height: calc(1.4em * 15); overflow-y:auto; overflow-x:hidden; }

  /* Intégration de voyage (2026-09-15, Phase A) : dossier choisi, journal de
     progression et texte d'indication sous le <select> de destination —
     même famille que .modal-note (texte secondaire discret,
     --settings-default-text-color plutôt que --bone en dur, voir le
     commentaire sur .modal-note juste au-dessus). */
  .integration-dir-label{ font-family:var(--text-font-family); font-size:var(--fs-md); color:var(--settings-default-text-color); opacity:0.75; }
  /* Hauteur doublée (2026-10-02, demande de Thierry) : 220 -> 440 px. */
  .integration-log{ margin-top:10px; max-height:440px; overflow-y:auto; font-family:var(--text-font-family); font-size:var(--fs-sm); line-height:1.5; color:var(--settings-default-text-color); background:color-mix(in srgb, var(--bone) 6%, transparent); border:1px solid color-mix(in srgb, var(--bone) 12%, transparent); border-radius:6px; padding:8px 10px; white-space:pre-wrap; }
  /* Bug corrigé (2026-09-15, "liste non défilable / bouton Générer
     invisible") : #integrationScanResults est un simple <div> ENTRE
     .settings-tabpanel (flex column) et .users-table-scroll (qui attend
     flex:1 1 auto + overflow:auto sur son PROPRE parent flex pour se
     limiter et défiler en interne — le patron déjà utilisé par toutes les
     autres grilles, ex. Voyages, où .users-table-scroll est un enfant
     DIRECT de .settings-tabpanel). Avec un <div> intermédiaire sans display
     flex, .users-table-scroll grandissait pour contenir TOUTES les lignes
     sans jamais défiler, poussant tout ce qui suit (barre "Générer", log)
     hors de la zone visible du panneau — d'où l'impression d'un bouton
     "Générer" absent alors qu'il existait bien, juste inatteignable.
     display:flex + flex:1 1 auto + min-height:0 ici rend ce <div>
     transparent pour la mise en page, exactement comme s'il n'existait
     pas. */
  /* Bug corrigé (2026-10-02, "textes superposés à la fin du traitement,
     activité 20/20", reproduit au banc bench_integration.html) : le panneau
     avait la hauteur FIXE de la fenêtre et ses blocs se comprimaient pour y
     tenir. Dès que la section "Destinations des activités" se remplissait
     (dernière étape du traitement), ce conteneur était écrasé sous la
     hauteur minimale de sa zone défilante (140 px) : le tableau débordait
     par-dessus la barre "Générer", le journal et le message de fin. Le
     panneau prend désormais sa hauteur naturelle (la fenêtre défile, voir
     .settings-body) et le tableau défile À L'INTÉRIEUR d'une zone bornée,
     la barre "Générer" restant juste dessous — ce qui couvre aussi le bug du
     2026-09-15 décrit ci-dessus. */
  .settings-tabpanel[data-tabpanel="integration"].active{ height:auto; }
  #integrationScanResults{ display:flex; flex-direction:column; flex:none; }
  #integrationScanResults .users-table-scroll{ flex:none; max-height:50vh; }

  /* Fiche voyage (2026-09-15) : même patron flex/overflow que
     #integrationScanResults ci-dessus (voir son commentaire pour le
     pourquoi) — plusieurs sections empilées, pas un seul grand tableau,
     donc le défilement se fait sur ce conteneur entier plutôt que sur une
     .users-table-scroll unique.
     Un SEUL ascenseur global, pas un par section (2026-09-16, demande
     explicite après un premier essai qui laissait le tableau Activités et
     le détail Photos défiler chacun indépendamment À L'INTÉRIEUR de ce
     conteneur déjà défilant — trois ascenseurs imbriqués). .users-
     table-scroll (flex:1 1 auto + overflow:auto, patron partagé par
     TOUTES les grilles à un seul tableau) est neutralisée ICI : hauteur
     naturelle, jamais bornée, pour que SEUL .trip-summary-content défile. */
  /* Résultats de recherche cliquables (2026-09-16) : liste déroulante
     positionnée sous le champ, distincte du <select> (qui reste la liste
     complète non filtrée) — voir renderTripSummarySearchResults(). */
  .trip-summary-search-wrap{ position:relative; }
  /* Largeur fixe : elle suivait le plus long voyage proposé et changeait avec
     le filtre global (inventaire « rien ne bouge », 12/10). */
  #tripSummaryVoyageSelect{ width:360px; max-width:100%; }
  /* Combo voyage générique (2026-09-23, Associations/Activités... — voir
     wireVoyageCombo() dans app.js) : même contexte de positionnement que
     .trip-summary-search-wrap ci-dessus, mêmes classes .trip-summary-search-*
     réutilisées telles quelles pour la liste de suggestions (zéro
     duplication de style). */
  .voyage-combo-wrap{ position:relative; }
  .trip-summary-search-results{ display:none; position:absolute; top:100%; left:0; right:0; z-index:5; margin-top:2px; max-height:240px; overflow-y:auto; background:var(--settings-bg); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius:6px; box-shadow:0 6px 16px color-mix(in srgb, var(--black-pure) 35%, transparent); }
  .trip-summary-search-result{ display:block; width:100%; text-align:left; background:none; border:none; padding:8px 10px; color:var(--settings-default-text-color); font-family:var(--text-font-family); font-size:var(--fs-sm); cursor:pointer; }
  .trip-summary-search-result:hover, .trip-summary-search-result.is-highlighted{ background:color-mix(in srgb, var(--bone) 8%, transparent); }
  .trip-summary-search-empty{ margin:0; padding:8px 10px; font-family:var(--text-font-family); font-size:var(--fs-sm); color:var(--settings-default-text-color); opacity:0.6; }
  /* Combo Destination en 2 blocs (2026-09-26, demande explicite — voir
     wireComboInput(showAllSection)) : délimiteur visuel entre "correspond
     à la saisie" (au-dessus, style inchangé) et "toutes les destinations"
     (en dessous). */
  .combo-section-label{ margin:2px 0 0; padding:8px 10px 4px; font-family:var(--text-font-family); font-size:var(--fs-xs); letter-spacing:0.06em; text-transform:uppercase; color: var(--dust-gold); border-top:1px solid color-mix(in srgb, var(--bone) 14%, transparent); }
  .trip-summary-content{ display:flex; flex-direction:column; flex:1 1 auto; min-height:0; overflow-y:auto; gap:20px; }
  .trip-summary-content .users-table-scroll{ flex:none; min-height:0; overflow:visible; }
  .trip-summary-section{ border-top:1px solid color-mix(in srgb, var(--bone) 12%, transparent); padding-top:14px; }
  .trip-summary-section:first-child{ border-top:none; padding-top:0; }
  .trip-summary-section h4{ font-family:var(--text-font-family); font-size:var(--fs-sm); text-transform:uppercase; letter-spacing:0.05em; color:var(--settings-label-color); margin:0 0 10px; }
  .trip-summary-fields{ display:flex; flex-wrap:wrap; gap:8px 24px; font-family:var(--text-font-family); font-size:var(--fs-md); color:var(--settings-default-text-color); }
  .trip-summary-fields span.label{ opacity:0.6; margin-right:4px; }
  .trip-summary-dest-list{ display:flex; flex-wrap:wrap; gap:8px; }
  .trip-summary-dest-chip{ font-family:var(--text-font-family); font-size:var(--fs-sm); padding:3px 10px; border-radius:12px; background:color-mix(in srgb, var(--bone) 8%, transparent); border:1px solid color-mix(in srgb, var(--bone) 20%, transparent); color:var(--settings-default-text-color); }
  .trip-summary-integration-live{ font-family:var(--text-font-family); font-size:var(--fs-sm); color:var(--ember); }
  /* Liste détaillée des photos (2026-09-15, repliée par défaut) : hauteur
     naturelle (2026-09-16, corrigé — un max-height+overflow ici créait un
     3e ascenseur indépendant, voir le commentaire de .trip-summary-content
     ci-dessus) — s'ajoute simplement à la hauteur totale de la page,
     défilée UNIQUEMENT par .trip-summary-content. */
  .trip-summary-photos-detail{ margin-top:10px; }
  .integration-dest-hint{ font-family:var(--text-font-family); font-size:var(--fs-sm); color:var(--settings-default-text-color); opacity:0.7; margin-bottom:4px; }
  /* Activité en échec (2026-09-15, résilience scan) : ligne repérable
     immédiatement dans le tableau, même famille de rouge que
     .logs-level-error. */
  tr.integration-row-error td{ background:color-mix(in srgb, var(--c-rouge-moyen) 12%, transparent); }
  /* Affiché uniquement si le message déborde réellement (voir
     renderLogsTable() dans app.js, mesuré après rendu) — jamais sur un
     message qui tient déjà sur une ligne. Ancré en haut à droite (reste en
     place même une fois la cellule agrandie/défilante, jamais emporté par
     le scroll). Agrandi (2026-09-15, demande explicite — "flèches trop
     petites") : 18px, plus grande zone de clic que le gabarit
     .sessions-group-toggle dont il s'inspirait initialement. */
  .logs-message-toggle{ position:absolute; top:0; right:0; background:none; border:none; color: var(--settings-default-text-color); opacity:0.7; cursor:pointer; font-size:var(--fs-3xl); line-height:1; padding:3px 6px; }
  .logs-message-toggle:hover{ opacity:1; }
  .logs-occurrences-badge{ display:inline-block; padding:1px 6px; margin-right:4px; border-radius:8px; background: color-mix(in srgb, var(--bone) 14%, transparent); font-size:var(--fs-xs); font-weight:600; }
  /* Générique (2026-09-13, renommé depuis .logs-retention-label/-input —
     réutilisé par le nouveau réglage "délai de disparition des messages",
     Interface > Affichage) : petit champ numérique admin, même gabarit
     partout. */
  .settings-number-label{ font-size:var(--fs-sm); color: var(--settings-default-text-color); }
  .settings-number-input{ width:60px; background: color-mix(in srgb, var(--bone) 8%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius: 6px; padding: 6px 8px; color: var(--settings-default-text-color); font-family:var(--text-font-family); font-size: var(--fs-sm); }
  /* Ligne de réglage avec un contrôle plus haut qu'une ligne de texte
     (2026-09-26, bandeau ticker — <textarea> multi-lignes) : .settings-row
     centre verticalement par défaut (align-items:center), ce qui écraserait
     visuellement le libellé/la description contre le haut d'un grand
     <textarea> — variante alignée en haut, réutilisable par tout futur
     réglage du même genre. */
  .settings-row-align-start{ align-items:flex-start; }
  .settings-ticker-textarea{ width:560px; height:auto; resize:vertical; font-family:var(--text-font-family); line-height:1.5; }
  /* Traitement alerte (2026-08-30, demande explicite) : réservé aux échecs
     (voir showUsersModalMsg(msg, isError) dans app.js — panneau Accès
     utilisateurs uniquement, les autres modales partageant cette même
     classe .users-modal-msg pour des confirmations neutres n'en ont pas
     besoin). Fond clair rouge-brun (pas un rouge froid, cohérent avec la
     palette safari du site) + texte plus foncé de la même teinte — un vrai
     encart d'alerte plutôt qu'un texte simple, même sur ce fond de modale
     sombre. */
  /* Teinte revue (2026-08-31, demande explicite) : la précédente lisait
     comme un rose plutôt qu'une alerte — remplacée par un jaune-orange vif
     (safran), délibérément distinct de --ember (terracotta, déjà utilisé
     pour les boutons/liens) pour ne jamais se confondre avec eux. Police
     un cran plus grande que le reste de .users-modal-msg (12px). */
  .users-modal-msg.error{ background: var(--c-orange-moyen-3); border: 1px solid var(--c-orange-moyen-5); color: var(--c-orange-sombre); font-size: var(--fs-lg); font-weight: normal; }
  .users-modal-msg.error::before{ content: "⚠ "; }
  /* Succès (2026-08-31, généralisé depuis showUsersModalMsg() — voir
     showSettingsMsg() dans app.js) : --acacia, déjà dans la palette du
     site, plutôt qu'un vert générique inventé pour l'occasion. */
  .users-modal-msg.success{ background: var(--acacia); border: 1px solid var(--c-vert-sombre); color: var(--bone); font-size: var(--fs-lg); font-weight: normal; }
  .users-modal-msg.success::before{ content: "✓ "; }
  .users-toolbar-filters{ flex:0 0 auto; display:flex; gap: 10px; flex-wrap:wrap; }
  .users-toolbar-filters select, .users-toolbar-filters input{ background: color-mix(in srgb, var(--bone) 8%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius: 6px; padding: 7px 10px; color: var(--settings-default-text-color); font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size: var(--fs-sm); }
  .users-toolbar-filters input{ flex:1; min-width: 160px; }
  /* Champ de recherche seul (2026-08-22, panneau Sessions connectées) : même
     style que .users-toolbar-filters input, mais sans la barre de filtres
     (dropdowns) qui n'a pas de sens ici — une session n'a ni statut ni
     catégorie, seulement les champs libres déjà listés dans le placeholder. */
  .users-search-solo{ display:block; width:100%; box-sizing:border-box; margin-bottom: 10px; background: color-mix(in srgb, var(--bone) 8%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius: 6px; padding: 7px 10px; color: var(--settings-default-text-color); font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size: var(--fs-sm); }
  /* Sessions connectées (2026-09-04, demande explicite — recherche / Tout
     sélectionner / Révoquer sur une seule ligne, un seul séparateur en bas
     de barre comme Utilisateurs/Voyages, plus le double séparateur d'avant) :
     tout tient dans le même .users-action-bar, le champ de recherche garde
     sa largeur naturelle (flex:1), les autres contrôles leur taille propre. */
  .sessions-action-bar .users-search-solo{ flex:1; margin-bottom: 0; }
  .sessions-select-all-label{ display:flex; align-items:center; gap: 6px; font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size: var(--fs-sm); color: var(--settings-default-text-color); cursor:pointer; flex:0 0 auto; white-space:nowrap; }
  /* Séparateur retiré (2026-09-04, demande explicite, répétée sur
     Voyages/Utilisateurs/Sessions connectées — "un séparateur apparaît
     juste avant le premier élément de la liste") : cette bordure fermait
     la barre d'action juste au-dessus de la liste/grille, perçue comme un
     séparateur superflu avant le premier élément. Le gap de
     .settings-tabpanel (16px) reste suffisant pour distinguer visuellement
     la barre d'action de la liste, sans trait. */
  .users-action-bar{ flex:0 0 auto; display:flex; gap: 8px; flex-wrap:wrap; align-items:center; }
  /* Info-bulle du "+" (2026-08-31, inventaire complet — mécanisme natif
     jusqu'ici) : centrée, cette barre n'étant jamais collée à un bord
     d'écran (fenêtre modale). */
  .users-action-bar [data-tooltip]{ position:relative; }
  .users-action-bar [data-tooltip]::after{ top: calc(100% + 10px); left: 50%; transform: translateX(-50%); }
  /* "+" cadré à gauche (2026-09-16, demande explicite, toutes les grilles) :
     TOUJOURS le premier bouton de la barre d'action, donc collé au bord
     gauche de la fenêtre modale — la règle centrée ci-dessus tronquait sa
     moitié gauche contre ce bord. Priorité par spécificité (3 classes)
     plutôt que !important. */
  .users-action-bar .users-add-row-btn[data-tooltip]::after{ left: 0; transform: none; }
  /* --action-button-text-color plutôt que --bone en dur (2026-09-03,
     nouveau paramètre "Libellés des boutons d'action") : + / Importer VCF/
     XLSX / Activer / Désactiver / Supprimer. */
  .users-action-bar button, .users-action-bar > label{ font-family:var(--text-font-family); font-size: var(--fs-sm); letter-spacing: 0.02em; color: var(--action-button-text-color); background: color-mix(in srgb, var(--bone) 8%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 25%, transparent); padding: 8px 12px; border-radius: 16px; cursor:pointer; white-space:nowrap; }
  .users-action-bar button:hover, .users-action-bar > label:hover{ background: color-mix(in srgb, var(--bone) 15%, transparent); }
  .users-action-bar button:disabled{ opacity: 0.35; cursor:not-allowed; }
  .users-action-bar button:disabled:hover{ background: color-mix(in srgb, var(--bone) 8%, transparent); }
  /* "Supprimer" strictement identique aux autres (2026-09-03, demande
     explicite reformulée) : classe "danger" retirée du markup ET de son
     style ici — plus aucune distinction visuelle avec + / Importer /
     Activer / Désactiver, seul le libellé diffère. */
  /* Bouton "+" unique (2026-08-30, remplace l'ancien formulaire séparé) :
     cercle compact plutôt que la pilule texte des autres boutons de cette
     barre — insère une ligne vide éditable en tête de tableau, voir
     insertDraftRow() dans app.js. */
  /* Revenu au gabarit standard .users-action-bar button (2026-09-08,
     demande explicite — le cercle dédié introduit le 2026-08-30/corrigé le
     2026-09-04 ne doit plus se distinguer des autres boutons de la barre,
     seul le glyphe "+" doit rester agrandi). Sélecteur à deux classes
     (0-2-0) conservé uniquement pour l'emporter sans ambiguïté sur
     .users-action-bar button (0-1-1, classe + type) — sans ça le font-size
     11.5px standard écraserait silencieusement le 24px ci-dessous. */
  .users-action-bar .users-add-row-btn{ font-size: var(--fs-4xl); line-height: 1; }
  /* Ligne de création (2026-08-30) : légèrement teintée tant qu'elle n'est
     pas confirmée en base (id absent), pour la distinguer visuellement des
     lignes réelles — retirée dès la promotion (voir promoteDraftRow()). */
  .users-draft-row{ background: color-mix(in srgb, var(--bone) 5%, transparent); }
  /* Bug corrigé (2026-09-24, "combo Voyage disparu") : .users-modal-table
     td pose overflow:hidden (nécessaire à l'ellipse de troncature ailleurs,
     voir plus bas) — ça rognait aussi silencieusement le menu de suggestions
     de wireVoyageCombo() (.trip-summary-search-results, position:absolute,
     top:100%) dès qu'il vivait dans une cellule de ligne brouillon : le menu
     était bien généré et positionné, juste invisible. Aucune perte de
     troncature ici : une ligne brouillon contient des champs de saisie, pas
     du texte statique long à tronquer. */
  .users-draft-row td{ overflow: visible; }
  /* Annuler une ligne brouillon (2026-09-08, demande explicite — jusqu'ici
     aucun moyen de sortir d'une ligne "+" en cours sans recharger toute la
     page) : dans la case à cocher de tête, vide pour une ligne pas encore
     créée — remplacée par ce bouton, sur les 5 grilles du site (voir
     projet/standards/GRID_STANDARD.md). */
  /* Couleur corrigée (2026-09-09, signalé "quasi invisible" sur Listes
     configurables) : --dusk-soft (#362d1e, brun foncé pensé pour du texte
     sur fond CLAIR — site public) contre --settings-bg (#1a150d, quasi
     noir) donnait un contraste proche de zéro, sur les 5 grilles à
     l'identique (même classe partagée) — pas une particularité de cet
     écran, juste le premier où l'écart a été repéré avec certitude.
     --settings-default-text-color est la couleur de texte par défaut déjà
     utilisée partout ailleurs dans cette fenêtre, garantie lisible sur
     --settings-bg. */
  .users-draft-cancel-btn{ background:none; border:none; color: var(--settings-default-text-color); opacity:0.6; cursor:pointer; font-size:var(--fs-xl); padding:2px; display:flex; align-items:center; justify-content:center; width:100%; }
  .users-draft-cancel-btn:hover{ opacity:1; color: var(--ember); }
  .users-draft-row input:disabled{ opacity: 0.3; }
  .users-selection-summary{ margin-left:auto; font-family:var(--text-font-family); font-size: var(--fs-xs); opacity:0.65; white-space:nowrap; }
  .users-btn-muted{ background: color-mix(in srgb, var(--bone) 4%, transparent) !important; }
  .users-import-preview{ flex:0 0 auto; }
  .users-field-row{ display:flex; align-items:center; gap: 10px; margin-bottom: 12px; }
  .users-field-row label{ font-family:var(--text-font-family); font-size: var(--fs-xs); white-space:nowrap; }
  .users-field-row input{ flex:1; background: color-mix(in srgb, var(--bone) 8%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius: 6px; padding: 7px 10px; color: var(--bone); font-family: inherit; font-size: var(--fs-md); }
  .users-import-list{ display:flex; flex-direction:column; gap: 6px; max-height: 200px; overflow-y:auto; overscroll-behavior: none; margin-bottom: 10px; }
  /* Sessions connectées : s'étend jusqu'au bas de la fenêtre, comme les
     grilles Utilisateurs/Voyages (2026-09-03, demande explicite) — SCOPÉ
     à ce seul contexte (pas .users-import-list en général, qui reste une
     prévisualisation d'import volontairement bornée à 200px ailleurs).
     #sessionsAdminSection doit lui-même flex-fill le tabpanel (voir
     .settings-tabpanel.active, height:100%) pour que son enfant en hérite. */
  #sessionsAdminSection{ display:flex; flex-direction:column; flex: 1 1 auto; min-height: 0; }
  /* Plus aéré (2026-09-04, demande explicite — "pas assez aéré") : gap
     6px -> 14px et padding vertical par ligne, SCOPÉ à Sessions connectées
     (.users-import-list reste compact ailleurs — prévisualisations
     d'import Utilisateurs/Voyages, liste Passkeys). */
  #sessionsAdminSection .users-import-list{ flex: 1 1 auto; min-height: 0; max-height: none; gap: 14px; }
  #sessionsAdminSection .users-import-item{ padding: 4px 0; }
  .users-import-item{ display:flex; align-items:center; gap: 8px; font-family:var(--text-font-family); font-size: var(--fs-sm); }
  .users-import-item.disabled{ opacity: 0.45; }
  .users-import-email{ opacity: 0.6; }
  .users-import-noemail{ color: var(--c-orange-moyen-4); }
  /* Aperçu de l'import Excel des voyages : création / mise à jour (2026-10-09). */
  .users-import-summary{ font-size: var(--fs-sm); margin-bottom: 6px; }
  .users-import-action{ opacity: 0.75; }
  .users-import-action-create{ color: var(--c-vert-moyen); }
  .users-import-action-update{ color: var(--c-orange-moyen-6); }
  /* Association Voyage<->Destination (2026-09-07, étape 6) : boutons
     de réordonnancement ▲▼, gabarit minimal cohérent avec le reste
     (donnée/action, jamais un cadre "bouton") — voir moveAssociationRow(). */
  .associations-order-cell{ display:flex; gap:4px; }
  .associations-move-btn{ background:none; border:none; color: var(--settings-default-text-color); opacity:0.75; cursor:pointer; font-size:var(--fs-xs); padding:2px 4px; }
  .associations-move-btn:hover{ opacity:1; }
  .associations-move-btn:disabled{ opacity:0.25; cursor:default; }
  .associations-remove-btn{ background:none; border:none; cursor:pointer; font-size:var(--fs-lg); padding:0 4px; line-height:1; }
  .sessions-admin-title{ font-family:var(--text-font-family); font-size:var(--fs-md); color:var(--bone); opacity:0.8; margin:16px 0 8px; }
  /* Fond retiré (2026-09-03, demande explicite — "pas esthétique") : le
     marqueur ⚠️ déjà inséré dans le texte (renderSessionsAdminList(), voir
     app.js) reste le seul signal d'un pays inhabituel, plus de teinte de
     fond ni de bordure sur toute la ligne. */
  .sessions-unusual{}
  /* Regroupement par utilisateur/appareil (2026-09-04, demande explicite) :
     bouton de dépli minimal, cohérent avec le reste (pas de bouton
     "bouton" — donnée/action, jamais un cadre). */
  .sessions-group-toggle{ background:none; border:none; color: var(--settings-default-text-color); opacity:0.7; cursor:pointer; font-size:var(--fs-xs); padding:0 4px; flex:0 0 auto; }
  .sessions-group-toggle:hover{ opacity:1; }
  .sessions-group-header{ font-weight: normal; }

  /* Scrollbar fine et semi-transparente (2026-09-26, généralisée depuis
     .theme-editor-rows-scroll — demande explicite : "sur toutes les grilles
     du site, pas seulement Thèmes") : même patron exact (scrollbar-width/
     -color pour Firefox, pseudo-éléments ::-webkit-scrollbar pour Chrome/
     Safari/Edge) — remplace la scrollbar native grise épaisse par défaut. */
  .users-table-scroll{ flex: 1 1 auto; min-height: 140px; overflow: auto; overscroll-behavior: none; border-top: 1px solid color-mix(in srgb, var(--bone) 12%, transparent); scrollbar-width: thin; scrollbar-color: rgba(246,241,230,0.18) transparent; }
  .users-table-scroll::-webkit-scrollbar{ width:6px; height:6px; }
  .users-table-scroll::-webkit-scrollbar-track{ background:transparent; }
  .users-table-scroll::-webkit-scrollbar-thumb{ background: color-mix(in srgb, var(--bone) 18%, transparent); border-radius:3px; }
  /* --frozen-w1/--frozen-w2 : largeurs réelles des colonnes "sélection" et
     "Prénom" (2026-08-20), tenues à jour en JS (syncTableColumns(), appelée
     après l'ouverture réelle de la modale — voir openUsersModal(), pas dans
     ensureUsersModal() où le backdrop est encore display:none — et pendant
     un redimensionnement manuel de colonne ou de fenêtre) — valeurs par
     défaut ici cohérentes avec les width:32px/100px posés en ligne sur les
     <th>, au cas où le premier calcul JS tarderait. */
  /* width:auto (2026-08-20, remplace un simple width:100%) : avec
     table-layout:fixed, un width:100% strict COMPRIME les colonnes pour
     tenir dans le conteneur au lieu de déborder — aucun défilement
     horizontal possible, quelle que soit la somme des largeurs de colonnes
     déclarées (vérifié).
     min-width:100% RETIRÉ (2026-09-20, bug corrigé — demande explicite et
     répétée : "si la grille devient plus étroite que la fenêtre, espace
     vide à droite, c'est acceptable"). Cause racine du redimensionnement de
     colonnes qui semblait sans effet : min-width, en CSS, force TOUJOURS le
     rendu à au moins cette largeur — même quand table.style.width (posé en
     ligne par syncTableColumns(), plus spécifique en apparence) déclare une
     valeur plus petite, min-width l'emporte quand même (ce n'est pas une
     histoire de spécificité de sélecteur, min-width/max-width CONTRAIGNENT
     toujours la valeur finale de width, quelle que soit son origine).
     Concrètement : sur une grille dont la somme des largeurs de colonnes
     tient déjà dans le conteneur (Statistiques, Photos, Destination...),
     min-width:100% forçait la table à occuper 100% quand même — et
     table-layout:fixed, devant redistribuer cet espace excédentaire,
     l'étalait sur TOUTES les colonnes plutôt que de respecter leurs largeurs
     individuelles épinglées. D'où l'effet observé ("toutes les colonnes se
     répartissent"), qui persistait malgré l'épinglage par colonne
     (makeColumnResizable ci-dessous) : celui-ci ne peut rien face à une
     largeur de TABLE elle-même forcée plus grande que la somme de ses
     colonnes. Sans min-width:100%, une grille qui tient déjà laisse
     désormais un espace vide à droite (accepté explicitement) ; une grille
     qui déborde continue de déclencher le défilement horizontal de
     .users-table-scroll comme avant (width:auto laisse toujours la table
     s'élargir au-delà du conteneur). */
  table.users-modal-table{ width:auto; table-layout:fixed; border-collapse:collapse; font-family:var(--text-font-family); font-size: var(--fs-sm); --frozen-w1: 32px; --frozen-w2: 100px; --frozen-w3: 100px; }
  /* --settings-bg plutôt que rgb(26,21,13) en dur (2026-08-31, même
     correctif que .users-frozen-* ci-dessous — même valeur aujourd'hui). */
  /* Exception explicitement maintenue au retrait général du gras
     (2026-09-03, demande explicite — "en-têtes de colonnes... doivent
     rester en gras") : gras posé EXPLICITEMENT ici (700), plutôt que de
     continuer à dépendre du gras par défaut du navigateur sur <th> (déjà
     le cas jusqu'ici, mais implicite) — plus robuste et cohérent avec le
     reste de cet audit, où chaque poids de police est désormais déclaré
     sans ambiguïté. Taille augmentée d'un cran (10px -> 11px) au passage. */
  /* Contraste renforcé (2026-09-04, demande explicite — "en-têtes trop
     effacés") : opacity 0.85 -> 1, en plus de la taille/graisse déjà
     ajustées précédemment. */
  .users-modal-table thead th{ position:sticky; top:0; z-index:1; text-align:left; text-transform:uppercase; font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.05em; opacity: 1; padding: 8px; background: color-mix(in srgb, var(--settings-bg) 97%, transparent); border-bottom: 1px solid color-mix(in srgb, var(--bone) 18%, transparent); overflow:hidden; white-space:nowrap; }
  /* Padding vertical resserré 6px->3px (2026-08-21, lignes jugées trop
     hautes) — le vrai gain vient surtout du nowrap+ellipsis posé plus bas
     sur .users-inline-edit/.users-comment-preview : un commentaire ou un
     libellé de passkey un peu long passait à la ligne dans une colonne
     étroite (ex. "MacBook — 2026-08-01 10:00" sur 80px), gonflant la
     hauteur de la ligne bien plus que le padding lui-même. */
  /* Jamais de retour à la ligne (2026-09-16, demande explicite) : une
     cellule texte SANS <input> (donc sans le clip natif d'un champ de
     formulaire, voir .users-inline-edit ci-dessous) repassait en
     white-space:normal par défaut — un libellé un peu long y passait à la
     ligne, gonflant la hauteur de la ligne. nowrap+ellipsis générique ici
     couvre TOUTE cellule de TOUTE grille, y compris les futures. */
  .users-modal-table td{ padding: 3px 8px; border-bottom: 1px solid color-mix(in srgb, var(--bone) 10%, transparent); overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }
  .users-modal-table td.users-check-cell{ text-align:center; }
  /* ID numériques / ID Externe centrés (2026-09-20, demande explicite) :
     Destination.id (numérique) et Voyage.external_id — jamais le slug
     texte de Voyage.id lui-même (pas un ID numérique). td (affichage) ET
     input (édition en ligne + ligne brouillon) : text-align centre aussi le
     texte d'un <input>. */
  .users-modal-table td.users-id-cell, .users-modal-table .users-id-cell{ text-align:center; }
  /* Interligne Voyages = Utilisateurs (2026-09-04, toujours signalé après
     un 1er correctif par padding seul, insuffisant) : un <select> natif
     impose un minimum de hauteur propre au navigateur, indépendant du
     padding réellement appliqué (contrairement à un simple <input>) — le
     seul moyen fiable de forcer une hauteur RÉELLEMENT identique entre
     .admin-select (Thème, Voyages) et .users-inline-edit (texte, les deux
     grilles) est une hauteur explicite sur les deux, en plus du padding
     déjà resserré ci-dessous. */
  /* Texte tronqué verticalement dans les combos de grille (2026-09-07,
     demande explicite — "Voyages" coupé en haut, sans élargir le combo ni
     augmenter l'interligne) : cette règle perdait jusqu'ici sur le padding
     face à la règle "combo autonome" ci-dessus (.users-modal.account-modal
     select.admin-select:not(...), spécificité 0,4,1 contre 0,2,1 ici) — le
     padding vertical réellement appliqué était donc 6px, pas 3px comme
     écrit ici. Avec height:24px fixe en box-sizing:border-box, 6px de
     chaque côté ne laisse que 10px de haut pour le texte (insuffisant pour
     12px de police) : d'où le rognage. Sélecteur étendu aux 2 classes
     ancestrales pour égaler cette spécificité (0,4,1) et l'emporter grâce à
     l'ordre du fichier (déclarée plus bas) — padding droit conservé large
     pour ne pas chevaucher la flèche du combo, largeur/interligne inchangés. */
  /* Fond retiré (2026-09-08, demande explicite — "pas de couleur de fond
     dans les combos au sein des grilles") : même sélecteur déjà gagnant
     sur le padding ci-dessus, étendu au fond — bordure conservée (signal
     "cliquable" toujours visible), uniquement le remplissage disparaît. */
  .users-modal.account-modal .users-modal-table td .admin-select{ padding: 3px 26px 3px 8px; height: 24px; box-sizing: border-box; background-color: transparent; }
  .users-modal-table .users-inline-edit{ height: 24px; box-sizing: border-box; }
  /* Colonnes figées (sélection+Prénom+Nom, "comme Excel", 2026-08-20) —
     jamais posées sur la table Statistiques, qui partage table.users-modal-
     table mais n'a pas ces classes. Fond opaque obligatoire (une cellule
     sticky laisserait sinon transparaître le contenu défilant derrière) ;
     .users-frozen-3 (dernière figée) porte le séparateur visuel. z-index 2
     pour les cellules de corps (au-dessus du contenu qui défile dessous),
     3 pour l'en-tête (au-dessus À LA FOIS des cellules figées du corps ET
     de l'en-tête normal, qui est déjà à 1). */
  /* Teinte légèrement plus claire (--dusk-soft) que le reste de la table
     annulée (2026-08-31, demande explicite — "il ne doit y avoir aucune
     différence") : ce choix datait du 2026-08-22 (signal visuel "ces
     colonnes ne défilent jamais"), mais --dusk-soft ne correspond à
     aucune variable de thème — bug rapporté une fois le fond de cette
     fenêtre devenu themable (--settings-bg) : les colonnes figées
     restaient toujours sur l'ancienne teinte fixe, désormais visiblement
     différente du reste dès qu'un thème change --settings-bg. Alignées
     sur --settings-bg (même color-mix que .users-modal) plutôt que de
     réintroduire une 2e teinte fixe à maintenir manuellement en phase. */
  /* Opaque (BE, 14/10) : à 97 %, le texte des colonnes qui défilent dessous transparaissait. */
  .users-frozen-1, .users-frozen-2, .users-frozen-3, .users-frozen-4{ position:sticky; z-index:2; background: var(--settings-bg); }
  /* Défense en profondeur (2026-09-25, écran Pays) : iso2 déjà normalisé en
     MAJUSCULES côté serveur (countries.php) — cette règle ne fait
     qu'assurer visuellement la forme canonique ISO même si une ligne
     importée fournissait un jour un code en minuscules, sans dépendre à
     nouveau uniquement de la donnée en base. */
  .country-iso2-cell{ text-transform: uppercase; text-align: center; }
  input.country-iso2-cell{ text-align: center; }
  /* ISO3 (2026-10-01) : même affichage en majuscules que ISO2. */
  input.country-iso3-cell{ text-transform: uppercase; }
  .users-modal-table thead .users-frozen-1,
  .users-modal-table thead .users-frozen-2,
  .users-modal-table thead .users-frozen-3,
  .users-modal-table thead .users-frozen-4{ z-index:3; background: color-mix(in srgb, var(--settings-bg) 97%, transparent); }
  /* Bug corrigé (2026-09-01) : .users-draft-row (ligne "nouvel
     utilisateur" en cours de saisie) pose une légère teinte
     (rgba(246,241,230,0.05)) sur le <tr>, mais les colonnes figées peignent
     leur PROPRE fond opaque à 97% par-dessus — masquant presque entièrement
     cette teinte (colonnes figées quasi inchangées pendant que le reste de
     la ligne se distingue nettement). Réappliquée explicitement sur les
     colonnes figées d'une ligne en brouillon, pour qu'elles restent
     identiques au reste de la ligne, comme partout ailleurs dans ce
     tableau. */
  .users-draft-row .users-frozen-1, .users-draft-row .users-frozen-2, .users-draft-row .users-frozen-3, .users-draft-row .users-frozen-4{ background: color-mix(in srgb, var(--settings-bg) 92%, color-mix(in srgb, var(--bone) 60%, transparent)); }
  .users-frozen-1{ left:0; }
  /* Info-bulle "Tout sélectionner" (2026-08-31, inventaire complet —
     mécanisme natif jusqu'ici) : colonne figée la plus à GAUCHE d'un
     tableau qui défile horizontalement — ancrée par la gauche (jamais
     centrée/par la droite, débordement garanti sinon). thead th (voir
     règle de sticky plus haut) sert déjà de contexte de positionnement. */
  thead .users-frozen-1[data-tooltip]::after{ top: calc(100% + 10px); left: 0; }
  .users-frozen-2{ left: var(--frozen-w1); }
  .users-frozen-3{ left: calc(var(--frozen-w1) + var(--frozen-w2)); }
  .users-frozen-4{ left: calc(var(--frozen-w1) + var(--frozen-w2) + var(--frozen-w3)); }
  /* Séparateur visuel de fin de gel (2026-09-07) : porté par une classe
     dédiée plutôt que codé en dur sur .users-frozen-4, car le nombre de
     colonnes figées varie selon la grille (4 pour Utilisateurs, seulement
     2 — case à cocher + Identifiant — pour Voyage/Destination). Posée
     sur la DERNIÈRE colonne figée de chaque grille dans le HTML. */
  .users-frozen-last{ box-shadow: 2px 0 4px color-mix(in srgb, var(--black-pure) 25%, transparent); }
  /* Validation explicite par ligne (2026-09-25, chantier majeur — remplace
     la sauvegarde automatique au premier champ modifié) : colonne figée à
     DROITE (symétrique de .users-frozen-1/2/3/4 à gauche), toujours
     ancrée même si la barre de défilement horizontale n'est pas allée
     jusqu'au bout. Une seule colonne fixe (jamais plusieurs niveaux
     comme à gauche), largeur constante quelle que soit la grille — pas
     de variable --frozen-w-right nécessaire.
     Amovible (2026-09-26, demande explicite — bug #4 : la colonne
     réservait 56px en permanence même sans aucune ligne modifiée,
     "occupant de la place pour rien" sur une grille déjà large) : réduite
     à largeur 0 tant qu'AUCUNE ligne de la table ne porte `.is-dirty` —
     voir `.has-dirty-row`, posée par syncGridDirtyColumn()/
     applyColumnWidths() côté app.js, jamais en CSS pur (une table ne peut
     pas "voir" l'état de ses lignes descendantes autrement qu'en JS).
  */
  /* overflow:visible !important (2026-09-26, bug remonté — ✕ affiché en
     "..." et injouable) : `.users-modal-table td` (règle générique, voir
     plus haut, "no text wrap") pose overflow:hidden/white-space:nowrap/
     text-overflow:ellipsis sur TOUTE cellule de grille, spécificité CSS
     ÉGALE à `.users-row-actions` (une classe chacune) — jamais neutralisée
     ici jusqu'ici. Un budget de largeur trop juste (2 boutons de 22px avec
     bordure 1px, comptée EN PLUS en box-sizing:content-box par défaut, dans
     une colonne de 56px) suffisait à dépasser de quelques pixels — assez
     pour que ce overflow:hidden hérité tronque le second bouton (✕),
     rendu par l'ellipse en "...". Corrigé aux deux bouts : box-sizing:
     border-box ci-dessous (la bordure ne s'ajoute plus aux 22px déclarés)
     ET overflow:visible ici (cette colonne n'a jamais eu besoin de
     tronquer quoi que ce soit — 2 icônes, jamais du texte variable). */
  .users-row-actions{ position:sticky; right:0; z-index:2; width:56px; min-width:56px; max-width:56px; text-align:center; background: color-mix(in srgb, var(--settings-bg) 97%, transparent); box-shadow: -2px 0 4px color-mix(in srgb, var(--black-pure) 25%, transparent); padding:2px 4px !important; overflow:visible !important; white-space:nowrap !important; }
  table.users-modal-table:not(.has-dirty-col) .users-row-actions{ width:0; min-width:0; max-width:0; padding:0 !important; box-shadow:none; overflow:hidden !important; }
  .users-draft-row .users-row-actions{ background: color-mix(in srgb, var(--settings-bg) 92%, color-mix(in srgb, var(--bone) 60%, transparent)); }
  .users-row-confirm-btn, .users-row-cancel-btn{
    display:none; width:22px; height:22px; box-sizing:border-box; border-radius:50%; border:1px solid color-mix(in srgb, var(--bone) 30%, transparent);
    background: color-mix(in srgb, var(--night) 95%, transparent); cursor:pointer; font-size:var(--fs-sm); line-height:1; padding:0;
    align-items:center; justify-content:center;
  }
  tr.is-dirty .users-row-confirm-btn, tr.is-dirty .users-row-cancel-btn{ display:inline-flex; }
  /* Verrou global par grille (2026-09-26, bugs #2/#3, voir wireRowActions()
     côté app.js) : pendant qu'UNE ligne valide/annule, les boutons de
     TOUTES les autres lignes de la même grille sont visuellement neutralisés
     — deuxième barrière (en plus du blocage JS) contre tout clic parasite
     pendant l'appel réseau en cours. Le bouton de la ligne en cours reste,
     lui, géré par son propre `disabled` (posé par wireRowActions()), pas
     par cette règle. */
  table.users-modal-table[data-validating="1"] .users-row-confirm-btn:not(:disabled),
  table.users-modal-table[data-validating="1"] .users-row-cancel-btn:not(:disabled){
    pointer-events:none; opacity:0.35;
  }
  .users-row-confirm-btn{ color:var(--c-vert-moyen-2); margin-right:4px; }
  .users-row-confirm-btn:hover{ background: color-mix(in srgb, var(--c-vert-moyen-2) 18%, transparent); }
  .users-row-cancel-btn{ color:var(--c-rouge-moyen-2); }
  .users-row-cancel-btn:hover{ background: color-mix(in srgb, var(--c-rouge-moyen-2) 18%, transparent); }
  /* Ligne modifiée (2026-09-25) : léger liseré pour repérer d'un coup
     d'œil quelles lignes ont des changements non validés, sans confusion
     possible avec le surlignage de sélection (couleur signature) ou de
     ligne active (Voyages, même couleur mais bien plus marquée). */
  tr.is-dirty{ box-shadow: inset 3px 0 0 var(--c-vert-moyen-2); }
  .users-email-cell{ display:flex; align-items:center; gap:4px; }
  .users-email-cell input{ flex:1 1 auto; min-width:0; }
  /* Info-bulle (2026-08-31, inventaire complet — mécanisme natif jusqu'ici) :
     ni bord gauche ni droit (colonne parmi d'autres dans le tableau,
     défilement horizontal indépendant) — centrée, sous le bouton. */
  .users-email-pending-btn{ position:relative; flex:0 0 auto; background:none; border:none; cursor:pointer; font-size:var(--fs-md); padding:0 2px; line-height:1; }
  .users-email-pending-btn[data-tooltip]::after{ top: calc(100% + 10px); left: 50%; transform: translateX(-50%); }
  .users-email-pending-box p{ margin:0 0 10px; font-size:var(--fs-md); line-height:1.4; }
  .users-email-pending-code{ width:110px; box-sizing:border-box; background:color-mix(in srgb, var(--bone) 8%, transparent); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius:6px; padding:8px 10px; color:var(--bone); font-family:inherit; font-size:var(--fs-md); margin-right:8px; }
  .users-th-label{ cursor:pointer; user-select:none; }
  /* Champ obligatoire (2026-09-08, demande explicite, généralisée à tout
     l'écran Compte — grilles ET formulaires type Mon Profil) : forme
     dessinée en CSS (clip-path), pas un glyphe Unicode "★" — corrige le
     retour "on ne perçoit qu'un point" (le caractère ★ rendu par la police
     technique IBM Plex Mono du reste de la fenêtre s'affiche minuscule/
     quasi invisible, peu importe la taille de police demandée ; une forme
     dessinée ne dépend d'aucune police). Couleur signature du thème,
     jamais une teinte fixe. `.field-required` : classe générique posée
     directement sur le libellé concerné (`.users-th-label` d'une grille OU
     `<label>` d'un formulaire) — un seul mécanisme pour toute la fenêtre
     Compte, pas un dérivé par écran. Espace resserré (3px, contre l'espace
     texte ' ' précédent jugé trop large) via margin, pas un caractère. */
  .field-required::after{
    content: '';
    display: inline-block;
    width: 0.85em;
    height: 0.85em;
    margin-left: 3px;
    vertical-align: middle;
    background: var(--ember);
    clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
  }
  /* Poignée de redimensionnement (2026-10-11, demande de Thierry : zone
     trop fine et fugace) : 10 px de prise au bord droit de l'en-tête, un
     trait discret visible dès le survol de la ligne d'en-têtes, accentué
     (couleur du thème) au survol de la poignée et pendant le glissé. */
  .users-col-resizer{ position:absolute; top:0; right:0; width:10px; height:100%; cursor:col-resize; user-select:none; z-index:3; }
  .users-col-resizer::after{ content:''; position:absolute; top:22%; bottom:22%; right:4px; width:2px; border-radius:1px; background:transparent; }
  thead:hover .users-col-resizer::after{ background:color-mix(in srgb, var(--bone) 30%, transparent); }
  .users-col-resizer:hover::after, .users-col-resizer.is-dragging::after{ top:8%; bottom:8%; background:var(--ember); }
  body.users-col-resizing, body.users-col-resizing *{ cursor:col-resize !important; user-select:none !important; }
  /* Étape D : glisser-déposer des colonnes (makeColumnsReorderable). */
  .users-col-draggable{ cursor: grab; }
  .users-col-draggable.users-col-dragging{ opacity: 0.4; }
  /* Curseur posé en ligne par JS sur body.style.cursor pendant le glissé
     (voir makeUsersModalResizable()) — dépend de la direction (ns/ew/nesw/
     nwse), pas fixe comme avant (une seule poignée bas-droit). Cette classe
     ne gère plus que le blocage de la sélection de texte. */
  body.users-modal-resizing, body.users-modal-resizing *{ user-select:none !important; }
  /* --settings-default-text-color plutôt que --bone en dur (2026-09-03,
     bug réel confirmé par test — les données de grille Utilisateurs/
     Voyages passent presque toutes par CET input, pas du texte simple
     hérité comme supposé à tort la fois précédente). */
  .users-inline-edit{ width:100%; box-sizing:border-box; background: transparent; border: 1px solid transparent; border-radius: 4px; padding: 3px 6px; color: var(--settings-default-text-color); font-family: inherit; font-size: var(--fs-sm); }
  /* Popup +/- retiré sur les champs numériques (2026-09-16, demande
     explicite — Thierry préfère la saisie directe au clavier), sur toutes
     les grilles. -moz-appearance:textfield pour Firefox, les 2 pseudo-
     éléments ci-dessous pour Chrome/Safari (WebKit/Blink). */
  .users-modal-table input[type="number"]{ -moz-appearance:textfield; }
  .users-modal-table input[type="number"]::-webkit-inner-spin-button,
  .users-modal-table input[type="number"]::-webkit-outer-spin-button{ -webkit-appearance:none; margin:0; }
  .users-inline-edit:hover, .users-inline-edit:focus{ background: color-mix(in srgb, var(--bone) 8%, transparent); border-color: color-mix(in srgb, var(--bone) 25%, transparent); outline:none; }
  /* Annuler une modification en cours (2026-09-08) : bouton unique flottant
     (voir ensureInlineEditRevertBtn() dans app.js), repositionné à côté du
     champ .users-inline-edit qui a le focus — jamais dans le flux normal
     de la grille (position:fixed), pour ne jamais décaler une colonne. */
  .users-inline-edit-revert{ display:none; position:fixed; transform: translateY(-50%); z-index:260; /* au-dessus de l infobulle de troncature (250), R24 */ align-items:center; justify-content:center; width:20px; height:20px; padding:0; background: color-mix(in srgb, var(--night) 95%, transparent); border:1px solid color-mix(in srgb, var(--bone) 30%, transparent); border-radius:50%; color: var(--ember); font-size:var(--fs-sm); cursor:pointer; }
  .users-inline-edit-revert:hover{ background: color-mix(in srgb, var(--night) 100%, transparent); }
  /* nowrap+ellipsis (2026-08-21) : sert le commentaire (troncature JS déjà
     à 24 caractères via truncate(), voir renderUsersModalTable — ceci est
     un filet de sécurité si 24 caractères ne tiennent quand même pas dans
     une colonne étroite) ET le badge passkey unique ("MacBook — date"), qui
     lui n'avait AUCUNE troncature JS et pouvait repasser à la ligne. Clic
     = "voir le détail" dans les deux cas (openCommentEditor()/
     openPasskeysDetail()) — déjà en place, juste rendu plus lisible. */
  /* height/box-sizing/line-height alignés sur .users-modal-table
     .users-inline-edit et .admin-select en cellule (2026-09-08, demande
     explicite — lignes Utilisateurs plus basses que Voyages) : sans ça, ce
     bouton (padding seul, pas de hauteur fixe) rendait ~20px de haut contre
     24px pour les <input>/<select> des autres colonnes, d'où des lignes de
     hauteur inégale selon leur contenu. line-height 18px = 24px - les 2×3px
     de padding vertical, pour un centrage vertical identique. */
  .users-comment-preview{ display:block; width:100%; height: 24px; box-sizing: border-box; line-height: 18px; text-align:left; background:none; border:none; color: var(--settings-default-text-color); opacity:0.85; font-family: inherit; font-size: var(--fs-sm); padding: 3px 6px; cursor:pointer; border-radius:4px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .users-comment-preview:hover{ background: color-mix(in srgb, var(--bone) 8%, transparent); }

  @keyframes usersSaveFlash{ 0%{ background: color-mix(in srgb, var(--c-vert-moyen-3) 45%, transparent); } 100%{ background: transparent; } }
  .save-flash{ animation: usersSaveFlash 0.8s ease; border-radius:4px; }

  .comment-editor-backdrop{ position:fixed; inset:0; background: color-mix(in srgb, var(--ink) 55%, transparent); z-index: 210; display:flex; align-items:center; justify-content:center; padding: 24px; }
  /* position:relative : voir le même commentaire sur .users-modal — point
     d'ancrage pour les poignées de redimensionnement injectées par
     initFloatingModal() (app.js), partagé par toutes les fenêtres flottantes
     du site depuis le 2026-08-21. */
  .comment-editor{ position:relative; width:100%; max-width: 460px; background: color-mix(in srgb, var(--night) 98%, transparent); color: var(--bone); border-radius: 8px; box-shadow: 0 20px 45px color-mix(in srgb, var(--ink) 40%, transparent); }
  .comment-editor-head{ display:flex; align-items:center; justify-content:space-between; padding: 14px 16px 10px; font-family:var(--text-font-family); font-size: var(--fs-sm); letter-spacing: 0.05em; text-transform:uppercase; border-bottom: 1px solid color-mix(in srgb, var(--bone) 12%, transparent); cursor:grab; }
  .comment-editor-head button{ position:relative; background:none; border:none; color: var(--bone); opacity:0.6; cursor:pointer; font-size: var(--fs-md); width:40px; height:40px; margin: -13px; display:inline-flex; align-items:center; justify-content:center; }
  .comment-editor-head button:hover{ opacity:1; }
  /* resize:none + line-height explicite (2026-08-31) : voir le commentaire
     de .comment-form textarea, même raison exacte. */
  .comment-editor textarea{ display:block; width:calc(100% - 32px); box-sizing:border-box; margin: 14px 16px 16px; background: color-mix(in srgb, var(--bone) 8%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius: 6px; padding: 10px; color: var(--bone); font-family: inherit; font-size: var(--fs-md); line-height:1.4; resize:none; }

  .passkeys-detail-list{ list-style:none; margin:0; padding: 6px 16px 16px; display:flex; flex-direction:column; gap: 8px; }
  .passkeys-detail-list li{ display:flex; align-items:center; justify-content:space-between; gap: 10px; padding: 8px 10px; background: color-mix(in srgb, var(--bone) 6%, transparent); border-radius: 6px; }
  .passkeys-detail-list .pk-info{ display:flex; flex-direction:column; gap: 2px; font-family:var(--text-font-family); font-size: var(--fs-sm); }
  .passkeys-detail-list .pk-label em{ opacity:0.6; font-style:italic; }
  .passkeys-detail-list .pk-date{ opacity:0.6; font-size: var(--fs-xs); }
  .pk-revoke{ flex:0 0 auto; font-family:var(--text-font-family); font-size: var(--fs-xs); color:var(--c-orange-moyen-4); background: color-mix(in srgb, var(--bone) 8%, transparent); border: 1px solid color-mix(in srgb, var(--c-rouge-moyen-3) 50%, transparent); padding: 6px 10px; border-radius: 14px; cursor:pointer; white-space:nowrap; }
  .pk-revoke:hover{ background: color-mix(in srgb, var(--c-rouge-moyen-3) 15%, transparent); }
  .passkeys-detail-empty{ margin: 6px 16px 16px; font-family:var(--text-font-family); font-size: var(--fs-sm); opacity:0.7; }

  /* ---------- FRISE DU VOYAGE (activités) ---------- */
  /* Masquée au niveau 1 (2026-08-24, demande explicite) : redondante avec
     la grille juste en dessous, qui montre déjà toutes les activités.
     Gardée au niveau 2 (voir renderStrip(), appelée par showActivity()) —
     body.voyage-level, déjà posé/retiré par renderVoyage()/showActivity(), est
     le signal le plus direct disponible ici, pas besoin d'en ajouter un. */
  body.voyage-level .stripbar{ display:none; }
  /* display:none, pas juste un contenu vidé (2026-08-25, bug corrigé) :
     #pageMeta/#controlsLabel sont vidés en JS au niveau 1 (renderVoyage(),
     voir app.js), mais un élément sans contenu garde tout de même sa
     marge et, pour #controlsLabel (pas un conteneur flex), une ligne
     "fantôme" d'après le line-height hérité — laissant un espace résiduel
     malgré l'absence de texte. display:none retire réellement la boîte
     du flux. */
  body.voyage-level #pageMeta, body.voyage-level #controlsLabel{ display:none; }
  /* Repoussé sous le titre, maintenant fixe (2026-08-31, demande explicite
     — voir header.hero plus haut) : top passe de var(--bar-h) à
     calc(var(--bar-h) + var(--title-h)). Sans effet au niveau 1 (masquée
     là-bas, voir body.voyage-level .stripbar plus bas) — la valeur ne
     compte que quand ce bandeau est réellement affiché (niveau 2). */
  /* Zone B, niveau 2 uniquement (--sable -> --n2-banner-bg, 2026-08-31) :
     ce bandeau n'est jamais visible au niveau 1 (masqué, voir
     body.voyage-level .stripbar plus bas) — assigné directement à la
     variable de niveau 2, pas besoin d'une surcharge conditionnelle
     comme .crumbbar/.bottombar (partagées entre les 2 niveaux, elles). */
  .stripbar{ position: fixed; left: 50%; transform: translateX(-50%); width: 100%; max-width: var(--text-max); top: calc(var(--bar-h) + var(--title-h) + var(--dis-h)); height: var(--strip-h); z-index: 49; background: var(--n2-banner-bg); border-bottom: 1px solid color-mix(in srgb, var(--dusk) 18%, transparent); }
  /* mask-image (2026-08-24, demande explicite "rendre plus intuitif le
     défilement horizontal") : fondu aux deux bords sur la largeur du
     padding (32px) déjà en place — signale qu'il y a plus de contenu à
     faire défiler sans ajouter de flèches ni changer la structure. */
  .stripbar-track{ display:flex; align-items:center; height:100%; padding: 0 32px; gap: 2px; overflow-x:auto; white-space:nowrap; scrollbar-width:none; -webkit-mask-image: linear-gradient(to right, transparent, black 32px, black calc(100% - 32px), transparent); mask-image: linear-gradient(to right, transparent, black 32px, black calc(100% - 32px), transparent); }
  .stripbar-track::-webkit-scrollbar{ display:none; }
  /* Taille augmentée (2026-08-24, demande explicite) : 12.5px -> 14.5px. */
  .strip-item{ flex: 0 0 auto; display:inline-flex; align-items:baseline; gap: 5px; background:none; border:none; font-family:var(--text-font-family); font-size: calc(var(--fs-xl) * var(--ui-scale)); letter-spacing: 0.03em; color: var(--dusk-soft); cursor:pointer; padding: 6px 11px; border-radius: 14px; white-space:nowrap; transition: background .15s ease, color .15s ease; }
  .strip-item:hover{ color: var(--dusk); }
  .strip-item.active{ background: var(--ember); color: var(--bone); }
  /* Activités déjà passées dans l'ordre du voyage (2026-08-31, demande
     explicite — "plus progressif") : teinte discrète de la même couleur
     que l'activité courante (--ember), numéro atténué, pour distinguer
     "déjà vu" de "à venir" (défaut, inchangé) d'un simple coup d'œil. */
  /* color-mix depuis --ember plutôt qu'un rgba en dur recopiant sa valeur
     d'origine (2026-08-31, bug corrigé — cette teinte ne suivait pas le
     thème malgré les apparences) : rgba(196,98,45,...) EST la valeur de
     --ember du thème "Afrique", mais figée, jamais recalculée pour un
     autre thème (ex. "Mer", accent bleu-vert) — ce point resterait
     orangé quel que soit l'accent réel choisi. */
  .strip-item.done{ background: color-mix(in srgb, var(--ember) 14%, transparent); color: var(--dusk); }
  /* Activité juste avant celle sélectionnée (2026-08-31, demande
     explicite) : teinte plus marquée que .done, DÉDUITE de --ember (même
     logique que --fullscreen-bg depuis --lightbox-bg) — jamais un
     réglage de thème séparé. */
  .strip-item.just-before-active{ background: color-mix(in srgb, var(--ember) 35%, transparent); }
  .strip-item.done .strip-num{ opacity: 0.35; }
  /* Numérotation discrète (2026-08-24, demande explicite) : renforce
     visuellement le fait qu'il s'agit d'une frise ordonnée d'activités. */
  .strip-item .strip-num{ font-size: 0.75em; opacity: 0.55; }
  .strip-item.active .strip-num{ opacity: 0.85; }
  .strip-sep{ flex: 0 0 auto; color: var(--dusk-soft); opacity: 0.4; }
  /* Barre de progression (2026-08-31, demande explicite — "plus
     progressif et percutant") : fine ligne sous le bandeau, remplie selon
     la position de l'activité courante dans le voyage (largeur calculée
     en JS, voir renderStrip() dans app.js). .stripbar est déjà positionnée
     (position:fixed) : sert de repère à ce positionnement absolu, pas
     besoin d'un position:relative séparé. */
  .strip-progress{ position:absolute; left:0; right:0; bottom:0; height:2px; background: color-mix(in srgb, var(--dusk) 10%, transparent); }
  .strip-progress-fill{ height:100%; width:0%; background: var(--ember); transition: width .3s ease; }

  /* ---------- HERO ---------- */
  header.hero{ padding: 22px 0 16px; border-bottom: 1px solid color-mix(in srgb, var(--dusk) 15%, transparent); }
  /* Titre rendu fixe aux DEUX niveaux (2026-08-31 : niveau 2 d'abord,
     niveau 1 étendu le lendemain, demande explicite — "comme c'est déjà
     le cas pour l'en-tête du niveau 2") : ancré juste sous le fil
     d'Ariane. Niveau 2 : .stripbar repoussée dessous à son tour (voir
     plus bas) — empilement fil d'Ariane -> titre -> bandeau d'activités
     -> grille de photos (qui défile). Niveau 1 : titre + carte du pays
     fixes, la liste d'activités défile juste en dessous (pas de bandeau
     à empiler ici, masqué à ce niveau). left:50%+transform et max-width
     reproduisent le centrage perdu en quittant .wrap-text (qui ne
     s'applique plus, l'élément fixe échappant à son parent) ; le padding
     horizontal (32px) reprend celui de .wrap-text pour la même raison. */
  /* Zone B (--sable -> --n1-banner-bg, 2026-08-31) : header.hero fait
     partie du même "bandeau" que crumbbar/bottombar/stripbar au sens du
     système de thèmes (même famille "en-tête/pied de page"), niveau 2
     surchargé juste en dessous. */
  body.diaporama-page header.hero{ position:fixed; top: calc(var(--bar-h) + var(--dis-h)); left:50%; transform: translateX(-50%); width:100%; max-width: var(--text-max); z-index: 48; background: var(--n1-banner-bg); padding: 22px 32px 16px; box-sizing: border-box; } /* renommage-ok */
  body.diaporama-page:not(.voyage-level) header.hero{ background: var(--n2-banner-bg); } /* renommage-ok */
  .eyebrow{ font-family:var(--text-font-family); font-size: var(--fs-md); letter-spacing: 0.14em; text-transform: uppercase; color: var(--acacia); margin-bottom: 14px; }
  /* --dusk-soft plutôt que --dusk (2026-08-26, demande explicite) : un cran
     plus clair, toujours dans la palette "safari" déjà en place (aucune
     nouvelle teinte introduite) — le noir quasi pur de --dusk était jugé
     trop agressif pour un titre de cette taille. */
  h1{ font-family: var(--title-font-family); font-weight: var(--title-font-weight); font-style: var(--title-font-style); font-size: calc(var(--fs-h1) * var(--n2-title-scale, 1)); line-height: 0.98; letter-spacing: -0.01em; color: var(--dusk-softer); }
  /* Tailles réglables (2026-10-02, Interface › Affichage) : N1 = titre du
     voyage (la période, en em, suit), N2 = titre de l'activité. */
  body.voyage-level h1{ font-size: calc(var(--fs-h1-trip) * var(--n1-title-scale, 1)); }
  /* Taille augmentée (2026-08-24, demande explicite) : 0.42em -> 0.55em. */
  /* --dusk-softer (2026-09-02, bug corrigé) : câblée en dur sur --dusk-soft,
     une variable STATIQUE absente de THEME_PARAM_TO_CSS_VAR — le titre
     (nom du voyage) suit déjà --dusk-softer (title_color, voir règle h1
     plus haut) mais pas cette partie date/période, qui restait donc figée
     quel que soit le thème choisi. */
  h1 .title-date{ font-style: var(--title-font-style); font-size: 0.55em; color: var(--dusk-softer); }
  .title-edit-btn{ position:relative; background:none; border:none; font-size: 0.3em; vertical-align: super; margin-left: 10px; opacity: 0.5; cursor:pointer; transition: opacity .15s ease; }
  .title-edit-btn:hover{ opacity: 1; }
  /* Info-bulle (2026-08-31, inventaire complet — mécanisme natif jusqu'ici) :
     crayons du titre/de la date, en haut de page mais jamais collés à un
     bord d'écran (texte du titre, généralement plus étroit que la page) —
     centrée, sous le bouton. */
  .title-edit-btn[data-tooltip]::after{ top: calc(100% + 10px); left: 50%; transform: translateX(-50%); }
  /* Crayon d'édition du nom d'activité sur N1 (2026-09-13) : le 0.3em de la
     règle de base est calé sur la taille du h1 (~30-44px) — beaucoup trop
     petit/illisible appliqué à .activity-title (14px)/.feed-post-head
     (13px), d'où une taille absolue propre à ces deux contextes. */
  .activity-info .title-edit-btn, .feed-post-head .title-edit-btn{ font-size: var(--fs-xs); margin-left: 6px; vertical-align: middle; }
  .hero-meta{ display:flex; gap: 32px; margin-top: 12px; flex-wrap: wrap; }
  .hero-meta div{ font-family:var(--text-font-family); font-size: var(--fs-md); color: var(--dusk-soft); }
  .hero-meta span{ display:block; font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.1em; color: var(--ember); margin-bottom: 4px; }
  /* Carte du pays (2026-08-31, demande explicite) : niveau 1 uniquement
     (jamais montrée au niveau 2, header.hero réutilisé pour le titre de
     l'activité) — à droite du titre général. Taille de départ à ajuster
     ensemble une fois en place (demande explicite), simple à revoir ici
     (une seule valeur, width). */
  .hero-title-row{ display:flex; align-items:center; justify-content:space-between; gap: 24px; }
  .hero-title-block{ min-width: 0; }
  /* Hauteur doublée (2026-08-31, demande explicite, "base x2 pour
     commencer") : ~64px (110px de large / ratio 1.71:1 du monde entier)
     -> 128px explicite. Le viewBox recadré sur un continent (voir
     applyCountryMapViewBox() dans app.js) n'a plus le même ratio que le
     planisphère complet — hauteur désormais fixe plutôt qu'auto-calculée
     depuis le ratio (qui varie maintenant selon le voyage/continent),
     SVG centré dedans (preserveAspectRatio par défaut, xMidYMid meet). */
  /* z-index relevé au-dessus de .crumbbar/.bottombar (50, voir plus haut) —
     bug corrigé (2026-08-31, "carte zoomée découpée par le drapeau de
     langue/l'avatar") : le zoom (transform:scale, voir plus bas) peut
     désormais visuellement déborder par-dessus le fil d'Ariane sans en
     être masqué. Sous les menus déroulants ponctuels (compte/langue,
     70) : jamais en conflit réel (états transitoires distincts), pas
     besoin de monter plus haut. */
  /* transform-origin coin haut-droit (2026-08-31, demande explicite —
     changement d'approche plutôt que continuer à chercher la cause exacte
     du chevauchement avec langue/compte) : l'agrandissement au survol
     s'étend désormais uniquement vers le bas et la gauche, jamais vers la
     droite où vivent ces icônes — les évite mécaniquement, sans dépendre
     d'un ajustement fin de z-index. Recentre au passage la carte agrandie
     vers le centre de la page (effet secondaire bienvenu, signalé par
     Thierry). */
  .country-map{ display:none; flex:0 0 auto; width: 110px; height: 128px; cursor: default; transition: transform .25s ease; transform-origin: top right; z-index: 52; }
  body.voyage-level .country-map{ display:block; }
  /* Zoom au survol encore renforcé (2026-08-31, demande explicite,
     "reste insuffisant") : x2 -> x3 par rapport à la taille de repos
     (déjà doublée ci-dessus) = x6 par rapport à la taille d'origine
     avant ce chantier. */
  /* --country-map-hover-scale (2026-09-02) : réglage GLOBAL admin (table
     site_settings, voir renderAccountDisplayAdmin()/applyCountryMapHoverScale()
     dans app.js) — 3 reste la valeur par défaut si jamais réglée. */
  .country-map:hover{ transform: scale(var(--country-map-hover-scale, 3)); }
  .country-map svg{ display:block; width:100%; height:100%; }
  /* Contours masqués par défaut (stroke = fill, seams invisibles entre
     pays voisins) — réactivés au survol (stroke contrastant, fin), comme
     demandé. Un fichier SVG PAR CONTINENT désormais (site/assets/maps/
     <continent>.svg, simplemaps.com — changement d'architecture explicite
     du 2026-08-31, remplace l'ancien fichier monde unique recadré/filtré
     en JS, voir initCountryMap() dans app.js) : plus aucun résidu visuel
     d'un autre continent possible par construction, chaque fichier ne
     contenant déjà que les pays du bon continent — le masquage explicite
     (.off-continent) devenu inutile a été retiré. vector-effect:non-
     scaling-stroke indispensable malgré tout : sans lui, l'épaisseur du
     trait est exprimée dans le système de coordonnées interne du SVG
     (~1000 unités pour l'Afrique) et devient microscopique une fois
     affichée à 110px — non-scaling-stroke l'exprime en pixels CSS réels,
     quelle que soit la taille d'affichage. */
  /* stroke-width ajouté à la transition (2026-09-03, "flash" de frontières
     très épaisses et blanches signalé en quittant le survol) : la largeur
     sautait jusqu'ici INSTANTANÉMENT de 0.083px à 1px dès la sortie du
     survol, pendant que .country-map:hover met .25s à revenir de scale(3)
     à 1 (voir plus haut) — ce décalage combiné au bug Chrome déjà
     documenté ci-dessous (non-scaling-stroke pas fiable sous un transform
     ANIMÉ posé sur un ancêtre) correspond exactement au symptôme décrit.
     Fait désormais interpoler la largeur sur la MÊME durée que le
     transform (.25s) plutôt que sauter instantanément : atténuation de
     bonne foi d'une bizarrerie du moteur de rendu, pas une garantie —
     aucun navigateur réel disponible ici pour la confirmer visuellement. */
  .country-map svg path{ fill: var(--dusk-soft); stroke: var(--dusk-soft); stroke-width: 1px; vector-effect: non-scaling-stroke; transition: stroke .2s ease, stroke-width .25s ease; }
  .country-map svg path.voyage-country{ fill: var(--ember); stroke: var(--ember); }
  /* P1-7 (13/10) : cercle autour d'un pays trop petit pour se voir. */
  .country-map svg .country-map-ring{ fill: none; stroke: var(--ember); stroke-width: 1.5px; vector-effect: non-scaling-stroke; pointer-events: none; }
  /* Affinées (2026-08-31, demande explicite, "trop épais/grossiers") :
     0.5px -> 0.25px, puis divisées par 3 une nouvelle fois (0.083px) :
     vector-effect:non-scaling-stroke protège du redimensionnement INTERNE
     du SVG (viewBox -> taille d'affichage), mais Chrome ne l'applique pas
     de façon fiable au transform:scale(3) CSS posé sur .country-map
     lui-même (un ancêtre HORS du document SVG, voir .country-map:hover
     plus haut) — le trait se retrouvait donc bel et bien agrandi x3 avec
     le reste. Valeur divisée par 3 (0.25/3) en compensation directe : une
     fois le zoom CSS appliqué par-dessus, le trait affiché revient à
     ~0.25px réels, comme prévu à l'origine. Ce correctif reste valable
     quelle que soit la source du SVG (changement d'architecture du même
     jour, sans lien avec ce point précis). Revu depuis (2026-09-03, voir
     juste en dessous) : la valeur 0.083px elle-même est abandonnée au
     profit de 0.25px (l'étape précédente de cet historique, avant cette
     compensation) — gardé ici pour la trace, ce correctif de principe
     (diviser par le facteur de zoom) reste correct, seule la préférence
     visuelle finale a changé. */
  /* Frontières "plus vives" au survol (2026-09-03, demande explicite —
     "pas plus grosses", stroke-width inchangé) : --sable (#e9dcc0, beige
     assez proche du fond --dusk-soft) remplacé par --bone (#f6f1e6, quasi
     blanc), net contraste plus élevé contre les DEUX fills possibles
     (--dusk-soft foncé et --ember, le pays du voyage courant). Encore
     poussé vers un blanc franc (2026-09-03, retour "reste trop discret") :
     --bone (légèrement crème) -> blanc pur #fff.
     Double rendu au ZOOM-IN (2026-09-03, nouveau retour) : stroke (.2s) et
     stroke-width (.25s) transitionnent sur des DURÉES LÉGÈREMENT
     DIFFÉRENTES depuis le correctif du flash de sortie ci-dessus — entre
     200ms et 250ms après le début du survol, la couleur a déjà fini de
     passer au blanc mais le trait n'a pas fini de s'affiner, produisant un
     bref instant "blanc ET encore relativement épais" (plus accentué/vif)
     avant l'état final settled (blanc, très fin) — exactement les "deux
     rendus successifs" décrits. Thierry préfère CET état transitoire,
     plus vif, comme rendu PERMANENT plutôt que l'état final settled.
     Corrigé en rendant l'ENTRÉE dans :hover instantanée (transition:none
     ICI, sur la règle :hover seulement) : aucune interpolation à
     l'entrée, une seule valeur s'applique d'un coup. La SORTIE continue
     d'utiliser la transition graduelle de la règle de base ci-dessus
     (transition prise sur l'état vers lequel on va, jamais celui qu'on
     quitte — même patron que le double-jeu déjà en place pour le zoom de
     la carte elle-même) : le correctif du flash de sortie reste donc
     intact, seule l'entrée change. Valeur remontée à 0.25px (retour à
     l'étape précédente de l'historique ci-dessus, avant sa division par
     3) : plus proche de l'épaisseur perçue pendant ce bref instant
     préféré, sans dépendre d'un calcul de compensation fragile.
     Légèrement adouci (2026-09-03, "reste un peu trop marqué" — mais PAS
     un retour complet à --sable) : blanc pur #fff -> --bone (#f6f1e6,
     l'étape intermédiaire déjà utilisée avant #fff dans cet historique) —
     reste un blanc cassé net, juste un cran moins criard que le blanc pur.
     Encore adouci d'un cran (2026-09-03, retour "encore trop blanc") :
     --bone -> un point milieu exact entre --bone et --sable
     (color-mix 50/50, plutôt qu'une nouvelle teinte devinée au jugé) — un
     compromis mesuré entre les deux extrêmes déjà testés, ni criard ni
     aussi discret qu'à l'origine.
     Encore un cran vers le beige (2026-09-03, retour "reste trop marqué") :
     50/50 -> 30% --bone / 70% --sable, en gardant EXPLICITEMENT le même
     principe (un dosage mesuré du même mélange, pas une teinte devinée) —
     penche cette fois nettement plus vers --sable tout en restant
     perceptiblement plus clair que lui.
     Un cran de plus (2026-09-03, "va dans la bonne direction, encore trop
     marqué") : 30/70 -> 15% --bone / 85% --sable, même principe, encore
     plus proche de --sable sans s'y confondre totalement. stroke-width
     INCHANGÉ (0.25px), seule la couleur est concernée ici. */
  .country-map:hover svg path{ stroke: color-mix(in srgb, var(--bone) 15%, var(--sable) 85%); stroke-width: 0.25px; transition: none; }

  /* ---------- CONTROLES ---------- */
  .controls{ display:flex; align-items:center; justify-content: space-between; padding: 14px 0 12px; flex-wrap: wrap; gap: 18px; }
  .controls-label{ font-family:var(--text-font-family); font-size: var(--fs-sm); color: var(--dusk-soft); letter-spacing: 0.05em; }
  /* Déplacé dans .bottombar (2026-08-16), aligné à droite (voir
     justify-content:flex-end sur .bottombar) — padding-right symétrique
     au padding:0 32px de .crumbs dans la barre du haut, pour un
     dégagement identique des deux côtés de la page. */
  .zoom-slider{ display:flex; align-items:center; gap: 10px; padding-right: 32px; }
  .zoom-slider .zi{ font-size: var(--fs-xs); color: var(--dusk-soft); font-family:var(--text-font-family); }
  .zoom-slider .zi.big{ font-size: var(--fs-xl); }
  /* Bug corrigé (2026-08-31, inventaire complet des info-bulles) :
     data-tooltip vivait directement sur <input type="range">, un élément
     REMPLACÉ qui ne génère jamais de ::before/::after (aucun navigateur ne
     les rend sur un <input>, quel que soit son type) — cette info-bulle
     n'a donc jamais pu s'afficher depuis sa création. Enveloppe dédiée
     (comme .lb-zoom-track pour le curseur de zoom N3, même patron) : seul
     élément à porter data-tooltip désormais. */
  .zoom-range-wrap{ position:relative; display:inline-flex; align-items:center; }
  .cell-reset-btn{ display:none; background:none; border:none; font-size: var(--fs-lg); color: var(--dusk-soft); cursor:pointer; opacity: 0.7; transition: opacity .15s ease, color .15s ease; padding: 2px 4px; }
  .cell-reset-btn.visible{ display:inline-block; }
  .cell-reset-btn:hover{ opacity: 1; color: var(--ember); }
  /* Éditeur de thème (2026-09-03, demande explicite) : "visible" désormais
     posé/retiré dynamiquement selon un vrai suivi de modification par
     champ (voir wireThemesTabEvents()/updateThemeResetBtnVisibility() dans
     app.js) — plus jamais figé en dur dans le template. Légèrement plus
     grand que la base (14px -> 18px) pour rester repérable une fois qu'il
     n'apparaît plus systématiquement au survol.
     visibility:hidden plutôt que display:none (2026-09-03, bug corrigé —
     "modifier un paramètre décale le rond de couleur vers la gauche") :
     le patron .cell-reset-btn de base (display:none/inline-block) ne
     réserve AUCUN espace tant qu'il est masqué — son apparition/
     disparition change alors la largeur totale de .theme-color-controls,
     décalant le picker/hex voisins. Même patron déjà en place ailleurs
     pour EXACTEMENT cette raison, voir .settings-slider .cell-reset-btn
     juste au-dessus : l'espace reste réservé en permanence, seule la
     visibilité (ET la possibilité de cliquer, visibility:hidden empêchant
     bien les deux) change. Couleur passée à --ember (2026-09-03, demande
     explicite — "doit utiliser la couleur signature du thème") : plus
     --dusk-soft/hover-only --ember hérités de la base. */
  .theme-color-reset{ display:inline-block; visibility:hidden; font-size: var(--fs-3xl); color: var(--ember); opacity: 1; }
  .theme-color-reset.visible{ visibility:visible; }
  .theme-color-reset:hover{ color: var(--ember); opacity: 0.8; }
  /* Polices de thème (2026-09-08) — mêmes ↺/⊘ que les couleurs ci-dessus,
     même raison de passer par visibility (pas display:none/.cell-reset-btn
     de base) : réserver leur espace en permanence, pour ne jamais décaler
     le select/les cases à cocher voisines selon l'état "modifié ou non". */
  .theme-font-reset{ display:inline-block; visibility:hidden; font-size: var(--fs-3xl); color: var(--ember); opacity: 1; }
  .theme-font-reset.visible{ visibility:visible; }
  .theme-font-reset:hover{ color: var(--ember); opacity: 0.8; }
  .theme-font-system-reset{ display:inline-block; visibility:hidden; font-size: var(--fs-2xl); color: var(--settings-default-text-color); opacity: 0.5; }
  .theme-font-system-reset.visible{ visibility:visible; }
  .theme-font-system-reset:hover{ opacity: 1; color: var(--c-orange-moyen-4); }
  input[type="range"]{ -webkit-appearance: none; appearance: none; width: 150px; height: 2px; background: color-mix(in srgb, var(--dusk) 30%, transparent); outline: none; cursor: pointer; }
  /* Longueur réduite d'un quart (2026-08-24, demande explicite) : 240px -> 180px. */
  #zoomRange{ width: 180px; }
  input[type="range"]::-webkit-slider-thumb{ -webkit-appearance: none; width: 14px; height: 14px; border-radius: 50%; background: var(--ember); cursor: pointer; border: 2px solid var(--bone); }
  input[type="range"]::-moz-range-thumb{ width: 14px; height: 14px; border-radius: 50%; background: var(--ember); cursor: pointer; border: 2px solid var(--bone); }
  /* Renforcé (2026-08-16, jugé trop discret) : contour plein (--ember,
     pas une version à 40% d'opacité) et plus épais (1.5px), texte en
     semi-gras — toujours un bouton "contour", pas rempli, comme demandé. */
  .play-start-btn{ display:none; font-family:var(--text-font-family); font-size: var(--fs-sm); font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--ember); background: none; border: 1.5px solid var(--ember); padding: 9px 16px; border-radius: 20px; cursor: pointer; transition: border-color .15s ease, background .15s ease; }
  .play-start-btn.visible{ display:inline-block; }
  .play-start-btn:hover{ border-color: var(--ember); background: color-mix(in srgb, var(--c-orange-moyen-2) 8%, transparent); }

  /* ---------- NIVEAU 1 : grille régulière d'activités ---------- */
  /* auto-fit (pas auto-fill) + taille fixe (pas 1fr) : avec auto-fill, les
     colonnes vides restantes occupent quand même leur largeur minimale et
     "poussent" les cartes réelles à gauche — justify-content:center n'aurait
     alors aucun effet visible. auto-fit les supprime réellement, laissant
     justify-content centrer le groupe de cartes quand il ne remplit pas la
     largeur disponible (ex. peu d'activités) ; les cartes gardent aussi une
     taille strictement pilotée par le curseur, sans grossir pour combler
     l'espace restant. */
  /* gap proportionnel à --cell-min-voyage (2026-08-24, bug corrigé — l'écart
     fixe de 22px restait identique quelle que soit la taille des vignettes
     choisie au curseur, donc PROPORTIONNELLEMENT plus grand quand elles
     rétrécissent : donnait l'impression qu'elles s'éloignaient les unes des
     autres. 8.5% donne ~22px à la taille par défaut (260px), et rétrécit
     avec les vignettes. */
  .grid{ display:grid; justify-content: center; gap: calc(var(--cell-min-voyage) * 0.085); padding-bottom: 90px; grid-template-columns: repeat(auto-fit, var(--cell-min-voyage)); }
  .activity-card{ position:relative; cursor:pointer; }
  /* Ratio 3/2 -> 16/9 (2026-08-31, chantier de différenciation N1/N2,
     conservé — seul le titre incrusté est annulé le lendemain, voir
     .activity-info plus bas) : carte panoramique façon affiche/carte de
     streaming, un vrai résumé visuel de l'activité (une vignette = un
     ensemble de photos), à l'opposé du N2 où chaque vignette est une
     photo précise (voir .photo-grid plus bas). */
  .single{ position:relative; aspect-ratio: 16/9; border-radius: 12px; overflow: hidden; box-shadow: 0 10px 24px color-mix(in srgb, var(--ink) 35%, transparent); }
  .single img{ display:block; width: 100%; height: 100%; object-fit: cover; transition: transform .35s ease; }
  .activity-card:hover .single img{ transform: scale(1.04); }
  /* Numéro de la carte (2026-08-31, demande explicite) : discret, coin
     haut-gauche — le badge photos (coin haut-droit, ci-dessous) reste
     réservé au survol, celui-ci est toujours visible (repère de
     navigation, pas un détail secondaire). */
  /* --card-num-bg plutôt que rgba(20,16,9,0.6) en dur (2026-08-31, système
     de thèmes, nouveau paramètre demandé explicitement) : color-mix
     reproduit la même opacité 0.6 à partir d'une couleur de thème opaque. */
  .activity-num{ position:absolute; top: 10px; left: 10px; z-index: 5; background: color-mix(in srgb, var(--card-num-bg) 60%, transparent); color: var(--bone); font-family:var(--text-font-family); font-size: var(--fs-xs); letter-spacing: 0.04em; padding: 4px 8px; border-radius: 10px; }
  /* Édition du N°/ordre depuis N1 (2026-09-13) : le badge lui-même devient
     cliquable pour l'admin, plutôt qu'un second crayon dans un coin déjà
     chargé (image + badge) — léger indice visuel au survol seulement. */
  .activity-num-editable{ cursor:pointer; transition: opacity .15s ease; }
  .activity-num-editable:hover{ opacity: 0.75; }
  /* Titre repassé en dessous de la carte (2026-08-31, annule l'incrustation
     de la veille — demande explicite) : couleur terracotta (--ember, déjà
     validée pour les libellés d'activité par le passé), poids 500, ~15px —
     minimaliste, sans gras excessif ni fond/encadrement, juste la couleur
     qui fait ressortir le texte. Nombre de photos + icône appareil photo
     ajoutés sur la même ligne, alignés à droite (demande explicite),
     discrets (couleur atténuée) pour ne pas concurrencer le titre. */
  .activity-info{ display:flex; align-items:baseline; justify-content:space-between; gap:12px; padding: 14px 2px 0; }
  /* Retour à la convention d'origine du site pour ce type de libellé
     sous une vignette (2026-08-31, demande explicite — annule le style
     inventé ce matin, Inter/normal/sans-serif) : mono, MAJUSCULES,
     poids 500, couleur ember — exactement ce que portait .cap-meta
     (lieu/jours) avant ce chantier, repris ici tel quel pour le nom de
     l'activité. */
  /* --card-label-color plutôt que --ember en dur (2026-08-31, système de
     thèmes) : même valeur aujourd'hui, mais un réglage de thème
     indépendant de l'accent global — voir :root. */
  .activity-title{ font-family: var(--title-font-family); font-size: calc(var(--fs-lg) * var(--reading-text-scale)); font-weight: var(--title-font-weight); font-style: var(--title-font-style); color: var(--card-label-color); text-transform: uppercase; letter-spacing: 0.02em; }
  /* Agrandi (2026-08-31, demande explicite, "trop petit") : 12px -> 15px,
     icône 12px -> 16px (svg partagé avec .badge, taille propre à ce
     contexte posée ici plutôt que dans le markup — voir cameraIconSvg
     dans app.js). */
  /* --card-count-color plutôt que --dusk-soft en dur (2026-08-31, système
     de thèmes, nouveau paramètre demandé explicitement). */
  .activity-count{ flex:0 0 auto; display:inline-flex; align-items:center; gap: 5px; font-family:var(--text-font-family); font-size: calc(var(--fs-xl) * var(--reading-text-scale)); color: var(--card-count-color); opacity: 0.75; white-space:nowrap; }
  .activity-count svg{ width: 16px; height: 16px; }
  /* Libellé sur 2 lignes au plus (lot A, 2026-10-10, décision de Thierry),
     « … » au-delà, libellé complet en infobulle (app.js). Réglage
     n1_tile_icons : A1 = appareil photo + nombre en pastille dans l'image
     (bas droite, comme le numéro en haut à gauche), crayon admin dans
     l'image (haut droite, au survol) ; A2 = icônes à droite de la 1re ligne,
     la 2e ligne prend toute la largeur (texte coupé par app.js). */
  .activity-card .activity-info{ display:block; }
  .activity-card .activity-title{ display:block; }
  .tile-icons-a1 .activity-title{ display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; line-clamp:2; overflow:hidden; }
  .activity-card .activity-title:has(.activity-title-editor){ display:block; -webkit-line-clamp:none; line-clamp:none; overflow:visible; }
  .tile-icons-a1 .single .activity-count{ position:absolute; right: 10px; bottom: 10px; z-index: 5; gap: 5px; padding: 3px 8px; border-radius: 10px; background: color-mix(in srgb, var(--card-num-bg) 60%, transparent); color: var(--bone); opacity: 1; font-size: var(--fs-sm); }
  .tile-icons-a1 .single .activity-count svg{ width: 13px; height: 13px; }
  .tile-icons-a1 .single .activity-edit-btn{ position:absolute; top: 10px; right: 10px; z-index: 6; margin: 0; width: 26px; height: 26px; padding: 0; border-radius: 13px; font-size: var(--fs-md); line-height: 26px; text-align:center; vertical-align: baseline; background: color-mix(in srgb, var(--card-num-bg) 60%, transparent); color: var(--bone); opacity: 0; }
  .tile-icons-a1:hover .single .activity-edit-btn, .tile-icons-a1 .single .activity-edit-btn:focus-visible{ opacity: 1; }
  .tile-icons-a1 .single .activity-edit-btn[data-tooltip]::after{ top: calc(100% + 6px); right: 0; left: auto; transform: none; }
  .tile-icons-a2 .activity-tile-icons{ float:right; display:inline-flex; align-items:center; gap: 6px; margin-left: 10px; margin-bottom: -8px; } /* marge négative : le bloc d'icônes (19 px) ne déborde jamais sur la 2e ligne du libellé (18 px), qui garde toute la largeur */
  .tile-icons-a2 .activity-tile-icons .title-edit-btn{ margin-left: 0; }

  /* ---------- Boutons "vignette" (réservés à l'administrateur, injectés par JS) ---------- */
  .cover-btn, .home-cover-btn{ position:absolute; top: 10px; z-index: 6; display:none; background: color-mix(in srgb, var(--ink) 75%, transparent); color: var(--bone); font-family:var(--text-font-family); font-size: var(--fs-xs); letter-spacing: 0.03em; padding: 6px 10px; border-radius: 16px; border:none; cursor:pointer; }
  .cover-btn{ left: 10px; }
  .home-cover-btn{ right: 10px; }
  /* Info-bulle (2026-08-31, inventaire complet — mécanisme natif jusqu'ici) :
     coins d'une vignette pouvant être étroite (160px mini, curseur N1/N2) —
     ancrée par le même bord que le bouton lui-même (gauche/droite),
     jamais centrée (déborderait de vignettes voisines sur les plus
     petites tailles). */
  .cover-btn[data-tooltip]::after{ bottom: calc(100% + 10px); left: 0; }
  .home-cover-btn[data-tooltip]::after{ bottom: calc(100% + 10px); right: 0; }
  figure:hover .cover-btn, .cover-btn.is-cover,
  figure:hover .home-cover-btn, .home-cover-btn.is-homecover{ display:inline-block; }
  .cover-btn.is-cover{ background: var(--ember); }
  .home-cover-btn.is-homecover{ background: var(--acacia); }
  /* Badge "likes" sur vignette (2026-08-16, grille/bandeau). Fond/pilule
     retiré (2026-08-19, cohérent avec le même changement en lightbox
     ci-dessous) : juste le cœur + son chiffre, une ombre portée
     (drop-shadow, couvre l'icône ET le texte) assure la lisibilité sur
     n'importe quelle photo au lieu d'un fond opaque. Purement informatif
     (pointer-events:none, le clic ouvre toujours la photo — voir
     renderPhotoContainer) ; masqué tant que le compteur est à 0 (.visible
     ajoutée/retirée en JS), jamais un simple point : le chiffre reste
     affiché en permanence dès le premier like, ce n'est pas une
     notification temporaire. .has-unseen (couleur seule, pas de dot séparé
     ici, contrairement au cœur lightbox) : il existe des likes d'autres
     personnes jamais vus (voir photo_like_views côté serveur). */
  /* Cœur agrandi 11px -> 15px, texte 10.5px -> 13px (2026-08-31, demande
     explicite : "à peine visibles/distinguables" sur la grille). */
  .like-badge{ position: absolute; bottom: 8px; right: 8px; z-index: 6; display: none; align-items: center; gap: 4px; color: var(--bone); font-family: var(--text-font-family); font-size: calc(var(--fs-md) * var(--ui-scale)); letter-spacing: 0.04em; pointer-events: none; filter: drop-shadow(0 1px 3px rgba(20,16,9,0.9)); }
  .like-badge.visible{ display: flex; }
  .like-badge svg{ display: block; }
  .like-badge.has-unseen{ color: var(--ember); }

  /* Tags photo "Essentiel"/"Culte" (2026-09-15, chantier fondations,
     admin uniquement). Inactif = fond sombre semi-transparent + icône dans
     une teinte neutre (cohérent avec cover-btn/home-cover-btn) ; actif =
     couleur signature du thème (--ember), comme demandé — les DEUX tags
     partagent la même couleur active, seule la forme (étoile/couronne) les
     distingue. Couronne légèrement plus grande que l'étoile (icône
     seulement, même taille de cercle que Essentiel depuis le correctif du
     2026-09-20 ci-dessous).
     Position revue (2026-09-20, demande explicite après capture) : tout en
     bas, centrés horizontalement, alignés avec le compteur "like" (même
     bottom que .like-badge) — plus haut au-dessus de la légende comme
     avant (la légende elle-même a été retirée le même jour, voir plus
     bas). Centrage par calc() plutôt qu'un wrapper flex commun : les deux
     boutons gardent leur affichage indépendant (chacun apparaît dès qu'il
     est actif, même si l'autre ne l'est pas), un wrapper commun aurait
     compliqué cette indépendance sans bénéfice réel ici. */
  /* Visibilité permanente (2026-09-24, demande explicite — l'apparition au
     survol uniquement gênait plus qu'elle n'aidait) : display:flex direct,
     remplace l'ancien display:none + figure:hover. .is-active ne pilote
     plus que la couleur (voir plus bas), jamais la visibilité. */
  .photo-tag-btn{ position:absolute; bottom:8px; z-index:6; display:flex; align-items:center; justify-content:center; padding:0; border:none; border-radius:50%; cursor:pointer; background: color-mix(in srgb, var(--ink) 75%, transparent); color: var(--secondary-text-color); }
  /* Style aligné sur le "like" (2026-09-16, demande explicite — pastille
     SANS changement de fond même une fois actif, voir .lb-like-btn.liked) :
     remplace l'ancien cercle plein coloré (background: var(--ember)) —
     seule l'icône (contour -> remplie, voir PHOTO_TAG_STAR/CROWN_OUTLINE_SVG)
     change de couleur. */
  .photo-tag-btn.is-active{ color: var(--ember); }
  .photo-tag-btn.is-active svg{ fill: currentColor; }
  /* Bug corrigé (2026-09-20, demande explicite) : le cercle Culte (26px)
     était plus grand que celui d'Essentiel (22px) — mêmes dimensions de
     cercle désormais pour les deux, seule l'icône (couronne déjà plus
     grande que l'étoile) continue de les distinguer. */
  .tag-essentiel-btn, .tag-culte-btn{ width:22px; height:22px; }
  .tag-essentiel-btn{ left: calc(50% - 26px); }
  .tag-culte-btn{ left: calc(50% + 4px); }
  .tag-essentiel-btn svg{ width:12px; height:12px; }
  .tag-culte-btn svg{ width:15px; height:15px; }
  /* Repositionnés dans .lb-actions-bar (2026-09-20, demande explicite) : à
     droite de la série d'icônes existante (like/commentaire/partage/
     export) au lieu d'un overlay ancré sur #lbFrame — toute la barre reste
     centrée ensemble via .lb-actions-bar (position:fixed; left:50%;
     transform:translateX(-50%), voir plus haut), l'ajout de ces deux
     boutons ne casse donc pas le centrage. position:relative (pas absolute,
     plus besoin d'ancrage à #lbFrame ici) : reste dans le flux normal de la
     barre flex EXACTEMENT comme position:static le permettait, mais sert
     aussi de contexte de positionnement à l'info-bulle ::after du bouton
     (bug corrigé 2026-09-23 — avec :static, ::after remontait jusqu'au
     premier ancêtre positionné au lieu de se centrer sur l'icône, même
     cause que le correctif .lb-fs-btn/.lb-like-btn plus bas). Bordure
     alignée sur .lb-like-btn/.lb-comment-btn (même pastille : fond +
     contour), qui manquait ici (fond seul jusqu'ici, voir .photo-tag-btn
     de base plus haut). */
  /* bottom:auto (2026-09-24, "pas à la même hauteur que like/commentaire") :
     .photo-tag-btn de base pose bottom:8px (utile pour son ancrage
     position:absolute dans la grille N2) — jamais réinitialisé ici jusqu'à
     présent alors que ce bouton est position:relative dans cette barre,
     donc décalé vers le haut de 8px par rapport à ses voisins qui n'ont
     pas cette propriété. */
  .lb-actions-bar .photo-tag-btn{ position:relative; bottom:auto; display:flex; border: 1px solid color-mix(in srgb, var(--bone) 35%, transparent); }
  /* left:auto (2026-09-25, cause réelle de l'espacement inégal
     Essentiel/Culte trouvée) : .tag-essentiel-btn/.tag-culte-btn portent
     un `left` calé sur la grille N2 (position:absolute, centrage de deux
     icônes dans une vignette — voir plus haut), jamais réinitialisé ici.
     .lb-actions-bar .photo-tag-btn passe en position:relative (pas
     static) : `left` continue donc à s'appliquer, décalant visuellement
     les deux icônes de leur position réelle dans le flux flex SANS
     changer l'espace qu'elles réservent — d'où un espacement à l'écran
     différent du simple gap:10px du flex, invisible dans le calcul mais
     bien réel au rendu. */
  .lb-actions-bar .tag-essentiel-btn, .lb-actions-bar .tag-culte-btn{ width:34px; height:34px; left:auto; }
  .lb-actions-bar .tag-essentiel-btn svg{ width:18px; height:18px; }
  .lb-actions-bar .tag-culte-btn svg{ width:22px; height:22px; }
  /* Rapprochées l'une de l'autre (2026-09-24, demande explicite, comme
     like/commentaire déjà groupés via .lb-counter-group) : .lb-tag-group
     ci-dessous a son propre gap, plus serré que celui de .lb-actions-bar
     entre les DIFFÉRENTS groupes d'icônes — voir lbActionsBar.appendChild
     dans app.js, qui crée maintenant ce wrapper au lieu d'ajouter les 2
     boutons directement dans la barre. */
  /* gap:10px (2026-09-25, demande explicite) : identique à .lb-actions-bar
     ci-dessus, pour que les 6 icônes (like/commentaire/partage/
     téléchargement/essentiel/culte) aient toutes le même espacement de
     bord à bord, malgré la taille propre plus petite des 2 dernières
     (34px vs 40px, sans incidence sur le gap). */
  .lb-tag-group{ display:flex; align-items:center; gap:10px; }

  /* Grille "Photos" (2026-09-20, chantier fondations) : mêmes icônes
     étoile/couronne que les badges N2/N3/N4, mais boutons de cellule de
     grille ordinaires (flux normal, pas d'overlay) — convention propre,
     .photo-tag-btn ne convient pas ici (position:absolute pensé pour un
     overlay sur une photo). */
  .photos-tag-btn{ display:inline-flex; align-items:center; justify-content:center; width:26px; height:26px; padding:0; border:none; border-radius:50%; cursor:pointer; background: color-mix(in srgb, var(--ink) 20%, transparent); color: var(--secondary-text-color); }
  .photos-tag-btn svg{ width:14px; height:14px; }
  .photos-tag-btn.is-active{ background: var(--ember); color: var(--bone); }

  figure{ position: relative; display:block; overflow: hidden; background: var(--dusk); cursor: pointer; border-radius: 2px; }
  /* Photos SANS filtre (13/10, AC : « photos blafardes ») : le filtre
     « ancien » de la maquette d'origine (sepia .28, saturate 1.15,
     brightness .94, contrast 1.05), posé sur les mosaïques, la visionneuse,
     les cartes et les vignettes, ternissait les couleurs ; les fichiers
     sont fidèles à l'original (sRGB, mêmes pixels). Retiré partout. */
  figure img{ display:block; width:100%; height:auto; transition: transform .5s cubic-bezier(.2,.8,.2,1); }
  figure:hover img{ transform: scale(1.03); }

  /* ---------- NIVEAU 2 : mode damier, toutes les vignettes en cases carrées ---------- */
  .photo-grid{ display:grid; grid-template-columns: repeat(auto-fill, minmax(var(--cell-min-grid), 1fr)); gap: 12px; padding-bottom: 14px; }
  .photo-grid figure{ aspect-ratio: 1/1; border-radius: 12px; }
  .photo-grid figure img{ width:100%; height:100%; object-fit:cover; }
  .photo-grid figure.active{ outline: 3px solid var(--ember); outline-offset: 2px; }
  /* Légende au survol (nom d'activité/date/N°) retirée (2026-09-20, demande
     explicite — "ça ne sert à rien") : figcaption/.cap-title/.cap-meta
     supprimés du markup (renderPhotoContainer, assets/app.js), plus aucune
     règle ne les cible ici. */

  /* ---------- LIGHTBOX (plein écran, quel que soit le format) ---------- */
  /* Niveau 3 (visionneuse normale, dans la page). Le plein écran natif
     (niveau 4, plus sombre) surcharge ce fond plus bas via #lightbox:fullscreen. */
  /* Simplifié (2026-08-27, demande explicite), PUIS partiellement annulé
     le même jour (voir #lightbox:not(:fullscreen):not(.lb-portrait) plus
     bas) : niveau 4 partage TOUJOURS exactement la même boîte que niveau 3
     par défaut (100% de #lightbox, voir .lightbox-media plus bas) — mais
     niveau 3 réserve à nouveau une zone haut/bas pour les photos non
     portrait, jugé plus esthétique à l'usage que le remplissage total.
     L'ancien calcul JS (computeLightboxBounds(), --lb-top-h/--lb-bottom-h)
     existait pour la même raison (empêcher la photo de chevaucher
     .lb-topbar/.lb-bottombar) — remesuré désormais par
     updateLightboxReservedZones() dans app.js, plutôt que ressuscité tel
     quel. Les pastilles opaques (voir plus bas) restent le filet de
     sécurité pour tout chevauchement résiduel (niveau 4, et niveau 3 pour
     les portraits, jamais insets). */
  /* overscroll-behavior-x (2026-08-19, bug corrigé) : sans elle, un glissé
     horizontal à deux doigts sur trackpad peut être intercepté par Chrome
     lui-même comme geste natif "naviguer en arrière/avant", AU-DESSUS de la
     page — indépendamment de e.preventDefault() dans le handler 'wheel' (qui
     ne suffit pas à le supprimer). Cette propriété indique explicitement au
     navigateur que #lightbox gère lui-même ce défilement horizontal. */
  .lightbox{ position: fixed; inset: 0; background: var(--lightbox-bg); display:none; align-items:center; justify-content:center; z-index: 100; flex-direction: column; overscroll-behavior: none; }
  .lightbox.open{ display:flex; }
  /* Boîte par défaut : 100% de #lightbox (niveau 4 toujours ; niveau 3 pour
     les photos portrait, voir plus bas pour l'exception non-portrait) —
     largeur/hauteur EXPLICITES (pas seulement max-width/max-height) : sur
     un grand écran, une photo source plus petite que la fenêtre ne serait
     sinon jamais agrandie par un simple plafond max-*, laissant du vide
     autour. object-fit:contain sur l'img garde la photo proportionnée,
     sans la déformer ni la recadrer — le lettrboxing éventuel (ratio photo
     ≠ ratio fenêtre) est géré par lightboxSizesFor()/
     updateClosePhotoPosition() dans app.js, pas ici (sauf niveau 3 non
     portrait, où ces deux fonctions n'ont plus besoin d'intervenir — la
     boîte inset s'en charge nativement, voir leurs commentaires). */
  /* touch-action:none (2026-09-03, cause RÉELLE trouvée par relecture — pas
     une nouvelle hypothèse de la même famille que les précédentes, jamais
     vérifiée jusqu'ici : le viewport du site reste redimensionnable
     (aucun "user-scalable=no"/"maximum-scale" dans les balises <meta
     viewport>, voir index.html etc.) et AUCUN élément ne portait de
     touch-action — le pincement à deux doigts sur la photo reste donc, par
     défaut, ambigu pour le navigateur entre "zoom natif du VIEWPORT
     entier" et "geste laissé au JS". e.preventDefault() dans le touchmove
     de pincement (voir plus bas, "Pincement (zoom tactile)") ne suffit pas
     à lui seul à trancher côté iOS/Android de façon fiable — c'est le
     patron EXACT déjà rencontré avec le swipe horizontal 2 doigts au
     trackpad, récupéré par Chrome comme navigation arrière/avant malgré
     preventDefault() dans 'wheel' (voir overscroll-behavior-x plus haut,
     même fichier) : un geste ambigu, quand deux systèmes se disputent son
     interprétation, peut être capté par le navigateur AVANT que le code
     JS n'ait fini de se prononcer — plausiblement plus souvent pour un
     geste LENT (le navigateur a le temps de "décider" qu'il s'agit d'un
     zoom de page) que pour un geste rapide (le pincement finit avant que
     cette décision native ait pris le dessus), cohérent avec le symptôme
     précisément décrit ("lent = quasi aucun effet, rapide = saute
     directement"). touch-action:none tranche cette ambiguïté de façon
     DÉCLARATIVE, en amont de tout JS — la manière standard et fiable de
     désactiver un geste natif concurrent sur un élément précis, sans
     toucher au reste du site (scroll normal de page, pincement pour
     zoomer le TEXTE ailleurs restent inchangés, hors de cette zone). Zone
     scopée à .lightbox-media (pas #lightbox entier) : épargne
     .exif-panel/.exif-body, un panneau frère (pas un descendant) qui
     dépend lui du défilement tactile natif (overflow-y:auto). Vérifié par
     lecture de code uniquement — aucun geste réel possible depuis cet
     environnement, la théorie mathématique du calcul de scale (basé sur
     la distance ABSOLUE entre les deux doigts, jamais un delta/une
     vitesse, voir touchPairDist()) exclut par ailleurs toute dépendance à
     la vitesse côté JS lui-même : la seule explication restante,
     compatible avec le symptôme, est une interception AVANT que ce JS ne
     s'exécute. */
  .lightbox-media{ position: relative; display:flex; align-items:center; justify-content:center; width: 100%; height: 100%; touch-action: none; }
  .lightbox-media .frame{ overflow: hidden; width: 100%; height: 100%; display:flex; align-items:center; justify-content:center; cursor: grab; }
  .lightbox-media .frame.dragging{ cursor: grabbing; }
  .lightbox-media img{ width: 100%; height: 100%; object-fit: contain; transform-origin: center center; user-select: none; -webkit-user-drag: none; }
  /* Niveau 3 UNIQUEMENT (jamais niveau 4, #lightbox:fullscreen reste 100%/
     100% plus bas), photos NON portrait uniquement (.lb-portrait posée en
     JS sur #lightbox selon p.r, voir updateLightbox()) — annulation
     partielle (2026-08-27, demande explicite) du remplissage total introduit
     plus tôt le même jour (voir commentaire au début de ce bloc LIGHTBOX) :
     une photo assez large pour toucher les bords gauche/droite ne doit
     jamais recouvrir .lb-topbar/.lb-bottombar avec elle. Les portraits
     restent inchangés (100%/100%) : leur largeur réduite par le ratio les
     tient déjà à l'écart du contenu de ces barres. --lb-top-h/--lb-bottom-h
     mesurées en JS (updateLightboxReservedZones() dans app.js), jamais
     codées en dur : dépendent du contenu réel des barres (langue, réglage
     "Taille de l'interface"...). position:absolute avec top/bottom explicites
     (plutôt que le centrage flex hérité de .lightbox) : définit exactement
     la zone utile même quand haut ≠ bas, sans calcul margin+centrage flex.
     Dupliqué plutôt que groupé par virgule (même raison que #lightbox
     :fullscreen/:-webkit-full-screen plus bas : un navigateur qui ne
     reconnaît pas l'un des deux pseudo-sélecteurs invaliderait toute la
     liste groupée si elle était combinée). */
  #lightbox:not(:fullscreen):not(.lb-portrait) .lightbox-media{ position: absolute; top: var(--lb-top-h, 0px); bottom: var(--lb-bottom-h, 0px); left: 0; right: 0; width: auto; height: auto; }
  #lightbox:not(:-webkit-full-screen):not(.lb-portrait) .lightbox-media{ position: absolute; top: var(--lb-top-h, 0px); bottom: var(--lb-bottom-h, 0px); left: 0; right: 0; width: auto; height: auto; }
  /* Affichage temporaire du zoom en cours, au centre de la photo, pendant
     l'ajustement (souris ou trackpad) — disparaît peu après l'arrêt du
     geste (voir showZoomOsd() dans app.js). */
  .lb-zoom-osd{ position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-family: var(--text-font-family); font-size: var(--fs-7xl); font-weight: 700; color: var(--bone); background: color-mix(in srgb, var(--ink) 55%, transparent); border-radius: 16px; padding: 14px 30px; pointer-events: none; z-index: 15; opacity: 0; transition: opacity .2s ease; }
  .lb-zoom-osd.visible{ opacity: 1; }

  /* Niveau 4 (plein écran natif) : le plus sombre de la hiérarchie, distinct
     du niveau 3 (visionneuse normale) — seule différence restante entre les
     deux niveaux (voir plus haut). Dupliqué plutôt que groupé par virgule :
     un navigateur qui ne reconnaît pas l'un des deux pseudo-sélecteurs
     (:fullscreen / :-webkit-full-screen) invaliderait toute la liste
     groupée si elle était combinée. */
  #lightbox:fullscreen{ background: var(--fullscreen-bg); }
  #lightbox:-webkit-full-screen{ background: var(--fullscreen-bg); }
  /* Réglage "Remplir tout l'écran" (Réglages → Affichage) : seul cas
     restant où la photo NE remplit PAS sa boîte — réservé au plein écran
     natif (jamais au niveau 3, voir libellé du réglage). Repli fixe
     96vw/84vh : simple boîte modeste, plus besoin de mesurer les barres
     (pastilles, jamais de chevauchement critique désormais). */
  body.fs-no-fill #lightbox:fullscreen .lightbox-media{ width: auto; height: auto; max-width: 96vw; max-height: 84vh; }
  body.fs-no-fill #lightbox:fullscreen .lightbox-media .frame{ width: auto; height: auto; max-width: 96vw; max-height: 84vh; }
  body.fs-no-fill #lightbox:fullscreen .lightbox-media img{ width: auto; height: auto; max-width: 96vw; max-height: 84vh; }
  body.fs-no-fill #lightbox:-webkit-full-screen .lightbox-media{ width: auto; height: auto; max-width: 96vw; max-height: 84vh; }
  body.fs-no-fill #lightbox:-webkit-full-screen .lightbox-media .frame{ width: auto; height: auto; max-width: 96vw; max-height: 84vh; }
  body.fs-no-fill #lightbox:-webkit-full-screen .lightbox-media img{ width: auto; height: auto; max-width: 96vw; max-height: 84vh; }

  /* Barre du bas, une seule ligne (2026-08-16, refonte complète — remplace
     l'ancienne .lightbox-info à deux niveaux + .lb-res-indicator séparée) :
     incrustation en bas d'écran, pas un bloc dans le flux (même raison que
     l'ancienne .lightbox-info : un enfant flex avec flex-shrink:0 dans
     .lightbox, display:flex/flex-direction:column, forcerait
     .lightbox-media, flex-shrink:1 par défaut, à rétrécir pour lui faire de
     la place — la photo ne pourrait alors jamais vraiment remplir l'écran).
     Deux groupes en une rangée (2026-08-31, groupe central "lieu/date/n°"
     retiré — jugé redondant avec le fil d'Ariane/l'EXIF, voir
     projet/journal/INSTRUCTIONS_CLAUDE_CODE.md) : gauche (résolution + Ultra), droite
     (curseur de zoom + pourcentage). display:flex + justify-content:
     space-between suffit désormais (repasse de grid à flex : la grille à 3
     colonnes n'avait de sens QUE pour centrer un groupe du milieu qui
     n'existe plus). */
  /* Dégradé de fond RETIRÉ (2026-08-31, résidu confirmé inutile) : ajouté à
     l'origine pour détacher la légende "lieu — date · n°" (texte nu, sans
     pastille propre) d'une photo chargée derrière — cette légende a été
     supprimée du site (voir projet/journal/INSTRUCTIONS_CLAUDE_CODE.md). Tout ce qui
     reste ici (résolution, bouton "forcer Ultra", valeur de zoom) porte
     déjà SA PROPRE pastille opaque (voir .lb-res-value/.lb-force-ultra-btn/
     .lb-zoom-value), donc un fond de barre commun n'apportait plus rien —
     seul un bandeau ombré résiduel restait visible directement sur la
     photo en plein écran natif (N4, où .lightbox-media épouse tout
     l'écran), invisible en fenêtré (N3, où cette zone tombe sur le fond
     sombre de la lightbox, pas sur la photo). */
  .lb-bottombar{ position:absolute; left:0; right:0; bottom:0; z-index:10; display:flex; justify-content:space-between; align-items:center; gap:16px; color: var(--bone); padding: 8px 24px; }
  .lb-bottombar-left, .lb-bottombar-right{ display:flex; align-items:center; gap:10px; }
  .lb-zoom{ display:flex; align-items:center; gap: 10px; }
  /* --settings-label-color plutôt que --dust-gold en dur (2026-08-31, bug
     corrigé — "couleur de police des curseurs" ne suivait pas le thème) :
     même famille que les libellés EXIF/Réglages (--dust-gold aujourd'hui
     des deux côtés). */
  .lb-zoom .zi{ font-size: var(--fs-xs); color: var(--settings-label-color); font-family:var(--text-font-family); }
  .lb-zoom .zi.big{ font-size: var(--fs-xl); }
  /* Repère "100% réel" : positionné dynamiquement en JS (left en %) au point
     de la piste où la taille affichée égale la vraie résolution du fichier
     (compte tenu du DPR) — masqué (voir [hidden] sur l'élément) si ce point
     tombe hors de la plage du curseur. z-index + hauteur > au curseur lui-
     même (voir input[type=range]::-webkit-slider-thumb, 14px) pour qu'il
     reste visible même quand le curseur le recouvre exactement (bug corrigé
     2026-08-15) : il dépasse alors visiblement au-dessus et en dessous.
     input positionné explicitement pour que le repère (z-index plus haut)
     passe bien par-dessus, quel que soit l'ordre naturel dans le DOM. */
  .lb-zoom-track{ position: relative; display: inline-flex; align-items: center; }
  /* La piste du curseur partagée (input[type="range"], plus haut) est un
     brun sombre à 30% d'opacité — pensé pour un fond clair (--sable), trop
     peu visible ici sur le fond sombre de la visionneuse. Surcharge locale,
     plus claire et légèrement plus épaisse (2px -> 3px). */
  /* Ligne plus fine et délicate (2026-08-24, demande explicite) : 3px ->
     1px, opacité légèrement réduite.
     Bug corrigé (2026-08-31, "curseur décalé par rapport au reste") :
     height:1px posé directement sur l'input (comme le fait la règle
     globale input[type="range"] plus haut, 2px) rendait sa boîte de mise
     en page quasi nulle malgré appearance:none — le curseur (14px,
     ::-webkit-slider-thumb/::-moz-range-thumb) ne se centre alors plus de
     façon fiable d'un navigateur à l'autre sur une boîte aussi fine.
     Séparé en 2 : l'input garde une vraie hauteur (14px, celle du
     curseur — align-items:center du parent la centre alors de façon
     fiable) et SEULE la piste visuelle (::-webkit-slider-runnable-track/
     ::-moz-range-track) reste fine (1px). */
  .lb-zoom-track input[type="range"]{ position: relative; z-index: 1; height: 14px; background: none; }
  .lb-zoom-track input[type="range"]::-webkit-slider-runnable-track{ height: 1px; background: color-mix(in srgb, var(--bone) 28%, transparent); }
  .lb-zoom-track input[type="range"]::-moz-range-track{ height: 1px; background: color-mix(in srgb, var(--bone) 28%, transparent); }
  .lb-zoom-native-marker{ position: absolute; top: 50%; transform: translate(-50%, -50%); width: 2px; height: 20px; background: var(--dust-gold); box-shadow: 0 0 0 1px color-mix(in srgb, var(--ink) 60%, transparent); pointer-events: none; z-index: 2; }
  /* Barre du haut, une seule ligne (2026-08-16, refonte complète) : gauche
     = fil d'Ariane + compteur ; droite = plein écran, EXIF, Fermer (le
     widget compte, élément séparé vivant dans .crumbbar, s'aligne sur
     cette même rangée via body:has(#lightbox.open) .crumbbar .account-widget
     plus haut — d'où le padding-right qui lui réserve de la place plutôt
     que d'aller jusqu'au bord). Simple flexbox plutôt que des `right`
     codés en dur par élément (l'ancien système) : les boutons/texte
     (largeur variable selon la langue, ex. "Fermer ✕" vs "Close ✕") se
     réorganisent tout seuls, sans recalcul manuel par langue. */
  /* padding-right symétrique (2026-08-16) : le widget compte n'est plus
     réservé par un padding-right calculé à la main — il est maintenant
     déplacé EN VRAI dans .lb-topbar-right (voir openLightbox()/
     closeLightbox() dans app.js), donc positionné tout à droite par la
     simple logique de flexbox (dernier enfant, gap régulier), pas par une
     estimation de largeur. */
  /* Pastilles par groupe (2026-08-27, remplace le dégradé continu ajouté le
     2026-08-26) : même traitement que les pastilles de .lb-bottombar
     (cœur, commentaire, partage, export, légende) plutôt qu'une bande
     continue sur toute la largeur — un fond par groupe fonctionnel (fil
     d'Ariane+compteur à gauche, icônes+compte à droite), pas par la barre
     entière. .lb-topbar lui-même n'a donc plus de fond propre, juste le
     padding qui écarte les deux pastilles du bord de l'écran. */
  .lb-topbar{ position:absolute; top:0; left:0; right:0; z-index:10; display:flex; align-items:center; justify-content:space-between; padding: 14px 20px; }
  .lb-topbar-left{ display:flex; align-items:center; gap:14px; min-width:0; background: color-mix(in srgb, var(--ink) 75%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 35%, transparent); border-radius: 22px; padding: 7px 16px; }
  /* Option A (2026-08-28, demande explicite) : une seule pastille compacte
     pour le SEUL groupe d'icônes (plein écran, EXIF, Fermer) — l'avatar
     compte, déplacé ici en vrai (voir openLightbox()/closeLightbox() dans
     app.js), reste libre, sans enveloppe/pastille à côté. .lb-topbar-right
     n'est donc plus qu'un conteneur flex neutre pour les deux. */
  .lb-topbar-right{ display:flex; align-items:center; gap:16px; flex:0 0 auto; }
  .lb-topbar-icons{ display:flex; align-items:center; gap:14px; background: color-mix(in srgb, var(--ink) 75%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 35%, transparent); border-radius: 18px; padding: 5px 14px; }
  /* Hauteur de la zone de clic portée à 40px (2026-08-31, bug corrigé,
     même raison que .users-modal-head button plus haut) : la largeur du
     texte "FERMER"/"CLOSE" suffit déjà, seule la hauteur (~14px de police,
     aucun padding) restait trop fine. margin vertical négatif : n'agrandit
     pas la pastille .lb-topbar-icons qui l'entoure. */
  /* --secondary-text-color plutôt que --bone en dur (2026-08-31, bug
     corrigé — "couleur des textes" de la visionneuse ne suivait pas le
     thème) : même famille que le texte secondaire de l'écran de
     paramétrage (--bone aujourd'hui des deux côtés). */
  .lb-close{ background: none; border: none; color: var(--secondary-text-color); font-family:var(--text-font-family); font-size: var(--fs-lg); letter-spacing: 0.08em; cursor: pointer; text-transform: uppercase; opacity: 0.65; transition: opacity .15s ease; padding: 13px 0; margin: -13px 0; display:inline-flex; align-items:center; }
  .lb-close:hover{ opacity: 1; }
  .lb-exif-btn{ background: none; border: none; font-size: var(--fs-icon-md); cursor: pointer; opacity: 0.65; transition: opacity .15s ease; line-height: 1; color: var(--bone); }
  .lb-exif-btn:hover, .lb-exif-btn.active{ opacity: 1; }
  /* Bouton "légendes" (2026-08-27, niveau 4 uniquement — voir les deux
     règles :not(:fullscreen) plus bas) : même patron visuel que
     .lb-exif-btn. .active = légendes actuellement AFFICHÉES (pas masquées),
     cohérent avec .lb-fs-btn.active = plein écran actuellement actif. */
  .lb-captions-btn{ background: none; border: none; cursor: pointer; opacity: 0.65; transition: opacity .15s ease; line-height: 1; color: var(--bone); }
  .lb-captions-btn:hover, .lb-captions-btn.active{ opacity: 1; }
  .lb-captions-btn svg{ display:block; }
  #lightbox:not(:fullscreen) .lb-captions-btn{ display: none; }
  #lightbox:not(:-webkit-full-screen) .lb-captions-btn{ display: none; }
  /* Effet du bouton ci-dessus, SEULEMENT en plein écran natif (la classe
     .lb-hide-captions peut être posée sur #lightbox au niveau 3 aussi,
     selon la préférence enregistrée, mais n'y a aucun effet visuel — voir
     applyFsShowCaptionsSetting() dans app.js). visibility:hidden plutôt que
     display:none : préserve la mise en page (.lb-topbar en flex
     space-between, un seul enfant restant se repositionnerait sinon au
     mauvais endroit). Icônes (.lb-topbar-right, .lb-actions-bar) et
     contrôles (.lb-bottombar-left/-right, zoom) jamais concernés — voir
     "Exception explicite" de la demande. */
  #lightbox:fullscreen.lb-hide-captions .lb-topbar-left{ visibility: hidden; }
  #lightbox:-webkit-full-screen.lb-hide-captions .lb-topbar-left{ visibility: hidden; }
  /* SVG plutôt que l'emoji 📷 d'origine (2026-08-16) : un emoji ignore
     `color` (rendu en couleurs fixes par la police), contrairement aux
     autres glyphes de la lightbox (✕, ⛶) qui héritent nativement de
     `currentColor` — d'où un contraste incohérent avec --bone. */
  .lb-exif-btn svg{ display:block; }
  .lb-fs-btn{ background: none; border: none; font-size: var(--fs-icon-lg); cursor: pointer; opacity: 0.65; transition: opacity .15s ease; line-height: 1; color: var(--bone); }
  .lb-fs-btn:hover, .lb-fs-btn.active{ opacity: 1; }
  /* Deuxième point de fermeture (2026-08-16), flottant sur la photo
     elle-même (voir .lightbox-media, position:relative) — coexiste avec
     .lb-close dans la barre du haut, ne le remplace pas. Discret par
     design : petit, opacité réduite au repos, jamais un vrai bouton
     "Fermer" dupliqué. */
  /* Zone de clic élargie à 40×40px SANS agrandir le disque visuel
     (2026-08-31, bug corrigé, demande explicite "même si l'icône visuelle
     reste plus petite") : contrairement aux boutons ✕ nus (voir
     .users-modal-head button plus haut), celui-ci porte un fond circulaire
     — un simple padding aurait aussi agrandi ce fond. Le bouton lui-même
     devient la boîte de clic invisible (40px, transparente) ; le disque
     visuel (26px, inchangé) est déporté sur ::before, positionné/dimensionné
     indépendamment. top/right décalés de -7px (la moitié de l'écart
     40-26) pour que le disque reste EXACTEMENT où il était (voir aussi
     updateClosePhotoPosition() dans app.js, même décalage appliqué à son
     calcul de position en plein écran/fenêtré). */
  .lb-close-photo{ position:absolute; top:3px; right:3px; z-index:11; width:40px; height:40px; border-radius:50%; background:none; border:none; color:transparent; font-size:0; cursor:pointer; display:flex; align-items:center; justify-content:center; }
  .lb-close-photo::before{ content:"✕"; display:flex; align-items:center; justify-content:center; width:26px; height:26px; border-radius:50%; background: color-mix(in srgb, var(--ink) 50%, transparent); color: var(--bone); font-size:var(--fs-md); opacity:0.55; transition: opacity .15s ease; }
  .lb-close-photo:hover::before{ opacity: 1; }
  /* Élargi (280px -> 340px, 2026-08-28, demande explicite) : les champs
     longs ("Objectif", nom de fichier...) retournaient trop souvent à la
     ligne dans .exif-row (justify-content:space-between, valeur alignée à
     droite). */
  /* --settings-bg (2026-08-31, système de thèmes) : le panneau EXIF fait
     partie de la même famille d'écrans que Compte/Réglages (demande
     explicite de Thierry — un seul réglage de thème partagé pour les
     deux). Bordure dérivée de --settings-bg (2026-09-01, plus de réglage
     séparé — voir --border-color, retiré, dans le bloc :root). */
  .exif-panel{ position: absolute; top: 0; right: 0; height: 100%; width: 340px; background: color-mix(in srgb, var(--settings-bg) 97%, transparent); border-left: 1px solid color-mix(in srgb, var(--settings-bg), white 15%); transform: translateX(100%); transition: transform .25s ease; z-index: 20; display:flex; flex-direction:column; }
  .exif-panel.open{ transform: translateX(0); }
  .exif-head{ display:flex; align-items:center; justify-content:space-between; padding: 20px 20px 16px; color: var(--bone); font-family:var(--text-font-family); font-size: var(--fs-md); letter-spacing: 0.06em; text-transform: uppercase; border-bottom: 1px solid color-mix(in srgb, var(--bone) 12%, transparent); }
  .exif-head button{ position:relative; background:none; border:none; color: var(--bone); opacity:0.6; cursor:pointer; font-size: var(--fs-md); width:40px; height:40px; margin: -13px; display:inline-flex; align-items:center; justify-content:center; }
  .exif-head button:hover{ opacity:1; }
  /* Info-bulle "Fermer" (2026-08-31, inventaire complet — mécanisme natif
     jusqu'ici, seule exception restante du panneau EXIF). Centrée, comme
     .users-modal-head/.comment-editor-head : texte court ("Fermer"), le
     panneau (340px) reste assez large pour l'accueillir sans déborder de
     l'écran. */
  .exif-head button[data-tooltip]::after{ top: calc(100% + 10px); left: 50%; transform: translateX(-50%); }
  .exif-body{ padding: 6px 20px 20px; overflow-y:auto; overscroll-behavior: none; }
  /* Zones de texte extensibles dans une fenêtre/popup (2026-09-02, demande
     explicite : le défilement d'une fenêtre ne doit jamais "chaîner" vers la
     page en dessous une fois la limite atteinte) — autoGrowTextarea() pose
     overflow-y:auto EN LIGNE une fois maxLines dépassé, mais
     overscroll-behavior n'a pas d'équivalent utile à poser en JS ici, une
     règle CSS ciblée suffit. */
  #commentInput, #contactBody, #commentEditorText{ overscroll-behavior: none; }
  .exif-row{ display:flex; justify-content:space-between; gap: 12px; padding: 11px 0; border-bottom: 1px solid color-mix(in srgb, var(--settings-bg), white 8%); font-family:var(--text-font-family); font-size: var(--fs-sm); }
  /* Couleur relevée (2026-08-30, contraste jugé insuffisant) : --dust-gold
     à 85% d'opacité restait trop terne sur le fond quasi noir du panneau
     — un jaune-or plus vif, à pleine opacité, sans changer le reste de la
     palette (--dust-gold garde son usage ailleurs sur le site). Taille
     reprise EXACTEMENT de l'onglet actif des fenêtres de paramétrage
     (.settings-tab.active, 13.5px) plutôt qu'une valeur inventée —
     cohérence avec ce standard déjà établi. */
  /* Poids normal explicite (2026-08-30, vérifié) : aucune règle ne posait
     réellement de gras ici (ni .exif-row, ni .k/.v, ni un ancêtre) — la
     couleur plus vive donnait probablement une impression optique de gras
     par comparaison à l'ancien --dust-gold terne, sans changement réel de
     graisse. Fixé explicitement malgré tout, pour lever tout doute.
     Couleur (2026-08-30, jugée trop vive/orangée) : reprise de
     .lb-bottombar-center .cap-meta (légende "lieu — date · N°" de la
     visionneuse, niveaux 3/4) — var(--dust-gold) à pleine opacité, une
     teinte déjà existante réutilisée telle quelle plutôt qu'une valeur
     inventée. */
  .exif-row .k{ color: var(--settings-label-color); font-size: var(--fs-lg); font-weight: normal; }
  /* Un cran plus grande que l'ancienne taille unique de .exif-row (12px) —
     même incrément que .users-modal-msg.error (12px -> 13.5px), déjà la
     convention "un cran" de ce projet. */
  .exif-row .v{ color: var(--settings-value-color); text-align:right; font-size: var(--fs-lg); font-weight: normal; }
  /* Séparateur de bloc (2026-08-28, demande explicite — panneau réorganisé
     en groupes : Date / exposition / appareil-objectif / fichier) : plus
     marqué que le simple trait fin déjà présent entre chaque .exif-row
     (border-bottom, 8% d'opacité) pour bien distinguer une VRAIE coupure de
     section d'une simple séparation de ligne. */
  .exif-group-sep{ border: none; border-top: 1px solid color-mix(in srgb, var(--bone) 25%, transparent); margin: 8px 0; }
  .exif-empty{ color: var(--bone); opacity: 0.5; font-family:var(--text-font-family); font-size: var(--fs-sm); padding: 16px 0; }
  /* .lb-exif-btn{right:70px} (mobile) retiré (2026-08-16) : n'a plus de
     sens depuis le passage de .lb-topbar-right à un flexbox — il se
     réorganise tout seul, plus besoin d'un `right` recalculé à la main. */
  @media (max-width: 700px){ .exif-panel{ width: 100%; } }
  .lb-counter{ color: var(--secondary-text-color); font-family:var(--text-font-family); font-size: var(--fs-md); opacity: 0.6; }
  .lb-crumbs{ font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size: var(--fs-lg); letter-spacing: 0.03em; }
  /* Couleur suit "Textes, légendes et données" (2026-09-04, demande
     explicite) — plus jamais --dust-gold/--bone en dur, comme .lb-counter
     déjà lié à --secondary-text-color. */
  .lb-crumbs button{ background:none; border:none; font-family:inherit; font-size:inherit; color: var(--settings-default-text-color); cursor:pointer; padding:0; text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--settings-default-text-color) 40%, transparent); transition: text-decoration-color .15s ease; }
  .lb-crumbs button:hover{ text-decoration-color: var(--settings-default-text-color); }
  .lb-crumbs .sep{ margin: 0 8px; opacity: 0.4; color: var(--settings-default-text-color); }
  .lb-prev, .lb-next{ position: absolute; top: 50%; transform: translateY(-50%); width: 48px; height: 48px; display:flex; align-items:center; justify-content:center; border-radius: 50%; border: 1px solid color-mix(in srgb, var(--bone) 35%, transparent); background: color-mix(in srgb, var(--ink) 55%, transparent); color: var(--bone); font-size: var(--fs-icon-lg); cursor: pointer; z-index: 10; }
  .lb-prev{ left: 36px; } .lb-next{ right: 36px; }
  /* Cœur + compteur flottants SUR la photo, centrés en bas (façon
     Instagram) — élément du DOM à l'intérieur de .lightbox-media
     (position:relative/absolute selon le cas, voir plus haut).
     Toujours tout en bas de l'écran (bottom:3px fixe), quels que soient
     l'orientation de la photo ou le réglage "remplir l'écran" (2026-08-31,
     demande explicite : "les 3 cas doivent se comporter identiquement").
     Avant cette date, la valeur par défaut réservait l'espace de
     .lb-bottombar (calc(var(--lb-bottom-h)+3px)) sauf dans 3 cas
     particuliers rendus flush par des règles dédiées — .lb-bottombar
     (résolution/Ultra à gauche, zoom à droite, justify-content:
     space-between) laisse de toute façon le CENTRE de cette bande libre,
     là où cette barre reste elle-même centrée (left:50%) : aucun risque de
     chevauchement horizontal réel dans le cas resté "réservé" non plus, la
     distinction n'avait donc plus lieu d'être. --lb-bottom-h reste utilisée
     ailleurs (voir .lightbox-media plus haut) — seul .lb-actions-bar cesse
     de s'y référer.
     RÉGRESSION CORRIGÉE (2026-08-31, "ne reste pas en bas pour les photos
     horizontales") : bottom:3px devenu fixe, mais toujours en
     position:absolute — la valeur "3px" reste relative à SON PROPRE
     CONTENANT POSITIONNÉ (.lightbox-media, parent direct dans le DOM, voir
     ci-dessus), pas à l'écran. Or .lightbox-media change de boîte selon
     l'orientation : PORTRAIT (et niveau 4) = position:relative, 100%/100%
     de #lightbox (donc son propre bas EST déjà le bas de l'écran, bottom:
     3px "juste bien" par coïncidence) ; PAYSAGE en niveau 3 = position:
     absolute, top/bottom INSET de var(--lb-top-h)/var(--lb-bottom-h) (donc
     son propre bas s'arrête BIEN AVANT le bas réel de l'écran — bottom:3px
     y reste alors coincé plus haut, comme rapporté). position:fixed (plutôt
     qu'absolute) ancre directement sur le VIEWPORT, en sautant purement et
     simplement .lightbox-media (ni #lightbox ni .lightbox-media ne posent
     de transform/filter/perspective/will-change qui capturerait un enfant
     fixed) — bottom:3px devient alors réellement le bas de l'écran, dans
     les 3 cas identiquement, sans plus jamais dépendre de la boîte
     changeante de .lightbox-media.
     RÉGRESSION CORRIGÉE (2026-08-31, "les 4 boutons ne répondent plus,
     info-bulles disparues") : cause trouvée — .lb-actions-bar et
     .lb-bottombar partageaient déjà le même z-index (10) AVANT ce
     correctif, sans jamais se chevaucher (l'ancien bottom:calc(var(
     --lb-bottom-h)+3px) réservait justement l'espace de .lb-bottombar).
     bottom:3px fixe les fait maintenant occuper le MÊME rectangle en bas
     de l'écran — à z-index égal, l'ordre du DOM tranche, et .lb-bottombar
     (plus tard dans le DOM, pleine largeur via left:0;right:0, même si
     son propre fond/contenu ne couvre visuellement que les 2 bords) passe
     AU-DESSUS : elle intercepte silencieusement le clic ET le survol
     (donc aussi l'info-bulle) des 4 icônes en dessous, sur toute sa
     largeur — invisible à l'œil (fond transparent au centre) mais bien
     présente pour la détection de pointeur. Zoom saccadé probablement
     causé par le même chevauchement (le geste change de cible en pleine
     manipulation dès que le doigt croise cette zone). z-index remonté à
     11 (au-dessus des 10 partagés par topbar/bottombar/prev/next, sous
     les popups/panneaux ponctuels comme .exif-panel/.lb-share-menu) pour
     que les 4 icônes restent toujours cliquables/survolables, quel que
     soit l'ordre DOM. */
  .lb-actions-bar{ position: fixed; left: 50%; transform: translateX(-50%); bottom: 3px; z-index: 11; display: flex; align-items: center; gap: 10px; }
  /* 12px → 14px (2026-08-16, alignée sur .lb-crumbs/.lb-close) : padding
     4px 10px → 5px 11px pour rester proportionné à la pilule, pas juste
     le texte agrandi dans le même espace. */
  .lb-res-value{ font-family: var(--text-font-family); font-size: calc(var(--fs-lg) * var(--ui-scale)); color: var(--secondary-text-color); opacity: 0.75; background: color-mix(in srgb, var(--ink) 55%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 35%, transparent); border-radius: 12px; padding: 5px 11px; white-space: nowrap; }
  /* Bug corrigé (2026-08-31, "info-bulle plus grisée que celle de loupe+") :
     l'opacité d'un élément s'applique à TOUT son contenu, y compris son
     ::after — un pseudo-élément ne peut pas s'en affranchir (contrairement
     à color, déjà réinitialisé). .lb-force-ultra-btn a lui aussi une
     opacité de repos réduite (0.85) mais remonte à 1 au survol — or
     l'info-bulle n'est visible QUE pendant le survol, elle hérite donc de
     cette opacité PLEINE, jamais celle de repos. .lb-res-value (simple
     badge informatif, jamais pensé comme "survolable" avant l'ajout de
     son info-bulle) n'avait pas cet état :hover. Ajouté ici, comme sur
     tous les autres éléments porteurs d'une info-bulle (vérifié
     systématiquement : fs/EXIF/légendes, like/commentaire/partage/
     enregistrer, crayons, curseurs de réglages — tous l'ont déjà). */
  .lb-res-value:hover{ opacity: 1; }
  /* Pourcentage de zoom courant, à côté de la résolution. .native (posée en
     JS quand le curseur tombe exactement sur le repère "100% réel") le met
     en évidence — gras + jaune. */
  .lb-zoom-value{ font-family: var(--text-font-family); font-size: var(--fs-lg); font-weight: 500; color: var(--secondary-text-color); opacity: 0.75; background: color-mix(in srgb, var(--ink) 55%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 35%, transparent); border-radius: 12px; padding: 5px 11px; white-space: nowrap; transition: color .15s ease; }
  /* Même bug, même correctif que .lb-res-value ci-dessus (2026-08-31). */
  .lb-zoom-value:hover{ opacity: 1; }
  .lb-zoom-value.native{ color: var(--c-or-moyen); font-weight: 700; opacity: 1; border-color: color-mix(in srgb, var(--c-or-moyen) 50%, transparent); }
  /* Icône refaite (2026-08-24, demande explicite) : cercle plus petit
     (40px -> 30px) — l'emoji 🔍➕ est remplacé par un SVG maison (loupe +
     son "+" DANS la lentille, voir index.html) pour un rendu net à
     n'importe quelle taille, cohérent avec les autres icônes du site.
     Fond orange (--ember) retiré (2026-08-30, jugé trop voyant) : reprend
     désormais le même fond neutre translucide que les 4 autres icônes de
     cette barre (like/commentaire/partage/export, voir .lb-like-btn etc.
     ci-dessous), pour ne plus se distinguer visuellement du groupe. */
  .lb-force-ultra-btn{ position: relative; background: color-mix(in srgb, var(--ink) 75%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 35%, transparent); border-radius: 50%; width: 30px; height: 30px; display:flex; align-items:center; justify-content:center; color: var(--bone); cursor: pointer; opacity: 0.85; transition: opacity .15s ease; }
  /* Loupe agrandie SANS changer le cercle (2026-08-25 : 19px -> 24px ;
     2026-08-26, encore un cran : 24px -> 27px, cercle resté à 30px — juste
     assez de marge pour ne pas toucher le bord. Tracé aminci en cohérence
     (stroke-width 2 -> 1.5, voir l'attribut inline dans index.html). */
  .lb-force-ultra-btn svg{ width: 27px; height: 27px; }
  .lb-force-ultra-btn:hover{ opacity: 1; }
  .lb-force-ultra-btn:disabled{ opacity: 0.35; cursor: wait; }
  /* Cœur + badge compteur (2026-08-16), même zone/style que le bouton
     "forcer Ultra" ci-dessus. .liked : rempli en --ember plutôt qu'en
     rouge générique, pour rester dans la palette du site.
     Fond circulaire RÉTABLI (2026-08-21, après une tentative sans fond le
     2026-08-19, elle-même revenue sur une pastille compacte encore avant) :
     cohérent avec les 3 autres icônes (commentaire, partage, export), qui
     n'avaient jamais perdu le leur. Le chiffre à côté (.lb-like-count plus
     bas) reste sans fond, ombre portée pour la lisibilité — lui seul flotte
     directement sur la photo. */
  .lb-like-btn{ position: relative; background: color-mix(in srgb, var(--ink) 75%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 35%, transparent); border-radius: 50%; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; cursor: pointer; opacity: 0.75; color: var(--bone); transition: opacity .15s ease, color .15s ease; }
  .lb-like-btn:hover{ opacity: 1; }
  .lb-like-btn.liked{ opacity: 1; color: var(--ember); }
  .lb-like-btn.liked svg{ fill: currentColor; }
  /* Pastille "non vu" — cœur ET commentaires de la lightbox (voir
     applyLikeState()/refreshCommentState() dans app.js) : un point plein
     sur le bouton, pas un contour, pour rester discret. Le badge des
     vignettes (.like-badge, plus bas) exprime le même signal autrement —
     changement de couleur du badge entier, pas un point séparé — donc pas
     concerné par ce sélecteur. */
  /* ::before (pas ::after), bug corrigé (2026-08-31, "info-bulle J'aime
     collée sur l'icône, police différente") : .lb-like-btn/.lb-comment-btn
     portent aussi un data-tooltip, dont le mécanisme générique
     ([data-tooltip]::after, voir plus bas) utilise DÉJÀ ::after — un même
     élément ne peut avoir qu'UN SEUL ::after, tous ses styles (content,
     position, police...) venant alors de la règle la plus spécifique par
     PROPRIÉTÉ. .lb-like-btn.has-unseen (2 classes) étant plus spécifique
     que [data-tooltip] seul (1 attribut), son "content:''"/position
     remplaçait purement et simplement le texte/l'emplacement de
     l'info-bulle dès qu'un like non vu était présent — visible comme un
     petit point collé au coin du bouton au lieu du texte attendu.
     ::before n'est utilisé nulle part ailleurs sur ces deux boutons :
     aucun risque de collision symétrique. */
  .lb-like-btn.has-unseen::before, .lb-comment-btn.has-unseen::before{
    content: '';
    position: absolute;
    top: 1px;
    right: 1px;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--ember);
    border: 1.5px solid var(--dusk);
  }
  /* Masqué tant qu'aucun like (voir toggle en JS) plutôt qu'un "0" en
     permanence — le badge n'apparaît qu'au premier like, comme les
     réactions WhatsApp/Instagram.
     Badge superposé (2026-08-27, remplace le chiffre flottant à côté de
     l'icône) : incrusté en bas-droite du cercle, façon badge de
     notification — le compteur n'a plus d'existence visuelle séparée du
     cœur/commentaire. Plafonné à "9+" en JS (voir applyLikeState()/
     applyCommentState() dans app.js), donc jamais plus large que ce
     min-width ne le permet. */
  /* border:none (2026-08-27, bug corrigé) : sans reset global des boutons
     sur ce site (voir tête de fichier), ce badge héritait du contour natif
     du navigateur — jamais retiré explicitement ici, contrairement aux
     autres boutons de la lightbox (.lb-close, .lb-like-btn...). */
  .lb-like-count{ position: absolute; bottom: -4px; right: -4px; min-width: 17px; height: 17px; padding: 0 3px; border: none; border-radius: 9px; font-family: var(--text-font-family); font-size: var(--fs-2xs); font-weight: 700; line-height: 17px; text-align: center; color: var(--bone); background: var(--ember); box-sizing: border-box; cursor: pointer; transition: transform .15s ease; }
  .lb-like-count:hover{ transform: scale(1.1); }
  /* Partage et enregistrement (2026-08-16) — même famille visuelle que le
     cœur/compteur, fond cohérent entre les 4 icônes de cette barre. */
  .lb-share-btn, .lb-save-btn{ position: relative; background: color-mix(in srgb, var(--ink) 75%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 35%, transparent); border-radius: 50%; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; cursor: pointer; opacity: 0.75; color: var(--bone); transition: opacity .15s ease; }
  .lb-share-btn:hover, .lb-save-btn:hover{ opacity: 1; }
  .lb-comment-btn{ position: relative; background: color-mix(in srgb, var(--ink) 75%, transparent); border: 1px solid color-mix(in srgb, var(--bone) 35%, transparent); border-radius: 50%; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; cursor: pointer; opacity: 0.75; color: var(--bone); transition: opacity .15s ease; }
  .lb-comment-btn:hover{ opacity: 1; }
  .lb-comment-count{ position: absolute; bottom: -4px; right: -4px; min-width: 17px; height: 17px; padding: 0 3px; border: none; border-radius: 9px; font-family: var(--text-font-family); font-size: var(--fs-2xs); font-weight: 700; line-height: 17px; text-align: center; color: var(--bone); background: var(--ember); box-sizing: border-box; cursor: pointer; transition: transform .15s ease; }
  .lb-comment-count:hover{ transform: scale(1.1); }
  /* Groupe icône+compteur : icône en pastille pleine (cohérente avec
     partage/export, voir plus haut), compteur incrusté en badge sur le
     cercle lui-même (voir .lb-like-count/.lb-comment-count ci-dessus). */
  .lb-counter-group{ position: relative; display: inline-flex; align-items: center; }
  /* Menu de taille avant partage (S/M/L, 2026-08-16) — même schéma que
     .account-widget/.account-menu et .lang-widget/.lang-menu (bouton +
     popup absolu), un cran plus large pour les libellés.
     .lb-export-widget (2026-08-31, "Enregistrer" -> popup S/M/L, demande
     explicite) : même widget, bouton/popup séparés (id différents) pour
     ne jamais entrer dans les sélecteurs JS de .lb-share-widget/.lb-share-
     menu (document.querySelectorAll('.lb-share-menu button') aurait sinon
     déclenché doShare() au lieu de doExport() sur ses propres boutons) —
     seul le CSS visuel est mutualisé, via ces listes de sélecteurs. */
  .lb-share-widget, .lb-export-widget{ position: relative; }
  /* Rétréci (180px -> 110px) et centré (2026-08-28, demande explicite) : le
     libellé "S [1280]" est court, l'ancienne largeur laissait trop d'espace
     sombre vide de chaque côté. */
  .lb-share-menu, .lb-export-menu{ display:none; position:absolute; bottom:calc(100% + 8px); left:50%; transform:translateX(-50%); min-width:110px; background: color-mix(in srgb, var(--night) 97%, transparent); border-radius:8px; box-shadow:0 20px 45px color-mix(in srgb, var(--ink) 35%, transparent); overflow:hidden; z-index:70; flex-direction:column; }
  .lb-share-widget.open .lb-share-menu, .lb-export-widget.open .lb-export-menu{ display:flex; }
  /* Titre du popup (ajouté le 2026-08-31, manquait jusqu'ici — seuls 3
     boutons nus S/M/L s'affichaient sans explication). Renforcé visuellement
     et raccourci le même jour (regénéralisation à l'export) : trop discret
     (11px, opacité 0.6) et "Choisir la taille à partager" s'étalait sur 3
     lignes dans ce popup volontairement étroit (110px, décision explicite
     du 2026-08-28 à ne pas élargir) — texte raccourci ("Taille à
     partager"/"Taille à exporter") plutôt que le popup. white-space normal
     (hérité, pas nowrap) laisse le texte revenir à la ligne plutôt que
     forcer le popup à s'élargir démesurément pour tenir sur une seule
     ligne. */
  .lb-share-menu-title, .lb-export-menu-title{ padding:10px 14px 6px; font-family:var(--text-font-family); font-size:var(--fs-md); font-weight:700; letter-spacing:0.02em; color:var(--dust-gold); opacity:0.9; text-align:center; }
  .lb-share-menu button, .lb-export-menu button{ display:flex; align-items:center; justify-content:center; gap:8px; width:100%; text-align:center; background:none; border:none; color:var(--bone); font-family:var(--text-font-family); font-size:var(--fs-xl); padding:10px 14px; cursor:pointer; }
  .lb-share-menu button:hover, .lb-export-menu button:hover{ background: color-mix(in srgb, var(--bone) 10%, transparent); }
  .lb-share-menu button strong, .lb-export-menu button strong{ flex:0 0 auto; color: var(--dust-gold); }
  /* Tooltip custom (pas title) : délai d'apparition réglable via
     transition-delay — le tooltip natif du navigateur a un délai fixe
     (~1s), trop lent, non personnalisable en CSS. Disparition immédiate au
     survol suivant (delay 0 sur l'état hors-hover). Généralisé une
     première fois le 2026-08-31 (délai réduit .2s → .08s) puis étendu HORS
     de #lightbox le même jour (inventaire complet des info-bulles du
     site) : déclaration commune à TOUTES les icônes du site partageant ce
     mécanisme (visionneuse ET crumbbar/curseur de vignettes, en dehors de
     la visionneuse) — seule la position d'ancrage change ci-dessous selon
     l'endroit de l'écran où vit chaque groupe de boutons. */
  /* Bug corrigé (2026-08-31, "police/graisse pas identiques partout") :
     ::after hérite du contexte de l'élément qui le porte pour TOUTE
     propriété héritable non fixée explicitement ci-dessous — font-weight,
     font-style et text-transform en particulier n'étaient PAS fixés,
     laissant passer ce que l'ancêtre réel imposait (ex. .users-modal-head/
     .comment-editor-head/.exif-head posent text-transform:uppercase pour
     LEUR PROPRE titre — sans réinitialisation ici, l'info-bulle "Fermer" y
     hérite le même TEXT-TRANSFORM et s'affichait "FERMER" ; un <h1> avec
     font-style:italic/line-height:0.98 aurait pareillement contaminé
     l'info-bulle des crayons titre/date). Toutes les propriétés de rendu
     du texte sont désormais fixées explicitement ici, sans exception —
     plus aucune propriété n'est laissée à hériter d'un ancêtre variable. */
  [data-tooltip]::after{
    content: attr(data-tooltip);
    position: absolute;
    background: color-mix(in srgb, var(--ink) 92%, transparent);
    color: var(--bone);
    font-family: var(--text-font-family);
    font-size: var(--fs-xs);
    font-weight: 400;
    font-style: normal;
    font-variant: normal;
    text-transform: none;
    line-height: normal;
    letter-spacing: 0.02em;
    padding: 6px 10px;
    border-radius: 6px;
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transition: opacity .1s ease;
    z-index: 30;
  }
  [data-tooltip]:hover::after{
    opacity: 1;
    transition-delay: .08s;
  }
  /* Mise en page stable (2026-10-04, R9 de Thierry) : une infobulle invisible
     restait dans la mise en page, positionnée hors de son conteneur — elle
     élargissait la zone défilable (panneau Activités +77 px, page +15 à
     20 px) et un léger défilement horizontal faisait bouger le contenu.
     Retirée de la mise en page hors survol ; @starting-style garde le fondu. */
  [data-tooltip]:not(:hover)::after{ display: none; }
  @starting-style { [data-tooltip]:hover::after{ opacity: 0; } }
  /* Infobulle de troncature de grille (2026-09-24, remplace title natif —
     voir applyOverflowTooltips()/showGridOverflowTooltip() dans app.js) :
     UN seul nœud flottant réel (jamais un pseudo-élément [data-tooltip],
     qui serait rogné par le même overflow:hidden que sa cellule d'origine),
     position:fixed calculée en JS — même z-index que les autres éléments
     fixed qui doivent déjà s'échapper de la fenêtre "Mon compte"
     (.users-inline-edit-revert, le menu de wireComboInput()). Police
     légèrement agrandie par rapport aux autres infobulles du site (11px
     -> 12.5px, demande explicite) : celle-ci porte du texte de contenu
     réel à relire, pas juste un libellé de bouton. */
  .grid-overflow-tooltip{
    display:none; position:fixed; z-index:250;
    /* Fond en couleur signature adoucie (2026-09-25, demande explicite —
       "se confond avec le texte des cellules") : mélange d'ember (couleur
       signature du site) et du fond sombre existant, plutôt que le même
       ton neutre que les cellules qu'elle survole — même principe que
       .users-row-selected (rgba(196,98,45,...)), en plus opaque ici
       (nœud flottant solide, pas une teinte de fond superposée). */
    background: color-mix(in srgb, var(--ember) 30%, color-mix(in srgb, var(--ink) 95%, transparent)); color: var(--bone);
    font-family: var(--text-font-family); font-size: var(--fs-lg); font-weight: 400;
    line-height: normal; letter-spacing: 0.02em;
    padding: 7px 11px; border-radius: 6px; white-space: nowrap;
    pointer-events: none; box-shadow: 0 4px 14px color-mix(in srgb, var(--black-pure) 40%, transparent);
    max-width: 60vw; overflow:hidden; text-overflow:ellipsis;
  }
  /* Bug corrigé (2026-08-31, "info-bulles mal alignées sous leur bouton") :
     .lb-fs-btn/.lb-captions-btn/.lb-exif-btn n'avaient aucun position:
     relative propre — leur ::after (position:absolute) remontait alors
     jusqu'au premier ANCÊTRE positionné (bien plus haut que le bouton
     lui-même), plaçant les 3 bulles au MÊME endroit au lieu de sous
     chacune. Même correctif que .lb-force-ultra-btn/.lb-like-btn/etc.
     ci-dessous, qui l'avaient déjà. */
  .lb-fs-btn, .lb-captions-btn, .lb-exif-btn{ position: relative; }
  /* Barre du bas, groupes GAUCHE (résolution + loupe+) et DROITE (zoom) :
     centrées sur leur propre icône (2026-08-31, demande explicite pour
     loupe+, généralisée par cohérence au reste du groupe — même principe
     que partout ailleurs sur le site). Remplace l'ancrage par le bord
     (gauche/droite selon le groupe) posé le même jour plus tôt À CAUSE
     d'un vrai débordement déjà rencontré sur loupe+ le 2026-08-25 — ce
     bouton n'est plus le premier élément du groupe depuis l'ajout de la
     résolution devant lui (~130px du bord, pas ~24px), le risque de
     débordement mesuré aujourd'hui est donc bien moindre qu'à l'époque ;
     resterait à revérifier spécifiquement sur mobile (bottombar plus
     étroite) si un débordement réapparaissait. */
  .lb-bottombar-left [data-tooltip]::after, .lb-bottombar-right [data-tooltip]::after{ bottom: calc(100% + 10px); left: 50%; transform: translateX(-50%); }
  .lb-bottombar-left [data-tooltip], .lb-bottombar-right [data-tooltip]{ position: relative; }
  /* Groupe de droite de la barre du haut (2026-08-31, corrigé le même
     jour : ancré par la droite comme le groupe de gauche, donc cadré à
     droite de chaque icône au lieu d'être centré dessus — bug signalé.
     Plein écran/légendes/EXIF ne sont PAS les éléments les plus proches du
     bord droit de l'écran, contrairement au groupe de gauche/droite de la
     barre du BAS : le widget compte, ajouté après eux dans le DOM, prend
     cette place-là (voir règle dédiée juste en dessous) — rien n'empêche
     donc ces 3 boutons de centrer leur bulle sur eux-mêmes). */
  .lb-topbar-right [data-tooltip]::after{ top: calc(100% + 10px); left: 50%; transform: translateX(-50%); }
  /* Barre d'actions (like/commentaire/partage/export, 2026-08-31) : ce
     groupe est déjà centré à l'écran (.lb-actions-bar, voir plus haut) —
     chaque bouton peut donc centrer sa propre bulle sans risque de
     déborder d'un bord ou l'autre. */
  .lb-actions-bar [data-tooltip]::after{ bottom: calc(100% + 10px); left: 50%; transform: translateX(-50%); }
  /* Compteurs (badges like/commentaire), régression corrigée (2026-08-31) :
     .lb-like-count/.lb-comment-count sont des ÉLÉMENTS POSITIONNÉS À PART
     ENTIÈRE (position:absolute, bottom:-4px;right:-4px SUR EUX-MÊMES, voir
     plus bas) — leur ::after prend donc leur PROPRE boîte (17px de haut,
     décalée de 4px) comme repère, pas celle du bouton like/commentaire à
     côté (40px) : la règle générique ci-dessus leur donnait une bulle 4px
     plus bas et sensiblement décalée à droite (centrée sur leur propre
     petite pastille, pas sur l'icône) que celle du bouton juste à côté —
     contraire à la règle demandée (les 6 bulles du groupe doivent partager
     la même ligne Y, chacune centrée en X sur l'icône qu'elle décrit). Calcul exact (constantes fixes de ce fichier, pas une
     approximation) : bouton 40px de haut, pastille 17px décalée de -4px ->
     37px de "gap" au lieu de 10px annule exactement l'écart de hauteur
     (17 + 37 = 40 + 10 + 4). Pastille ancrée par la droite (right:0, seul
     bord fixe quelle que soit sa largeur variable selon le nombre de
     chiffres) puis recentrée sur l'icône (40px) via un transform mêlant
     un terme en % (annule la propre largeur de la bulle, comme le
     translateX(-50%) générique) et un terme fixe (24px = moitié de 40 moins
     le décalage -4px de la pastille) — fonctionne quel que soit le nombre
     de chiffres affichés, sans dépendre de la largeur réelle de la
     pastille. */
  .lb-actions-bar .lb-like-count[data-tooltip]::after, .lb-actions-bar .lb-comment-count[data-tooltip]::after{
    bottom: calc(100% + 37px);
    left: auto;
    right: 0;
    transform: translateX(calc(50% - 24px));
  }
  /* Crumbbar (2026-08-31, hors visionneuse — niveaux 1/2, ET niveau 3 pour
     le compte, déplacé dans .lb-topbar-right à l'ouverture de la
     visionneuse, voir renderAccountWidget()) : compte + langue vivent tous
     deux près du bord DROIT de l'écran — même ancrage que le groupe
     symétrique de la visionneuse. Bug corrigé le même jour ("avatar pas
     aligné avec le drapeau de langue") : pas un problème d'ancrage ici
     (chacun ancré par la droite, cohérent), mais de hauteur — voir le
     correctif sur .account-btn (hauteur fixée à 40px, comme .lang-btn).
     Régression corrigée le lendemain ("Mon compte" tronquée/débordante à
     droite) : .lb-topbar-right .account-widget [data-tooltip] est niché
     DANS .lb-topbar-right, donc matché par LES DEUX règles à la fois — la
     règle générale (plus haut) posait left:50%+transform:translateX(-50%)
     pour centrer fs/captions/exif ; celle-ci, plus spécifique, ne gagne
     QUE sur top/right (les 2 propriétés qu'elle fixe elle-même), pas sur
     left/transform (qu'elle ne mentionnait pas) — la cascade CSS
     s'applique propriété par propriété, pas règle entière. Résultat : left
     ET right actifs en même temps sur une boîte width:auto -> largeur
     RECALCULÉE de force à la distance entre les deux (~20px au lieu du
     texte réel), texte débordant de cette boîte trop étroite jusqu'à
     sortir de l'écran. left/transform explicitement neutralisés ici pour
     que cette règle possède entièrement son propre positionnement, sans
     rien hériter de la règle générale. */
  .crumbbar [data-tooltip]::after{ top: calc(100% + 10px); right: 0; }
  /* Séparé de la règle crumbbar ci-dessus (2026-08-31, "haut à droite N3/N4 :
     3 boutons + avatar doivent partager le même Y") : dans .lb-topbar-right,
     l'avatar (.account-btn, 40px fixe) est un FRÈRE direct de la pastille
     d'icônes (.lb-topbar-icons, ~32px — fs/EXIF eux-mêmes 20/19px, centrés
     deux fois : dans la pastille, puis la pastille dans la rangée). Calcul
     exact (constantes fixes de ce fichier) : Y d'une icône = son propre bas
     (ROW_CENTER+10) + 10px de marge = ROW_CENTER+20. Y de l'avatar, marge
     identique de 10px = (ROW_CENTER+20, son propre bas) + 10 = ROW_CENTER+30
     — 10px plus bas que les icônes, jamais alignée. Avec 0px de marge ici
     (top:100% pile), l'avatar retombe exactement sur ROW_CENTER+20, comme
     les icônes — coïncidence exacte de ces constantes (40/2 = 20+10-10),
     pas une valeur à ajuster si ces tailles changent un jour. */
  .lb-topbar-right .account-widget [data-tooltip]::after{ top: 100%; right: 0; left: auto; transform: none; }
  .crumbbar [data-tooltip], .lb-topbar-right .account-widget [data-tooltip]{ position: relative; }
  /* Curseur de taille des vignettes (niveaux 1 et 2, hors visionneuse) :
     bug corrigé (2026-08-31) — cette barre (.bottombar) est en réalité
     ÉPINGLÉE tout en bas de l'écran (bottom:0), contrairement à ce
     qu'affirmait ce commentaire à l'origine ("jamais collé à un bord") :
     une bulle apparaissant EN DESSOUS (top:calc(100% + 10px), premier
     réglage) sort donc de l'écran, coupée à moitié. Ancrage inversé,
     au-dessus du curseur, comme les autres info-bulles de cette même
     barre inférieure. */
  .zoom-slider{ position: relative; }
  /* Lot B (2026-10-10) : curseur placé par app.js (placeZoomSlider) sous la
     carte, sur la ligne « itinéraire / Masquer la carte » ; caché tant
     qu'il n'est pas placé (N1 hors administrateur, smartphone). */
  .zoom-slider:not(.is-placed){ display:none; }
  .trip-mini-map-foot .zoom-slider{ padding-right: 0; gap: 6px; margin-right: auto; }
  .trip-mini-map-foot .zoom-slider input[type=range]{ width: 110px; }
  .trip-mini-map-slot .trip-mini-map-foot{ align-self: stretch; }
  .zoom-slider [data-tooltip]::after{ bottom: calc(100% + 10px); left: 50%; transform: translateX(-50%); }
  .zoom-slider [data-tooltip]{ position: relative; }
  /* Simplifié (2026-08-16) : plus besoin de lister .lb-close/.lb-exif-btn/
     .lb-fs-btn/.lb-counter/.lb-crumbs individuellement — tous nichés
     maintenant dans .lb-topbar (voir plus haut), déjà couverts par lui.
     Idem .lb-res-value/.lb-force-ultra-btn/.lb-zoom/.cap-meta via
     .lb-bottombar, et le widget compte (maintenant un vrai enfant DOM de
     .lb-topbar-right, voir openLightbox() dans app.js) via .lb-topbar.
     .lb-close-photo listé explicitement (2026-08-16, bug corrigé : oublié
     à sa création) — contrairement au widget compte/aux autres icônes, il
     vit dans .lightbox-media, qui elle-même ne doit JAMAIS s'estomper (la
     photo doit rester visible pendant l'inactivité), donc pas de parent
     commun à exploiter ici. */
  .lb-topbar, .lb-bottombar, .lb-prev, .lb-next, .lb-actions-bar, .lb-close-photo{ transition: opacity .35s ease; opacity: 1; }
  .lightbox.idle .lb-topbar, .lightbox.idle .lb-bottombar, .lightbox.idle .lb-prev, .lightbox.idle .lb-next, .lightbox.idle .lb-actions-bar, .lightbox.idle .lb-close-photo{ opacity: 0; }
  .lightbox.idle{ cursor: none; }

  /* ---------- ÉCRAN DE DÉBUT/FIN D'ACTIVITÉ (symétriques, même composant .lb-end) ---------- */
  .lb-end{ display:none; flex-direction:column; align-items:center; gap: 20px; color: var(--bone); text-align:center; max-width: 440px; padding: 0 24px; }
  .lightbox:is(.ended, .started) .lb-end{ display:flex; }
  /* .lb-actions-bar et .lb-close-photo n'ont plus besoin d'être listés ici
     (2026-08-16) : ils vivent DANS .lightbox-media (voir plus haut), déjà
     masquée par ce display:none sur son parent. Idem .lb-exif-btn/
     .lb-fs-btn/.lb-counter/.lb-crumbs/.lb-close via .lb-topbar. */
  .lightbox:is(.ended, .started) .lightbox-media, .lightbox:is(.ended, .started) .lb-topbar, .lightbox:is(.ended, .started) .lb-bottombar, .lightbox:is(.ended, .started) .lb-prev, .lightbox:is(.ended, .started) .lb-next, .lightbox:is(.ended, .started) .exif-panel{ display:none !important; }
  .lb-end-title{ font-family:var(--title-font-family); font-style: italic; font-size: var(--fs-end-title); }
  .lb-end-next{ display:flex; flex-direction:column; align-items:center; gap: 12px; cursor:pointer; }
  .lb-end-next img{ width: 220px; height: 150px; object-fit:cover; border-radius: 6px; box-shadow: 0 14px 30px color-mix(in srgb, var(--ink) 40%, transparent); transition: transform .3s ease; }
  .lb-end-next:hover img{ transform: scale(1.03); }
  .lb-end-cta{ font-family:var(--text-font-family); font-size: var(--fs-md); letter-spacing: 0.04em; text-transform: uppercase; color: var(--dust-gold); }
  .lb-end-final{ font-family:var(--text-font-family); font-size: var(--fs-xl); color: var(--bone); opacity: 0.85; }
  .lb-end-back{ font-family:var(--text-font-family); font-size: var(--fs-sm); letter-spacing: 0.05em; text-transform: uppercase; color: var(--bone); background:none; border: 1px solid color-mix(in srgb, var(--bone) 35%, transparent); padding: 10px 18px; border-radius: 20px; cursor:pointer; transition: border-color .15s ease, background .15s ease; }
  .lb-end-back:hover{ border-color: var(--bone); background: color-mix(in srgb, var(--bone) 8%, transparent); }

  /* ---------- Fil smartphone façon Instagram (2026-08-23) ----------
     html.device-phone : classe miroir posée par applyDeviceModeClass()
     (assets/app.js), même idiome que voyage-level/activity-level/no-share —
     une seule expression du seuil tactile+768px, jamais une query CSS
     dupliquée. #diaporamaGrid porte soit .grid (desktop/tablette, inchangé), /* renommage-ok */
     soit .voyage-feed (smartphone, ici) — jamais les deux à la fois. */
  /* background (2026-08-31, système de thèmes) : niveau 5, jusqu'ici sans
     variable dédiée (héritait --canvas-bg de .canvas/body.diaporama-page) — /* renommage-ok */
     voir --feed-bg dans :root. */
  html.device-phone #diaporamaGrid.voyage-feed{ display:block; padding-bottom: 90px; background: var(--feed-bg); } /* renommage-ok */
  .feed-post{ margin-bottom: 26px; }
  .feed-post-head{ padding: 0 2px 8px; font-family:var(--text-font-family); font-size: var(--fs-md); letter-spacing: 0.03em; text-transform: uppercase; color: var(--dusk); }
  /* aspect-ratio (pas 100vh + scroll-snap) : le post suivant est déjà
     partiellement visible en approchant du bas du précédent, sans le
     moindre JS de scroll — c'est tout l'effet "fil continu" demandé. */
  .feed-post-media{ position: relative; width:100%; aspect-ratio: 4/5; border-radius: 8px; overflow:hidden; cursor:pointer; background: var(--dusk); }
  .feed-post-media img{ width:100%; height:100%; object-fit:cover; display:block; }
  .feed-post-actions{ display:flex; align-items:center; gap: 18px; padding: 10px 2px 0; }
  .feed-post-actions button{ background:none; border:none; padding:0; display:flex; align-items:center; gap: 5px; color: var(--dusk-soft); cursor:pointer; }
  .feed-post-actions button svg{ display:block; }
  .feed-like-btn.liked{ color: var(--ember); }
  .feed-like-btn.liked svg{ fill: currentColor; }
  .feed-action-count{ font-family:var(--text-font-family); font-size: var(--fs-md); }
  .feed-action-count:empty, .feed-action-count[hidden]{ display:none; }

  /* Aperçu des photos voisines pendant le glissé carrousel (2026-08-23,
     smartphone uniquement, voir le bloc "Carrousel à glissé suivi" dans
     assets/app.js) — centrée comme #lbFrame (.lightbox-media est centrée
     horizontalement dans .lightbox sur toute la largeur de l'écran, voir
     .lightbox{justify-content:center}), translateX() en JS l'amène depuis
     hors-écran. pointer-events:none : ne doit jamais intercepter le geste
     en cours, purement visuel. */
  .lb-feed-peek{ position:absolute; top:50%; left:50%; max-width:96vw; max-height:80vh; width:auto; height:auto; object-fit:contain; pointer-events:none; z-index:1; display:none; }

  /* Visionneuse simplifiée sur smartphone (2026-08-23) : masque le fil
     d'Ariane, le compteur, le plein écran natif, l'EXIF, la résolution/
     🔍➕ et le curseur de zoom précis + % — laisse #lbClose (dans
     .lb-topbar), .lb-actions-bar et #lbClosePhoto (hors .lb-topbar/
     .lb-bottombar, confirmé dans index.html) intacts. (2026-08-31 :
     .lb-bottombar-center/#lbMeta, lieu/date/n°, retiré du site entier —
     plus rien à masquer ou pas ici, voir projet/journal/INSTRUCTIONS_CLAUDE_CODE.md.) */
  html.device-phone .lightbox .lb-crumbs,
  html.device-phone .lightbox .lb-counter,
  html.device-phone .lightbox .lb-fs-btn,
  html.device-phone .lightbox .lb-captions-btn,
  html.device-phone .lightbox .lb-exif-btn,
  html.device-phone .lightbox .lb-bottombar-left,
  html.device-phone .lightbox .lb-bottombar-right{ display:none; }

  @media (max-width: 900px){ :root{ --cell-min-voyage: 220px; --cell-min-grid: 150px; } header.hero{ padding: 16px 0 12px; } .hero-meta{ margin-top: 8px; gap: 24px; } .controls{ padding: 10px 0 10px; } }
  @media (max-width: 700px){ :root{ --cell-min-voyage: 140px; --cell-min-grid: 100px; --bar-h: 46px; --strip-h: 38px; } .zoom-slider{ display:none; } .crumbs{ font-size: var(--fs-md); } .strip-item{ font-size: var(--fs-sm); padding: 5px 9px; } }
  @media (prefers-reduced-motion: reduce){ figure img, .single img{ transition: none; } }

  /* ---------- ACCUEIL : liste des voyages ---------- */
  .home-hero{ padding: 64px 0 30px; }
  .home-hero .eyebrow{ margin-bottom: 16px; }
  .home-hero h1{ font-size: var(--fs-h1-home); }
  .home-hero p{ font-family:var(--text-font-family); font-size: var(--fs-lg); color: var(--dusk-soft); margin-top: 18px; max-width: 520px; line-height: 1.6; }
  .voyages-grid{ display:grid; gap: 22px; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); padding: 10px 0 90px; }
  .voyage-card{ display:block; text-decoration:none; color:inherit; }
  .voyage-card .cover{ aspect-ratio: 3/2; overflow:hidden; border-radius: 3px; background: var(--dusk); }
  .voyage-card .cover img{ width:100%; height:100%; object-fit:cover; transition: transform .4s ease; }
  .voyage-card:hover .cover img{ transform: scale(1.05); }
  .voyage-card .info{ padding: 13px 2px 0; }
  .voyage-card .num{ font-family:var(--text-font-family); font-size: var(--fs-xs); color: var(--ember); letter-spacing: 0.08em; text-transform: uppercase; }
  .voyage-card .name{ font-family:var(--title-font-family); font-style: italic; font-size: var(--fs-card-title); color: var(--dusk); margin-top: 3px; }
  .voyage-card .meta{ font-family:var(--text-font-family); font-size: var(--fs-xs); color: var(--dusk-soft); margin-top: 4px; text-transform: uppercase; letter-spacing: 0.04em; }

/* Rendu progressif des grandes grilles (2026-09-30, voir
   renderGridRowsProgressively() dans app.js) : ligne sentinelle en bas du
   tableau, visible seulement le temps que le lot suivant se construise. */
.users-modal-table tr.grid-progressive-sentinel td{ padding:10px 12px; font-size:var(--fs-sm); color:var(--settings-default-text-color); opacity:.6; text-align:left; }

/* Suggestion de destination (2026-09-30, Phase 2 — maquette "Destinations
   suggérées") : 🔍 dans la grille Activités, liste flottante, relecture en
   masse dans Intégration. Couleurs dérivées des variables de thème. */
.activities-dest-cell{ display:flex; align-items:center; gap:6px; }
.activities-dest-cell select{ flex:1 1 auto; min-width:0; }
.activities-suggest-btn{ flex:none; position:relative; font-size:var(--fs-sm); line-height:1; padding:4px 7px; border-radius:12px; cursor:pointer;
  background:color-mix(in srgb, var(--ember) 18%, transparent); border:1px solid color-mix(in srgb, var(--ember) 50%, transparent); color:var(--settings-default-text-color); }
.activities-suggest-btn:hover{ border-color:var(--ember); }
.dest-suggest-pop{ position:fixed; z-index:1000; min-width:260px; max-width:440px; padding:8px; border-radius:8px; display:flex; flex-direction:column; gap:4px;
  background:var(--settings-bg); border:1px solid color-mix(in srgb, var(--settings-bg), white 15%); box-shadow:0 10px 28px color-mix(in srgb, var(--black-pure) 35%, transparent);
  font-family:var(--text-font-family); font-size:var(--fs-md); color:var(--settings-default-text-color); }
.dest-suggest-pop-title{ font-size:var(--fs-xs); letter-spacing:.06em; text-transform:uppercase; opacity:.7; padding:2px 6px 4px; }
.dest-suggest-opt{ display:flex; justify-content:space-between; align-items:center; gap:10px; padding:6px 8px; border:0; border-radius:6px; background:transparent;
  color:inherit; font:inherit; text-align:left; cursor:pointer; }
.dest-suggest-opt:hover, .dest-suggest-opt:focus-visible{ background:color-mix(in srgb, var(--settings-default-text-color) 8%, transparent); outline:none; }
.dest-suggest-create{ color:var(--ember); border-top:1px solid color-mix(in srgb, var(--settings-bg), white 12%); border-radius:0 0 6px 6px; }
.dest-suggest-empty{ margin:0; padding:4px 8px; opacity:.7; }
.dest-suggest-why{ opacity:.65; font-size:var(--fs-sm); }
.dest-suggest-chips{ display:inline-flex; gap:4px; flex:none; }
.dest-suggest-chip{ display:inline-flex; align-items:center; padding:2px 8px; border-radius:10px; font-size:var(--fs-xs); white-space:nowrap;
  background:color-mix(in srgb, var(--settings-default-text-color) 10%, transparent); }
.dest-suggest-chip.ok{ background:color-mix(in srgb, var(--c-vert-moyen-4) 22%, transparent); }
.dest-suggest-chip.warn{ background:color-mix(in srgb, var(--c-orange-moyen-7) 22%, transparent); }
.dest-suggest-chip.bad{ background:color-mix(in srgb, var(--c-rouge-moyen-4) 22%, transparent); }
.dest-suggest-chip.new{ background:color-mix(in srgb, var(--ember) 25%, transparent); }
.integration-suggest{ display:flex; flex-direction:column; gap:10px; margin-top:18px; }
.integration-suggest-bar{ align-items:flex-start; }
.integration-suggest-trip{ position:relative; min-width:260px; }
.integration-suggest-summary{ display:flex; flex-wrap:wrap; gap:6px; }
.integration-suggest-table td{ vertical-align:top; white-space:normal; }
.integration-suggest-choices{ display:flex; flex-direction:column; gap:4px; }
.integration-suggest-choice{ display:flex; align-items:center; gap:6px; cursor:pointer; }
.integration-suggest-choice input{ margin:0; accent-color:var(--ember); }
.integration-suggest-choice.create{ color:var(--ember); }

/* Intégration non défilable (2026-09-30, bug signalé par Thierry) :
   .users-table-scroll porte overscroll-behavior:contain (2026-09-02, pour
   qu'un défilement en bout de grille n'entraîne pas la page) — juste dans
   les grilles où ce conteneur EST l'élément qui défile verticalement. Ici,
   les tableaux (résultats d'analyse, relecture des destinations) ne sont
   pas bornés en hauteur : sans rien à faire défiler verticalement, ils
   avalaient quand même la molette, et la section paraissait figée dès que
   la souris était sur un tableau. La molette est rendue au panneau, et la
   relecture reçoit sa propre hauteur maximale (barre "Appliquer" visible). */
.settings-tabpanel[data-tabpanel="integration"] .users-table-scroll{ overscroll-behavior:auto; }
.integration-suggest .users-table-scroll{ max-height:60vh; overflow:auto; }

/* Bloc "Destination + recherche Google Maps" (2026-09-30) — sous la ligne
   concernée (Activités, Destinations). Collé au bord gauche visible du
   tableau (sticky) pour rester lisible quelle que soit la position du
   défilement horizontal. */
tr.dest-geo-row > td{ padding:0 !important; white-space:normal; background:color-mix(in srgb, var(--settings-default-text-color) 4%, var(--settings-bg)); }
.dest-geo-block{ position:sticky; left:0; box-sizing:border-box; width:min(760px, 90vw); display:flex; flex-direction:column; gap:10px; padding:12px 14px;
  font-family:var(--text-font-family); font-size:var(--fs-md); color:var(--settings-default-text-color); }
.dest-geo-fields{ display:flex; flex-wrap:wrap; align-items:flex-end; gap:10px; }
.dest-geo-field{ display:flex; flex-direction:column; gap:4px; flex:1 1 220px; }
.dest-geo-field span{ font-size:var(--fs-xs); opacity:.75; }
.dest-geo-field input{ font:inherit; color:inherit; background:transparent; border:1px solid color-mix(in srgb, var(--settings-bg), white 18%); border-radius:6px; padding:6px 8px; }
.dest-geo-preview{ display:flex; flex-direction:column; gap:6px; }
.dest-geo-result{ display:flex; flex-wrap:wrap; align-items:baseline; gap:8px; cursor:pointer; }
.dest-geo-result input{ accent-color:var(--ember); margin:0; }
.dest-geo-result-name{ font-weight:500; }
.dest-geo-preview a{ color:var(--ember); }
.dest-geo-actions{ display:flex; flex-wrap:wrap; align-items:center; gap:8px; }
.dest-geo-msg{ font-size:var(--fs-sm); opacity:.8; }
/* Zone de message « attention » (2026-10-12, lieu introuvable) : bien
   visible, comme les alertes des grilles — encadré orangé du thème, ⚠,
   texte lisible, choix en boutons. */
.dest-geo-msg:has(.geo-warning){ opacity:1; display:block; }
.geo-warning{ display:flex; gap:10px; align-items:flex-start; margin:6px 0; padding:10px 12px; border:1px solid var(--c-orange-moyen-8); border-radius:6px;
  background:color-mix(in srgb, var(--c-orange-moyen-8) 14%, transparent); color:var(--settings-default-text-color); font-size:var(--fs-md); line-height:1.4; }
.geo-warning-icon{ color:var(--c-orange-moyen-8); font-size:var(--fs-xl); line-height:1; }
.geo-warning-body{ display:flex; flex-direction:column; gap:8px; min-width:0; }
.geo-warning-actions{ display:flex; flex-wrap:wrap; gap:8px; }
.dest-label-cell{ display:flex; align-items:center; gap:4px; }
.dest-label-cell input{ flex:1 1 auto; min-width:0; }
.dest-geo-btn{ flex:none; position:relative; background:transparent; border:0; cursor:pointer; font-size:var(--fs-md); padding:2px 4px; opacity:.75; }
.dest-geo-btn:hover{ opacity:1; }

/* Champ clé API Google (2026-09-30) : champ + œil afficher/masquer. */
.settings-key-field{ display:flex; align-items:center; gap:6px; }
.settings-key-toggle{ position:relative; background:transparent; border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius:6px; padding:4px 7px; cursor:pointer; font-size:var(--fs-md); line-height:1; opacity:.75; }
.settings-key-toggle:hover, .settings-key-toggle[aria-pressed="true"]{ opacity:1; border-color:var(--ember); }

/* Carte intégrée (Maps Embed), boutons Remplacer/Annuler de clé, loupe SVG
   (2026-09-30). */
.dest-geo-map-wrap:empty{ display:none; }
.dest-geo-map{ display:block; width:100%; height:240px; border:0; border-radius:8px; margin-top:4px; }
.settings-key-replace{ align-self:auto; padding:5px 12px; }
.activities-suggest-btn, .dest-geo-btn{ display:inline-flex; align-items:center; justify-content:center; color:var(--settings-default-text-color); }
.activities-suggest-btn svg, .dest-geo-btn svg{ display:block; }

/* Écran Activités réduit + panneau Destination (2026-10-01). Statut calculé
   en texte (pas un bouton) ; couleurs dérivées des tokens existants. */
.activities-status{ font-size:var(--fs-md); letter-spacing:0.02em; white-space:nowrap; }
.activities-status-to_associate{ opacity:0.55; }
.activities-status-to_geocode{ color:var(--c-orange-moyen-8); }
/* Vert clair lisible sur le fond sombre (2026-10-01) — --acacia (#4a5d3a)
   l'était à peine. */
.activities-status-ok{ color:var(--c-vert-moyen-5); }
.activities-cover-cell{ text-align:center; }
.activities-cover-icon{ position:relative; display:inline-block; }
.activities-cover-icon.is-absent{ opacity:0.4; }
/* Panneau Activités en 3 pavés (2026-10-01, maquette validée). */
.activity-pave{ display:flex; flex-direction:column; gap:10px; }
.activity-summary-row{ align-items:flex-end; gap:8px; }
.activity-summary-row .activity-dest-combo{ flex:1 1 auto; min-width:0; position:relative; }
.activity-summary-row .activity-dest-f-status{ flex:0 0 130px; }
.activity-summary-row .activity-dest-f-priority{ flex:0 0 84px; }
.users-modal.account-modal .activity-summary-loupe{ flex:none; height:38px; padding:0 14px; align-self:flex-end; display:inline-flex; align-items:center; justify-content:center; box-sizing:border-box; }
.activity-dest-place-row .activity-dest-f-iso_country{ flex:0 0 80px; }
.activity-dest-flags{ display:flex; flex-wrap:wrap; gap:12px; align-items:center; align-self:flex-end; min-height:38px; font-family:var(--text-font-family); font-size:var(--fs-sm); }
.activity-dest-visit-row{ align-items:flex-end; gap:8px; }
.activity-dest-visit-row .activity-dest-f-year{ flex:0 0 88px; }
.activity-dest-visit-row .activity-dest-f-month{ flex:0 0 64px; }
.activity-geo-coords-row{ gap:8px; }
.activity-geo-coords-row .activity-dest-f-gps_lat, .activity-geo-coords-row .activity-dest-f-gps_lng{ flex:0 0 112px; }
.activity-geo-coords-row .activity-dest-f-google_map_url{ flex:1 1 auto; min-width:0; }
.activity-geo-search-row{ align-items:flex-end; gap:8px; }
.activity-geo-search-row .activity-dest-f-google_search{ flex:1 1 auto; min-width:0; }
.users-modal.account-modal .activity-geo-search-row .settings-save-btn{ height:38px; box-sizing:border-box; display:inline-flex; align-items:center; gap:6px; flex:none; }
.activity-geo-search-row .settings-save-btn:disabled{ opacity:0.4; cursor:not-allowed; }
#activityGeoMsg:empty{ display:none; }
.activity-dest-flag{ display:inline-flex; align-items:center; gap:6px; }
.activity-geo-results{ display:flex; flex-direction:column; gap:6px; }
.activity-geo-results:empty{ display:none; }
.activity-geo-map:empty{ display:none; }
.activity-geo-map .dest-geo-map{ width:100%; height:600px; border:0; border-radius:6px; display:block; }
.activity-geo-actions{ display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.activity-geo-actions .settings-save-btn{ display:inline-flex; align-items:center; gap:6px; }
.activity-geo-actions .settings-save-btn:disabled{ opacity:0.4; cursor:not-allowed; }

/* Listes déroulantes harmonisées en 3 types (2026-10-01, décision de
   Thierry ; GRID_STANDARD §9) — selon leur EMPLACEMENT, sans classe à
   ajouter dans le markup :
   1. filtre (barre d'en-tête de grille) : pastille arrondie, bordure neutre ;
   2. saisie en grille (cellule, champs à autocomplétion compris) : bordure
      fine et discrète, dorée au survol/focus ;
   3. fiche / formulaire : cadre fermé, 96 px minimum.
   Spécificité volontairement supérieure aux règles "données" (0,2,1) et
   "combo autonome" (0,4,1) plus haut. */
.users-modal.account-modal .users-toolbar-filters select,
.users-modal.account-modal .users-toolbar-filters select.admin-select:not(.voyages-theme-select){
  appearance:none; -webkit-appearance:none;
  background-color:color-mix(in srgb, var(--bone) 8%, transparent);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6'%3E%3Cpath d='M1 1l3.5 3.5L8 1' stroke='%23f6f1e6' stroke-width='1.4' fill='none' stroke-linecap='round' stroke-linejoin='round' opacity='0.6'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 12px center;
  border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius:16px; padding:6px 30px 6px 14px;
}
.users-modal.account-modal .users-toolbar-filters select:hover,
.users-modal.account-modal .users-toolbar-filters select:focus{ background-color:color-mix(in srgb, var(--bone) 15%, transparent); border-color:color-mix(in srgb, var(--bone) 40%, transparent); outline:none; }
.users-modal.account-modal .users-modal-table td select,
.users-modal.account-modal .users-modal-table td select.admin-select:not(.voyages-theme-select),
.users-modal.account-modal .users-modal-table td .voyage-combo-wrap input{
  border:1px solid color-mix(in srgb, var(--bone) 12%, transparent); border-radius:4px;
}
.users-modal.account-modal .users-modal-table td select:hover,
.users-modal.account-modal .users-modal-table td select:focus,
.users-modal.account-modal .users-modal-table td select.admin-select:not(.voyages-theme-select):hover,
.users-modal.account-modal .users-modal-table td select.admin-select:not(.voyages-theme-select):focus,
.users-modal.account-modal .users-modal-table td .voyage-combo-wrap input:hover,
.users-modal.account-modal .users-modal-table td .voyage-combo-wrap input:focus{ border-color:var(--dust-gold); outline:none; }
.users-modal.account-modal .modal-field select{
  appearance:none; -webkit-appearance:none;
  min-width:96px;
  background-color:color-mix(in srgb, var(--bone) 8%, transparent);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6'%3E%3Cpath d='M1 1l3.5 3.5L8 1' stroke='%23f6f1e6' stroke-width='1.4' fill='none' stroke-linecap='round' stroke-linejoin='round' opacity='0.6'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 10px center;
  border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius:6px; padding:9px 28px 9px 10px;
}
.users-modal.account-modal .modal-field select:focus{ border-color:var(--dust-gold); outline:none; }

/* Filtres Voyage en saisie filtrée (2026-10-01) : même pastille que les
   filtres <select> (GRID_STANDARD §9). */
.users-modal.account-modal .users-toolbar-filters .filter-combo-input{
  border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius:16px; padding:6px 14px; min-width:220px;
  background:color-mix(in srgb, var(--bone) 8%, transparent);
}
.users-modal.account-modal .users-toolbar-filters .filter-combo-input:hover,
.users-modal.account-modal .users-toolbar-filters .filter-combo-input:focus{ background:color-mix(in srgb, var(--bone) 15%, transparent); border-color:color-mix(in srgb, var(--bone) 40%, transparent); outline:none; }

/* ✓/✕ de la ligne affichés dans l'en-tête du panneau de détail (2026-10-01) :
   même action que les boutons de la ligne (masqués tant que le panneau
   affiche cette ligne). */
.voyages-split-detail-head{ display:flex; align-items:center; justify-content:space-between; gap:10px; }
.panel-row-actions{ display:none; gap:6px; flex:none; }
.panel-row-actions.is-visible{ display:inline-flex; }
.panel-row-confirm-btn, .panel-row-cancel-btn{
  position:relative; width:26px; height:26px; box-sizing:border-box; border-radius:50%; border:1px solid color-mix(in srgb, var(--bone) 30%, transparent);
  background:color-mix(in srgb, var(--night) 95%, transparent); cursor:pointer; font-size:var(--fs-md); line-height:1; padding:0;
  display:inline-flex; align-items:center; justify-content:center;
}
.panel-row-confirm-btn{ color:var(--c-vert-moyen-2); }
.panel-row-confirm-btn:hover{ background:color-mix(in srgb, var(--c-vert-moyen-2) 18%, transparent); }
.panel-row-cancel-btn{ color:var(--c-rouge-moyen-2); }
.panel-row-cancel-btn:hover{ background:color-mix(in srgb, var(--c-rouge-moyen-2) 18%, transparent); }
table.users-modal-table[data-grid-key="trips"] .users-row-actions > button,
table.users-modal-table[data-grid-key="activities"] .users-row-actions > button{ visibility:hidden; }
/* Ligne modifiée qui n'est plus celle du panneau : repère discret (filet
   doré à gauche) — ses ✓/✕ s'affichent en la resélectionnant. */
table.users-modal-table[data-grid-key="trips"] tr.is-dirty:not(.users-row-selected) > td:first-child,
table.users-modal-table[data-grid-key="activities"] tr.is-dirty:not(.users-row-selected) > td:first-child{ box-shadow:inset 3px 0 0 var(--dust-gold); }

/* Hauteurs et menus harmonisés (2026-10-01) : combo éditable (champ à
   autocomplétion) = même hauteur que les <select> du même contexte ; menus
   déroulants natifs sur fond sombre comme les listes des combos éditables. */
.users-modal.account-modal .modal-field select,
.users-modal.account-modal .modal-field input[type="text"],
.users-modal.account-modal .modal-field input[type="number"]{ height:38px; box-sizing:border-box; }
.users-modal.account-modal .users-toolbar-filters select,
.users-modal.account-modal .users-toolbar-filters .filter-combo-input{ height:30px; box-sizing:border-box; }
.users-modal.account-modal .users-modal-table td .voyage-combo-wrap input{ height:24px; box-sizing:border-box; }
.users-modal.account-modal select{ color-scheme:dark; }
.users-modal.account-modal select option,
.users-modal.account-modal select optgroup{ background-color:var(--night); color:var(--bone); }

/* Panneau Activités, largeurs adaptées au contenu (2026-10-01) : la règle
   générale .modal-field-row .modal-field impose 140 px minimum et le retour
   à la ligne — levés ici pour les champs étroits ; boutons alignés sur le bas
   des champs (même marge basse que .modal-field). */
#activityDetailPanel .activity-dest-f-status, #activityDetailPanel .activity-dest-f-priority,
#activityDetailPanel .activity-dest-f-year, #activityDetailPanel .activity-dest-f-month,
#activityDetailPanel .activity-dest-f-gps_lat, #activityDetailPanel .activity-dest-f-gps_lng,
#activityDetailPanel .activity-dest-f-iso_country{ min-width:0; }
#activityDetailPanel .activity-summary-row, #activityDetailPanel .activity-dest-visit-row,
#activityDetailPanel .activity-geo-coords-row, #activityDetailPanel .activity-geo-search-row{ flex-wrap:nowrap; }
#activityDetailPanel .activity-summary-loupe, #activityDetailPanel .activity-geo-search-row .settings-save-btn,
#activityDetailPanel .activity-dest-flags{ margin-bottom:14px; }

/* Menu déroulant sombre (2026-10-01) — remplace le menu natif des <select>
   (ignoré par le CSS sur Mac), même habillage que les listes des combos
   éditables (.trip-summary-search-results). Au-dessus de toute fenêtre. */
.dark-select-menu{ position:fixed; z-index:2147483000; max-height:320px; overflow-y:auto; overscroll-behavior:contain;
  background:var(--night); color:var(--bone); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius:6px; padding:4px 0;
  box-shadow:0 6px 18px color-mix(in srgb, var(--black-pure) 45%, transparent); font-family:var(--text-font-family); font-size:var(--fs-sm); scrollbar-width:thin; }
.dark-select-option{ padding:6px 12px; cursor:pointer; white-space:nowrap; }
.dark-select-option:hover, .dark-select-option.is-active{ background:color-mix(in srgb, var(--bone) 12%, transparent); }
.dark-select-option.is-selected{ color:var(--dust-gold); }
.dark-select-option.is-disabled{ opacity:0.4; cursor:default; }
.dark-select-group{ padding:6px 12px 2px; font-size:var(--fs-xs); letter-spacing:0.04em; text-transform:uppercase; color:var(--dust-gold); opacity:0.9; }

/* Toast (2026-10-01) : confirmations succès/erreur en bas de l'écran,
   par-dessus le contenu, sans jamais décaler la mise en page. */
.settings-toast{ position:fixed; left:50%; bottom:24px; transform:translate(-50%, 20px); z-index:2147482000;
  display:flex; align-items:flex-start; gap:12px; max-width:min(640px, calc(100vw - 32px)); box-sizing:border-box;
  padding:10px 12px 10px 16px; border-radius:8px; box-shadow:0 8px 24px color-mix(in srgb, var(--black-pure) 45%, transparent);
  font-family:var(--text-font-family); font-size:var(--fs-md); line-height:1.45;
  background:var(--c-gris-sombre); color:var(--bone); border:1px solid color-mix(in srgb, var(--bone) 20%, transparent);
  opacity:0; pointer-events:none; transition:opacity .2s ease, transform .2s ease; }
.settings-toast.visible{ opacity:1; transform:translate(-50%, 0); pointer-events:auto; }
.settings-toast.success{ background:var(--c-vert-sombre-2); border-color:var(--c-vert-sombre-3); color:var(--bone); }
.settings-toast.error{ background:var(--c-orange-moyen-3); border-color:var(--c-orange-moyen-5); color:var(--c-orange-sombre); }
.settings-toast-text{ flex:1 1 auto; overflow-wrap:anywhere; }
.settings-toast-action{ flex:none; background:transparent; color:inherit; border:1px solid currentColor; border-radius:4px; padding:2px 10px; font:inherit; cursor:pointer; }
.settings-toast-action[hidden]{ display:none; }
.settings-toast-close{ flex:none; background:transparent; border:none; color:inherit; cursor:pointer; font-size:var(--fs-md); line-height:1; padding:2px 4px; opacity:0.75; }
.settings-toast-close:hover{ opacity:1; }

/* Panneau Activités, retouches (2026-10-01) : en-tête (titre + ✓/✕) figé en
   haut du panneau pendant le défilement ; ligne Année/Mois/famille sur toute
   la largeur ; ↺ collé à la ligne GPS ; "Rechercher" aligné sur le CHAMP
   (pas sur libellé + champ — .settings-save-btn a align-self:flex-start). */
.voyages-split-detail-head{ position:sticky; top:0; z-index:3; background:var(--settings-bg); padding-top:2px; }
#activityDetailPanel .activity-dest-visit-row .activity-dest-flags{ flex:1 1 auto; justify-content:space-around; }
#activityDetailPanel .activity-geo-search-row .settings-save-btn{ align-self:flex-end; }
.activity-geo-clear-btn{ position:relative; flex:none; align-self:flex-end; margin-bottom:14px; width:30px; height:38px; box-sizing:border-box;
  background:transparent; border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius:6px; color:var(--settings-default-text-color); cursor:pointer; font-size:var(--fs-xl); }
.activity-geo-clear-btn:hover:not(:disabled){ border-color:var(--dust-gold); }
.activity-geo-clear-btn:disabled{ opacity:0.35; cursor:not-allowed; }

/* Mini-carte des activités en N2 (2026-10-01) : même emplacement que la
   carte du pays en N1 (à droite du titre), 260×170, agrandie RÉELLEMENT au
   survol (la carte se redimensionne, pas un zoom d'image), vers la gauche
   et le bas, par-dessus le contenu. */
/* Option A (2026-10-01, décision de Thierry) : 480×300 EN PERMANENCE
   (plus d'agrandissement au survol), 320×200 sur écran étroit ; repliable
   (bouton Masquer/Afficher, mémorisé par visiteur). */
.trip-mini-map-slot{ display:none; position:relative; flex:0 0 auto; z-index:52; }
.canvas.activity-level .trip-mini-map-slot.has-map{ display:flex; flex-direction:column; align-items:flex-end; gap:4px; }
.trip-mini-map{ position:relative; width:480px; height:300px; border-radius:8px; overflow:hidden;
  box-shadow:0 2px 10px color-mix(in srgb, var(--dusk) 25%, transparent); background:var(--c-or-clair-2); }
.trip-mini-map-slot.is-collapsed .trip-mini-map{ display:none; }
/* Zoom au survol (2026-10-02, réglage n2_minimap_hover_scale) : le cadre
   s'agrandit par-dessus la page, ancré en haut à droite de son emplacement
   (dont la taille est figée par le script : rien ne se décale). */
.trip-mini-map-slot.is-hover-zoomed .trip-mini-map{ position:absolute; top:0; right:0; z-index:3; transition:width .25s ease, height .25s ease; }
@media (max-width: 1100px){ .trip-mini-map{ width:320px; height:200px; } }
.trip-mini-map-toggle{ background:none; border:none; padding:3px 0; cursor:pointer; color:var(--c-orange-sombre-2); opacity:0.9;
  font:var(--fs-sm)/1.3 var(--text-font-family); }
.trip-mini-map-toggle:hover{ opacity:1; color:var(--c-orange-moyen-2); }
.trip-mini-map-slot.is-collapsed .trip-mini-map-toggle{ border:1px solid color-mix(in srgb, var(--dusk) 30%, transparent); border-radius:12px; padding:4px 10px; opacity:0.9; }
.trip-map-marker{ position:absolute; transform:translate(-50%, -50%); background:none; border:none; padding:0; cursor:pointer; }
/* Marqueurs (2026-10-02, contraste renforcé — décision de Thierry) :
   inactifs à la couleur signature du thème du voyage (--ember), petits,
   liseré fin, légèrement transparents ; actif (survol/sélection) doré vif
   FIXE, plus grand, liseré blanc et halo. */
.trip-map-dot{ display:flex; align-items:center; justify-content:center; box-sizing:border-box; min-width:18px; height:18px; padding:0 4px; border-radius:9px;
  background:var(--ember, var(--c-orange-moyen-2)); border:1px solid color-mix(in srgb, var(--white) 75%, transparent); color:var(--white); font:700 var(--fs-2xs)/1 var(--text-font-family);
  opacity:0.85; box-shadow:0 1px 3px color-mix(in srgb, var(--black-pure) 45%, transparent); transition:transform .15s ease, opacity .15s ease; }
.trip-map-dot.is-wide{ font-size:var(--fs-3xs); }
.trip-map-marker:hover .trip-map-dot{ opacity:1; }
.trip-map-marker.is-active .trip-map-dot{ background:var(--c-or-moyen-2); color:var(--night); min-width:28px; height:28px; border-radius:14px; border:2px solid var(--white); font-size:var(--fs-sm);
  opacity:1; box-shadow:0 0 0 4px color-mix(in srgb, var(--c-or-moyen-2) 35%, transparent), 0 2px 8px color-mix(in srgb, var(--black-pure) 60%, transparent); }
.trip-map-badge{ position:absolute; left:12px; top:-11px; padding:0 4px; border-radius:8px; background:var(--ember, var(--c-orange-moyen-2)); color:var(--bone);
  font:600 var(--fs-2xs)/14px var(--text-font-family); white-space:nowrap; opacity:0.9; }
.trip-map-marker.is-active .trip-map-badge{ left:16px; top:-13px; opacity:1; }
/* Bouton "maintenir pour masquer le tracé" (2026-10-02) : marqueurs
   masqués tant qu'il est enfoncé (le tracé, lui, est masqué côté Google). */
.trip-map-marker.is-held{ visibility:hidden; }
.trip-map-hold{ user-select:none; -webkit-user-select:none; touch-action:none; -webkit-touch-callout:none; }
.trip-map-hold.is-pressed{ background:var(--c-or-moyen-2); color:var(--night); border-color:var(--c-or-moyen-2); }
.trip-map-label{ display:none; position:absolute; left:50%; top:26px; transform:translateX(-50%); white-space:nowrap; padding:2px 6px; border-radius:4px;
  background:color-mix(in srgb, var(--night) 88%, transparent); color:var(--bone); font:var(--fs-xs)/1.3 var(--text-font-family); pointer-events:none; }
.trip-map-marker:hover .trip-map-label, .trip-map-marker.is-active .trip-map-label{ display:block; }
.trip-map-marker.is-active{ z-index:2; }
.trip-map-popup{ position:absolute; z-index:60; min-width:170px; max-width:260px; background:var(--night); color:var(--bone); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent);
  border-radius:6px; padding:4px 0; box-shadow:0 6px 18px color-mix(in srgb, var(--black-pure) 45%, transparent); font:var(--fs-sm)/1.35 var(--text-font-family); }
.trip-map-popup-title{ padding:4px 10px 6px; font-size:var(--fs-xs); letter-spacing:0.04em; text-transform:uppercase; color:var(--c-orange-moyen-9); }
.trip-map-popup-item{ display:block; width:100%; text-align:left; background:none; border:none; color:inherit; font:inherit; padding:6px 10px; cursor:pointer; }
.trip-map-popup-item:hover{ background:color-mix(in srgb, var(--bone) 12%, transparent); }
.trip-map-popup-item.is-current{ color:var(--c-orange-moyen-10); }
body.diaporama-page header.hero.mini-map-open{ z-index:60; } /* renommage-ok */

/* Mini-carte : lien itinéraire Google Maps et ↺ cadrage automatique
   (contrôles Google, donc présents aussi en plein écran). */
.trip-map-ctrl{ margin:6px; background:color-mix(in srgb, var(--night) 85%, transparent); color:var(--bone); border:1px solid color-mix(in srgb, var(--bone) 30%, transparent); border-radius:12px;
  font:var(--fs-xs)/1.2 var(--text-font-family); padding:4px 8px; cursor:pointer; text-decoration:none; }
.trip-map-ctrl:hover{ background:color-mix(in srgb, var(--night) 95%, transparent); color:var(--c-orange-moyen-10); }
.trip-map-reset{ font-size:var(--fs-md); padding:2px 8px; }

/* ID technique de la destination (lecture seule) et résultat de géocodage
   imprécis (2026-10-01). */
/* ID à gauche du champ Destination (pavé Synthèse), aligné sur le champ. */
#activityDetailPanel .activity-dest-id-field{ flex:0 0 auto; min-width:0; }
.activity-dest-id{ display:inline-flex; align-items:center; height:38px; padding:0 4px; font-family:var(--text-font-family); font-size:var(--fs-sm); color:var(--settings-default-text-color); opacity:0.5; }
.geo-imprecise{ color:var(--c-orange-moyen-8); font-size:var(--fs-xs); }

.trip-mini-map-foot{ display:flex; align-items:center; justify-content:flex-end; gap:14px; }
.trip-mini-map-gmaps{ color:var(--c-orange-sombre-2); opacity:0.8; font:var(--fs-sm)/1.3 var(--text-font-family); text-decoration:none; }
.trip-mini-map-gmaps:hover{ opacity:1; text-decoration:underline; }
/* Carte absente (2026-10-10, anomalie signalée par le guide, étape 12) :
   même cadre, même taille, message au centre ; ni Masquer ni itinéraire. */
.trip-map-placeholder{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center; padding:12px; text-align:center;
  color:var(--c-orange-sombre-3); font:var(--fs-md)/1.4 var(--text-font-family); letter-spacing:0.02em; }
.no-map .trip-mini-map-toggle, .no-map .trip-mini-map-gmaps{ display:none; }

/* Carte des activités en N1 (2026-10-01, variante 2 validée) : pleine
   largeur au-dessus de la grille des vignettes, vue relief ; clic sur un
   marqueur = contour doré sur la vignette. */
.trip-map-n1-slot{ display:none; margin:0 0 18px; }
.trip-map-n1-slot.has-map{ display:flex; flex-direction:column; gap:4px; }
.trip-map-n1{ position:relative; width:100%; height:340px; border-radius:10px; overflow:hidden; box-shadow:0 2px 10px color-mix(in srgb, var(--dusk) 25%, transparent); background:var(--c-or-clair-2); }
.trip-map-n1-slot.is-collapsed .trip-map-n1{ display:none; }
.trip-map-n1-slot.is-collapsed .trip-mini-map-toggle{ border:1px solid color-mix(in srgb, var(--dusk) 30%, transparent); border-radius:12px; padding:4px 10px; opacity:0.9; }
/* Vignette(s) liée(s) au marqueur survolé (2026-10-02, option retenue par
   Thierry) : pulsation dorée avec léger agrandissement (×1,06), trois fois,
   puis halo doré fixe tant que le marqueur reste survolé. N1 : vignettes
   d'activité ; N2 : élément du bandeau d'activités. */
@keyframes mapHoverPulse{
  0%{ transform:scale(1); box-shadow:0 0 0 0 color-mix(in srgb, var(--c-or-moyen-2) 90%, transparent); }
  50%{ transform:scale(1.06); box-shadow:0 0 0 8px color-mix(in srgb, var(--c-or-moyen-2) 35%, transparent); }
  100%{ transform:scale(1); box-shadow:0 0 0 3px color-mix(in srgb, var(--c-or-moyen-2) 95%, transparent); }
}
.activity-card .single{ transition:transform .2s ease; }
.activity-card.map-hover .single{ animation:mapHoverPulse .8s ease-in-out 3; box-shadow:0 0 0 3px color-mix(in srgb, var(--c-or-moyen-2) 95%, transparent), 0 10px 24px color-mix(in srgb, var(--ink) 35%, transparent); }
.strip-item.map-hover{ animation:mapHoverPulse .8s ease-in-out 3; box-shadow:0 0 0 3px color-mix(in srgb, var(--c-or-moyen-2) 95%, transparent); border-radius:12px; }
@media (prefers-reduced-motion: reduce){ .activity-card.map-hover .single, .strip-item.map-hover{ animation:none; box-shadow:0 0 0 3px var(--c-or-moyen-2); } }
.activity-step-pill{ display:inline-block; vertical-align:middle; margin-left:12px; padding:3px 9px; border-radius:12px; background:var(--c-orange-moyen-2); color:var(--bone);
  font:500 var(--fs-md)/1 var(--text-font-family); letter-spacing:0.02em; font-style:normal; }


/* Vignette référence / proposition (2026-10-02) : indicateur de
   réconciliation dans la grille Activités et pavé du panneau. */
.activities-cover-rec{ margin-left:4px; font:700 var(--fs-sm)/1 var(--text-font-family); }
.activities-cover-rec.is-diff{ color:var(--c-orange-moyen-8); }
.activities-cover-rec.is-same{ color:var(--settings-default-text-color); opacity:0.55; }
.activity-cover-pave{ display:flex; flex-direction:column; gap:8px; }
/* 2026-10-03 : deux images côte à côte, chacune sur la moitié du panneau,
   au format des vignettes N1 (16/9) ; clic = agrandissement. */
.activity-cover-pair{ display:grid; grid-template-columns:1fr 1fr; gap:12px; }
.activity-cover-fig{ margin:0; display:flex; flex-direction:column; gap:4px; min-width:0; }
.activity-cover-zoom{ display:block; padding:0; border:0; background:none; cursor:zoom-in; width:100%; }
.activity-cover-fig img, .activity-cover-empty{ display:block; width:100%; aspect-ratio:16/9; height:auto; object-fit:cover; border-radius:6px; background:color-mix(in srgb, var(--bone) 6%, transparent); border:1px solid color-mix(in srgb, var(--bone) 15%, transparent); box-sizing:border-box; }
.activity-cover-empty{ display:flex; align-items:center; justify-content:center; text-align:center; padding:6px; font:var(--fs-xs)/1.3 var(--text-font-family); color:var(--settings-default-text-color); opacity:0.6; }
.activity-cover-fig figcaption .activity-cover-file{ overflow-wrap:anywhere; }
.activity-cover-overlay{ position:fixed; inset:0; z-index:100000; background:color-mix(in srgb, var(--c-orange-sombre-4) 88%, transparent); display:flex; align-items:center; justify-content:center; cursor:zoom-out; padding:16px; box-sizing:border-box; }
.activity-cover-overlay img{ max-width:100%; max-height:100%; object-fit:contain; border-radius:4px; box-shadow:0 10px 40px color-mix(in srgb, var(--black-pure) 60%, transparent); }
.activity-cover-fig figcaption{ display:flex; flex-direction:column; gap:2px; font:var(--fs-xs)/1.3 var(--text-font-family); color:var(--settings-default-text-color); }
.activity-cover-role{ text-transform:uppercase; letter-spacing:0.05em; color:var(--settings-label-color); }
.activity-cover-file{ opacity:0.7; word-break:break-all; }
.activity-cover-status{ margin:0; font:var(--fs-sm)/1.4 var(--text-font-family); color:var(--settings-default-text-color); opacity:0.8; }
.activity-cover-status.is-diff{ color:var(--c-orange-moyen-8); opacity:1; }
.activity-cover-apply{ align-self:flex-start; }

/* Guide de parcours de l'écran Intégration (2026-10-02) : prochaine action
   en évidence, puis les 12 étapes et leur état. */
.integration-guide{ display:flex; flex-direction:column; gap:10px; padding:12px 14px; border:1px solid color-mix(in srgb, var(--bone) 18%, transparent); border-radius:8px;
  background:color-mix(in srgb, var(--bone) 4%, transparent); font-family:var(--text-font-family); color:var(--settings-default-text-color); }
.integration-guide:empty{ display:none; }
.ig-head{ display:flex; justify-content:space-between; align-items:baseline; gap:12px; flex-wrap:wrap; font-size:var(--fs-md); }
.ig-title b{ color:var(--settings-label-color); }
.ig-progress{ font-size:var(--fs-sm); opacity:0.75; }
.ig-note{ margin:0; font-size:var(--fs-sm); opacity:0.8; }
.ig-next{ display:flex; flex-direction:column; gap:8px; padding:10px 12px; border-radius:6px; border-left:3px solid var(--ember); background:color-mix(in srgb, var(--c-orange-moyen-2) 12%, transparent); }
.ig-next.is-finished{ border-left-color:var(--c-vert-moyen-6); background:color-mix(in srgb, var(--c-vert-moyen-6) 12%, transparent); }
.ig-next-title{ font-size:var(--fs-md); font-weight:600; }
.ig-next-title .ig-where{ font-weight:400; }
.ig-next-body{ display:flex; flex-wrap:wrap; align-items:center; gap:8px; font-size:var(--fs-sm); line-height:1.5; }
.ig-next-body > :not(button):not(a):not(label){ flex-basis:100%; }
.ig-btn{ font-size:var(--fs-sm); text-decoration:none; }
.ig-code{ display:block; flex-basis:100%; padding:6px 8px; border-radius:4px; background:color-mix(in srgb, var(--black-pure) 35%, transparent); font:var(--fs-sm)/1.4 var(--text-font-family); white-space:pre-wrap; word-break:break-all; user-select:all; }
.ig-sub{ display:block; flex-basis:100%; font-size:var(--fs-xs); opacity:0.75; }
.ig-field{ display:inline-flex; align-items:center; gap:6px; font-size:var(--fs-sm); }
.ig-field input{ width:48px; text-transform:lowercase; background:color-mix(in srgb, var(--bone) 8%, transparent); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius:4px; padding:4px 6px; color:inherit; font:inherit; }
.ig-steps{ list-style:none; margin:0; padding:0; display:grid; grid-template-columns:repeat(auto-fill, minmax(330px, 1fr)); gap:6px 18px; align-items:start; }
.ig-step{ display:grid; grid-template-columns:26px 1fr auto; column-gap:6px; row-gap:1px; align-items:baseline; align-content:start; padding:3px 0; font-size:var(--fs-sm); }
.ig-step > *{ grid-column:2 / 4; }
.ig-step > .ig-num{ grid-column:1; grid-row:1; }
.ig-step > .ig-label{ grid-column:2; grid-row:1; }
.ig-step > .ig-state{ grid-column:3; grid-row:1; }
.ig-num{ display:inline-flex; align-items:center; justify-content:center; width:21px; height:21px; border-radius:11px; font-size:var(--fs-xs); font-weight:700;
  border:1px solid color-mix(in srgb, var(--bone) 35%, transparent); box-sizing:border-box; }
/* Icône de statut sous la pastille (2026-10-03, demande de Thierry). */
.ig-step > .ig-icon{ grid-column:1; grid-row:2 / span 2; align-self:start; justify-self:center; width:21px; text-align:center; font:700 var(--fs-2xl)/1.15 system-ui, sans-serif; margin-top:2px; }
.ig-icon.k-done{ color:var(--ig-ok); }
.ig-icon.k-current, .ig-icon.k-running{ color:var(--ember); }
.ig-icon.k-warn{ color:var(--ig-warn); }
.ig-icon.k-error{ color:var(--ig-error); }
.ig-icon.k-wait{ color:var(--settings-default-text-color); }
.ig-icon.k-interrupted{ color:var(--c-orange-moyen-8); }
.ig-label{ font-weight:500; }
.ig-where{ font-size:var(--fs-xs); opacity:0.6; }
.ig-state{ font-size:var(--fs-xs); white-space:nowrap; opacity:0.85; }
.ig-detail{ font-size:var(--fs-xs); opacity:0.7; }
.ig-inline-action{ display:flex; flex-wrap:wrap; align-items:center; gap:6px; font-size:var(--fs-xs); line-height:1.45; color:var(--c-orange-moyen-11); }
.ig-inline-action .ig-sub{ color:inherit; }
.ig-step.is-done .ig-num{ background:var(--c-vert-moyen-6); border-color:var(--c-vert-moyen-6); color:var(--c-vert-sombre-4); }
.ig-step.is-done .ig-state{ color:var(--c-vert-moyen-7); }
.ig-step.is-current .ig-num{ background:var(--ember); border-color:var(--ember); color:var(--white); }
.ig-step.is-current .ig-state, .ig-step.is-current .ig-label{ color:var(--ember); }
.ig-step.is-warn .ig-num{ border-color:var(--c-orange-moyen-11); color:var(--c-orange-moyen-11); }
.ig-step.is-warn .ig-state{ color:var(--c-orange-moyen-11); }
.ig-step.is-todo{ opacity:0.75; }
/* « ⏳ en attente » (2026-10-03) : étape qui attend une étape précédente, neutre. */
.ig-step.is-wait{ opacity:0.55; }
/* « ✕ erreur » (2026-10-03, demande de Thierry) : rouge, mêlé à la couleur de
   texte du thème — plus clair sur un fond sombre, plus foncé sur un fond
   clair (lisible dans les deux, voir --ig-error). */
.integration-guide{ --ig-error: color-mix(in srgb, #d63c3c 72%, var(--settings-default-text-color));
  /* Vert et orange des icônes (2026-10-03) : même mélange, lisibles sur fond clair. */
  --ig-ok: color-mix(in srgb, #6fae55 70%, var(--settings-default-text-color)); --ig-warn: color-mix(in srgb, #d98c2b 75%, var(--settings-default-text-color)); }
.ig-step.is-error .ig-num{ background:var(--ig-error); border-color:var(--ig-error); color:var(--settings-bg); }
.ig-step.is-error .ig-state, .ig-step.is-error .ig-label{ color:var(--ig-error); }
.ig-step.is-error .ig-detail{ color:var(--ig-error); opacity:1; }
.ig-step.is-error .ig-inline-action{ color:var(--ig-error); }
.ig-next.is-error{ border-left-color:var(--ig-error); background:color-mix(in srgb, var(--ig-error) 14%, transparent); }
.ig-next-title.is-error{ color:var(--ig-error); }
.ig-progress-errors{ color:var(--ig-error); }

/* Statut d'examen des vignettes (2026-10-02, maquette "processus Vignette"). */
.activities-review-badge{ display:inline-block; padding:1px 8px; border-radius:10px; font:var(--fs-xs)/1.5 var(--text-font-family); white-space:nowrap; }
.activities-review-badge.is-a_examiner{ border:1px solid var(--c-orange-moyen-8); color:var(--c-orange-moyen-8); }
/* États de la vignette (2026-10-03) : vert = choisie, bleu = choix humain
   dans le voyage, gris = vignette de l'ancien site, rouge = aucune. */
.activities-review-badge.is-acceptee, .activities-review-badge.is-auto{ border:1px solid var(--c-vert-moyen-6); color:var(--c-vert-moyen-7); }
.activities-review-badge.is-refusee{ border:1px solid var(--c-or-moyen-3); color:var(--c-or-moyen-4); }
.activities-review-badge.is-voyage{ border:1px solid var(--c-bleu-moyen); color:var(--c-bleu-clair); }
.activities-review-badge.is-ancien{ border:1px solid color-mix(in srgb, var(--c-gris-moyen) 60%, transparent); color:var(--settings-default-text-color); opacity:0.8; }
.activities-review-badge.is-aucune{ border:1px solid var(--c-rouge-moyen-5); color:var(--c-rouge-moyen-6); font-weight:700; }
.activity-cover-status.is-acceptee, .activity-cover-status.is-auto{ color:var(--c-vert-moyen-7); opacity:1; }
.activity-cover-status.is-refusee{ color:var(--c-or-moyen-4); opacity:1; }
.activity-cover-status.is-voyage{ color:var(--c-bleu-clair); opacity:1; }
.activity-cover-status.is-aucune{ color:var(--c-rouge-moyen-6); opacity:1; }
/* Étape 5 du guide (2026-10-03) : liste des vignettes, activités sans
   vignette en tête et en évidence. */
.ig-cover-list ul{ margin:4px 0 0; padding-left:18px; }
.ig-cover-list li{ margin:2px 0; }
.ig-cover-list li.is-missing{ color:var(--c-rouge-moyen-6); font-weight:700; }
[data-ig-cover-state]{ display:flex; flex-wrap:wrap; gap:4px 8px; align-items:baseline; }
.activities-review-cell{ font-size:var(--fs-sm); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:0; }
.activities-cover-rec.is-diff.is-reviewed{ color:var(--settings-default-text-color); opacity:0.5; }
.activity-cover-head{ display:flex; justify-content:flex-end; }
.activity-cover-detect{ margin:0; font:var(--fs-xs)/1.4 var(--text-font-family); color:var(--settings-default-text-color); opacity:0.75; }
.activity-cover-status.is-reviewed{ color:var(--c-vert-moyen-7); opacity:1; }
.activity-cover-actions{ display:flex; gap:8px; flex-wrap:wrap; }
.activity-cover-comment-field textarea{ width:100%; box-sizing:border-box; resize:vertical; font:var(--fs-sm)/1.4 var(--text-font-family); }

/* Titre d'activité multi-lignes (2026-10-02) : retour à la ligne affiché
   (N2 et vignettes N1) ; champ d'édition en place. */
#pageTitleMain, .activity-card .activity-title, .feed-post-name{ white-space:pre-line; }
.activity-title-editor{ display:block; width:min(100%, 900px); box-sizing:border-box; resize:none; overflow:hidden; font:inherit; color:inherit;
  line-height:1.15; background:color-mix(in srgb, var(--white) 35%, transparent); border:1px dashed currentColor; border-radius:6px; padding:2px 8px; }

/* Message de progression de l'Intégration plus visible (2026-10-02,
   demande de Thierry) : plus grand, couleur signature, collé en haut de la
   zone qui défile pour rester visible pendant tout le traitement. */
.settings-tabpanel[data-tabpanel="integration"] #integrationModalMsg.visible:not(.error):not(.success){
  position:sticky; top:0; z-index:6; font-size:var(--fs-xl); font-weight:600; padding:12px 16px;
  background:color-mix(in srgb, var(--ember) 28%, var(--settings-bg, var(--night))); border:1px solid var(--ember); border-left:5px solid var(--ember);
  color:var(--bone); box-shadow:0 4px 14px color-mix(in srgb, var(--black-pure) 35%, transparent); }

/* Guide persistant (2026-10-02, maquette v5) : état enregistré, mentions
   d'écart, « Rejouer », détail des messages, rattachement des dossiers. */
.ig-badges{ display:flex; gap:8px; flex-wrap:wrap; }
.ig-badge{ padding:2px 10px; border-radius:12px; font-size:var(--fs-xs); background:color-mix(in srgb, var(--bone) 8%, transparent); color:var(--settings-default-text-color); opacity:0.85; }
.ig-badge.is-warn{ border:1px solid var(--c-orange-moyen-8); color:var(--c-orange-moyen-8); background:none; opacity:1; }
.ig-step > .ig-drift, .ig-step > .ig-row-actions, .ig-step > .ig-messages{ grid-column:2 / 4; }
.ig-drift{ margin-top:3px; padding:3px 8px; border:1px dashed var(--c-orange-moyen-8); border-radius:6px; color:var(--c-orange-moyen-8); font-size:var(--fs-xs); line-height:1.4; }
.ig-row-actions{ display:flex; gap:10px; align-items:center; margin-top:2px; }
.ig-replay, .ig-detail-toggle{ background:none; border:none; padding:2px 0; font:var(--fs-xs) var(--text-font-family); color:var(--dust-gold, var(--c-orange-moyen-9)); cursor:pointer; text-decoration:underline; text-underline-offset:2px; }
.ig-replay:hover, .ig-detail-toggle:hover{ color:var(--c-orange-moyen-10); }
.ig-messages{ margin:4px 0 0; padding:8px 10px; max-height:260px; overflow:auto; border-radius:6px; background:color-mix(in srgb, var(--black-pure) 35%, transparent); font:var(--fs-xs)/1.55 var(--text-font-family); white-space:pre-wrap; color:var(--settings-default-text-color); }
.ig-step.is-running .ig-num{ background:var(--ember); border-color:var(--ember); color:var(--white); }
.ig-step.is-running .ig-state{ color:var(--ember); }
.ig-step.is-interrupted .ig-num{ border-color:var(--c-orange-moyen-8); color:var(--c-orange-moyen-8); }
.ig-step.is-interrupted .ig-state, .ig-step.is-interrupted .ig-label{ color:var(--c-orange-moyen-8); }
.ig-replay-panel{ display:flex; flex-direction:column; gap:8px; padding:12px 14px; border:2px solid var(--ember); border-radius:8px; background:color-mix(in srgb, var(--c-orange-moyen-2) 8%, transparent); font-size:var(--fs-sm); line-height:1.5; }
.ig-replay-panel p{ margin:0; }
.ig-panel-title{ font-size:var(--fs-md); font-weight:600; }
.ig-panel-actions{ display:flex; gap:8px; flex-wrap:wrap; }
.ig-btn.is-primary, .settings-save-btn.is-primary{ background:var(--ember); border-color:var(--ember); color:var(--white); }
.integration-attach{ display:flex; flex-direction:column; gap:10px; padding:14px 16px; border:2px solid var(--c-orange-moyen-8); border-radius:8px; background:color-mix(in srgb, var(--c-orange-moyen-8) 6%, transparent); font-family:var(--text-font-family); font-size:var(--fs-sm); color:var(--settings-default-text-color); }
.integration-attach[hidden]{ display:none; }
.integration-attach p{ margin:0; }
.ig-attach-missing{ color:var(--c-orange-moyen-8); }
.ig-attach-table td{ vertical-align:middle; white-space:normal; }
.ig-attach-select{ min-width:320px; }
.ig-attach-ambiguous{ color:var(--c-orange-moyen-8); }
.visually-hidden{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }

/* Dossier du serveur déjà occupé par une autre page (2026-10-02). */
.ig-deploy-warn{ display:block; flex-basis:100%; padding:6px 10px; border:1px solid var(--c-orange-moyen-8); border-radius:6px; color:var(--c-orange-moyen-8); font-size:var(--fs-sm); font-weight:600; }

/* N0 — voyage désactivé visible du seul administrateur (2026-10-03) :
   atténué, en pointillés, avec un badge « Désactivé » — dans style.css
   (chargé par N0) plutôt que dans family/index.php, envoyé à part. */
.n0-admin-only{ opacity:0.6; }
.n0-admin-only:hover{ opacity:0.85; }
.n0-recent-item.n0-admin-only{ border-style:dashed; }
.n0-result-item.n0-admin-only{ outline:1px dashed color-mix(in srgb, var(--dusk) 30%, transparent); outline-offset:-2px; }
.n0-disabled-badge{
  display:inline-block; vertical-align:middle; margin-left:6px;
  font-family:var(--text-font-family); font-size:var(--fs-xs); font-weight:400; text-transform:uppercase;
  letter-spacing:0.05em; color:var(--dusk-soft, var(--c-gris-moyen-2)); border:1px dashed currentColor;
  border-radius:20px; padding:1px 7px;
}

/* Champs de chemin (Système > Environnement, 2026-10-03) : 4 fois plus longs
   qu'un champ texte ordinaire, alignés à droite comme les autres contrôles ;
   la ligne passe à la ligne (champ sous son libellé) quand la place manque. */
.settings-row.settings-row-path{ flex-wrap:wrap; }
.settings-row-path .settings-row-text{ flex:1 1 260px; min-width:0; }
.settings-path-field{ display:flex; align-items:center; gap:8px; flex:0 1 780px; min-width:0; margin-left:auto; justify-content:flex-end; }
.settings-path-field input.admin-select{ flex:1 1 600px; width:640px; min-width:0; max-width:100%; box-sizing:border-box; font-family:var(--text-font-family); }
.settings-path-field .settings-save-btn{ flex:none; align-self:center; white-space:nowrap; }
/* Bordures et fonds dérivés de la couleur de texte du thème : visibles sur un
   fond clair comme sur un fond sombre. */
.settings-path-field input.admin-select, .settings-path-field .settings-save-btn{
  border-color: color-mix(in srgb, var(--settings-default-text-color) 32%, transparent);
  background: color-mix(in srgb, var(--settings-default-text-color) 6%, transparent);
  color: var(--settings-default-text-color);
}
.settings-path-field input[readonly]{ cursor:text; }

/* « détail » d'une étape du guide (2026-10-03) : compact, bilan en tête,
   échecs puis avertissements, journal complet replié. */
.ig-step > .ig-detail-panel{ grid-column:2 / 4; }
.ig-detail-panel{ display:flex; flex-direction:column; gap:6px; margin-top:4px; padding:8px 10px; border-radius:6px;
  background:color-mix(in srgb, var(--settings-default-text-color) 5%, transparent); font-size:var(--fs-xs); line-height:1.45; }
.ig-detail-summary{ font-size:var(--fs-sm); }
.ig-detail-group ul{ margin:3px 0 0; padding-left:16px; }
.ig-detail-group li{ margin:2px 0; }
.ig-detail-group.is-error{ color:var(--ig-error); }
.ig-detail-group.is-warn{ color:var(--c-orange-moyen-11); }
.ig-issue-where{ color:var(--settings-default-text-color); }
.ig-issue-action, .ig-issue-tech{ display:block; color:var(--settings-default-text-color); opacity:0.75; }
.ig-issue-tech{ font-size:var(--fs-xs); opacity:0.55; }
.ig-journal summary{ cursor:pointer; opacity:0.75; }
.ig-journal .ig-messages{ max-height:240px; overflow:auto; margin:4px 0 0; }
.ig-badge-err{ color:var(--ig-error); }
/* « ✕ 1 » de « détail » en rouge (2026-10-03) : hors du soulignement doré du bouton. */
.ig-detail-toggle .ig-badge-err, .ig-detail-toggle .ig-badge-warn{ display:inline-block; text-decoration:none; font-weight:700; }
.ig-detail.is-error{ color:var(--ig-error); opacity:1; }
.ig-badge-warn{ color:var(--c-orange-moyen-11); }

/* Fenêtre « Mon compte » : bordures et fonds des champs et boutons dérivés
   de la couleur de texte du thème (2026-10-03, demande de Thierry : presque
   invisibles sur un fond clair). Sur le thème sombre, rendu identique aux
   anciennes valeurs rgba(246,241,230,…). Les boutons principaux
   (.is-primary), actifs et désactivés gardent leurs propres règles. */
.users-modal.account-modal{
  --ctl-border: color-mix(in srgb, var(--settings-default-text-color) 27%, transparent);
  --ctl-border-hover: color-mix(in srgb, var(--settings-default-text-color) 42%, transparent);
  --ctl-bg: color-mix(in srgb, var(--settings-default-text-color) 7%, transparent);
  --ctl-bg-hover: color-mix(in srgb, var(--settings-default-text-color) 14%, transparent);
}
.users-modal.account-modal .admin-select,
.users-modal.account-modal select.admin-select:not(.voyages-theme-select),
.users-modal.account-modal .settings-number-input,
.users-modal.account-modal .users-search-solo,
.users-modal.account-modal .users-field-row input,
.users-modal.account-modal .modal-field input,
.users-modal.account-modal .modal-field textarea,
.users-modal.account-modal .modal-field select,
.users-modal.account-modal .users-toolbar-filters select,
.users-modal.account-modal .users-toolbar-filters input,
.users-modal.account-modal .ig-field input,
.users-modal.account-modal .settings-save-btn:not(.is-primary):not(:disabled):not(.active),
.users-modal.account-modal .users-action-bar button:not(:disabled),
.users-modal.account-modal .users-action-bar > label{
  border-color: var(--ctl-border); background-color: var(--ctl-bg);
}
.users-modal.account-modal .settings-save-btn:not(.is-primary):not(:disabled):not(.active):hover,
.users-modal.account-modal .users-action-bar button:not(:disabled):hover,
.users-modal.account-modal .users-action-bar > label:hover,
.users-modal.account-modal .users-toolbar-filters select:hover{
  border-color: var(--ctl-border-hover); background-color: var(--ctl-bg-hover);
}
.users-modal.account-modal .admin-select:focus,
.users-modal.account-modal .settings-number-input:focus,
.users-modal.account-modal .users-search-solo:focus,
.users-modal.account-modal .modal-field :is(input, textarea, select):focus{ border-color: var(--ember); outline:none; }
.users-modal.account-modal .tech-info-input{ border-bottom-color: var(--ctl-border); }
.users-modal.account-modal .tech-info-input:focus{ border-bottom-color: var(--ember); }
.users-modal.account-modal .switch input:not(:checked) + .switch-track{ background: color-mix(in srgb, var(--settings-default-text-color) 22%, transparent); }
.users-modal.account-modal .settings-slider-track input[type="range"]{ background: color-mix(in srgb, var(--settings-default-text-color) 35%, transparent); }
.users-modal.account-modal .settings-slider-tick{ background: color-mix(in srgb, var(--settings-default-text-color) 40%, transparent); }
/* « Nombre de photos affiché sur N0 » : assez large pour « automatique » et « 22 473 ». */
.users-modal.account-modal #n0PhotoCountInput{ width:120px; }
.ig-issue-stage{ opacity:0.75; }

/* Fenêtre de réglages détachée (2026-10-03) : voyage concerné dans l'en-tête,
   bandeau quand la fenêtre principale affiche un autre voyage. */
.detached-trip-label{ font-weight:400; opacity:0.75; }
.detached-trip-banner{ display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin:0 0 8px; padding:8px 12px; border-left:3px solid var(--ember); background:color-mix(in srgb, var(--ember) 14%, transparent); font-size:var(--fs-sm); }

/* Flèche des listes déroulantes (2026-10-03, demande de Thierry : trop
   claire sur fond clair) : dessinée dans la couleur du texte du thème au
   lieu d'un trait crème fixe — lisible en thème clair comme en sombre.
   Deux demi-carrés en dégradé = chevron ; positions reprises de chaque règle. */
.users-modal.account-modal select,
.users-modal.account-modal select.admin-select:not(.voyages-theme-select),
.users-modal.account-modal .users-toolbar-filters select,
.users-modal.account-modal .users-toolbar-filters select.admin-select:not(.voyages-theme-select),
.users-modal.account-modal .modal-field select,
.theme-font-picker-trigger{
  --select-arrow: color-mix(in srgb, var(--settings-default-text-color) 70%, transparent);
  background-image: linear-gradient(45deg, transparent 50%, var(--select-arrow) 50%), linear-gradient(135deg, var(--select-arrow) 50%, transparent 50%);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.users-modal.account-modal select{ background-position: right 7px center, right 2px center; }
.users-modal.account-modal select.admin-select:not(.voyages-theme-select){ background-position: right 15px center, right 10px center; }
.theme-font-picker-trigger{ background-position: right 13px center, right 8px center; }
.users-modal.account-modal .users-toolbar-filters select,
.users-modal.account-modal .users-toolbar-filters select.admin-select:not(.voyages-theme-select){ background-position: right 17px center, right 12px center; }
.users-modal.account-modal .modal-field select{ background-position: right 15px center, right 10px center; }

/* Géolocalisation proposée d'office (2026-10-03) : un seul lieu fiable,
   aperçu et bouton « Appliquer cette position ». */
.geo-proposal{ display:flex; flex-wrap:wrap; align-items:center; gap:6px 10px; }
.geo-proposal .dest-geo-map-wrap{ flex-basis:100%; }
.geo-cache-note{ font-size:var(--fs-xs); opacity:0.75; }

/* Pavé Synthèse du panneau Activités (2026-10-03, bouton « + ») : deux
   rangées — ID et Destination sur toute la largeur, puis Statut, Priorité,
   « + » et loupe (le champ Destination était écrasé à quelques pixels). */
#activityDetailPanel .activity-summary-row{ flex-wrap:wrap; row-gap:0; }
#activityDetailPanel .activity-summary-row .activity-dest-combo{ flex:1 1 calc(100% - 90px); }
#activityDetailPanel .activity-summary-row .activity-dest-f-status{ flex:1 1 130px; }

/* Association automatique des destinations (2026-10-03, point 14). */
.activities-status-to_validate{ color:var(--c-orange-moyen-11); font-weight:600; }
.activities-status-auto{ font-size:var(--fs-2xs); opacity:0.6; margin-left:4px; }
.activity-dest-review.is-to-validate{ display:flex; flex-wrap:wrap; align-items:center; gap:8px; padding:6px 10px; border-left:3px solid var(--c-orange-moyen-11); background:color-mix(in srgb, var(--c-orange-moyen-11) 12%, transparent); font-size:var(--fs-sm); margin:6px 0; }

.activities-status-no_destination{ opacity:0.7; font-style:italic; }
.activities-status-gps_to_validate{ color:var(--c-orange-moyen-11); }

/* Indicateurs du pavé Vignette (2026-10-03, maquette validée) : vert, orange,
   rouge mêlés à la couleur du texte (lisibles en clair comme en sombre). */
.activity-cover-pave{ --cv-ok: color-mix(in srgb, #6fae55 70%, var(--settings-default-text-color)); --cv-warn: color-mix(in srgb, #d98c2b 75%, var(--settings-default-text-color)); --cv-bad: color-mix(in srgb, #d63c3c 72%, var(--settings-default-text-color)); }
.activity-cover-verdict{ margin:0; display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 8px; font-size:var(--fs-sm); line-height:1.4; padding:6px 8px; border-radius:6px; border-left:3px solid currentColor; }
.activity-cover-verdict.is-ok{ color:var(--cv-ok); background:color-mix(in srgb, var(--cv-ok) 10%, transparent); }
.activity-cover-verdict.is-warn{ color:var(--cv-warn); background:color-mix(in srgb, var(--cv-warn) 10%, transparent); }
.activity-cover-verdict.is-bad{ color:var(--cv-bad); background:color-mix(in srgb, var(--cv-bad) 10%, transparent); }
.activity-cover-verdict.is-none{ color:var(--settings-default-text-color); opacity:0.75; }
.activity-cover-when{ font-size:var(--fs-xs); opacity:0.75; color:var(--settings-default-text-color); }
.activity-cover-help{ margin-left:auto; display:inline-flex; align-items:center; justify-content:center; width:18px; height:18px; border-radius:50%; border:1px solid currentColor; font:700 var(--fs-xs)/1 system-ui, sans-serif; cursor:help; }
.activity-cover-gauges{ display:flex; flex-direction:column; justify-content:center; gap:10px; min-width:0; }
.cover-gauge{ display:grid; grid-template-columns:1fr auto; gap:3px 6px; font:var(--fs-xs)/1.2 var(--text-font-family); color:var(--settings-default-text-color); }
.cover-gauge-label{ grid-column:1 / 3; opacity:0.8; }
.cover-gauge-track{ height:8px; border-radius:4px; background:color-mix(in srgb, var(--settings-default-text-color) 14%, transparent); overflow:hidden; align-self:center; }
.cover-gauge-fill{ display:block; height:100%; border-radius:4px; }
.cover-gauge.is-ok .cover-gauge-fill{ background:var(--cv-ok); } .cover-gauge.is-ok .cover-gauge-value{ color:var(--cv-ok); }
.cover-gauge.is-warn .cover-gauge-fill{ background:var(--cv-warn); } .cover-gauge.is-warn .cover-gauge-value{ color:var(--cv-warn); }
.cover-gauge.is-bad .cover-gauge-fill{ background:var(--cv-bad); } .cover-gauge.is-bad .cover-gauge-value{ color:var(--cv-bad); }
.cover-gauge-value{ font-weight:700; white-space:nowrap; }
.activity-cover-fig{ background:none; box-shadow:none; border-radius:0; aspect-ratio:auto; }
/* Comparaison Référence / Proposition (2026-10-03) : image entière (la
   vignette de Setup/ n'est pas au format 16/9), légendes du fichier original. */
.activity-cover-pave .activity-cover-fig img{ object-fit:contain; }
.activity-cover-caption{ display:flex; flex-direction:column; gap:2px; }
.activity-cover-info{ opacity:0.85; }
.activity-cover-info.is-missing{ color:var(--cv-warn); opacity:1; }
.activity-cover-pave .activity-cover-gauges{ flex-direction:row; flex-wrap:wrap; gap:8px 16px; }
.activity-cover-pave .activity-cover-gauges .cover-gauge{ flex:1 1 160px; }
.activity-cover-pave.is-compact{ gap:6px; min-width:340px; }
.activity-cover-pave.is-compact .activity-cover-verdict{ font-size:var(--fs-xs); padding:4px 6px; }
.activity-cover-pave.is-compact .activity-cover-pair{ grid-template-columns:repeat(2, minmax(0, 240px)); }
/* Tableau du scan (2026-10-03) : colonnes N°, Activité, Photos resserrées,
   la comparaison des vignettes prend le reste. */
#integrationScanResults table[data-grid-key="integration"] th:nth-child(1){ width:56px; }
#integrationScanResults table[data-grid-key="integration"] th:nth-child(2){ width:200px; }
#integrationScanResults table[data-grid-key="integration"] th:nth-child(3){ width:70px; }
#integrationScanResults table[data-grid-key="integration"] td{ vertical-align:top; }
/* Position proposée, non appliquée (2026-10-04, panneau Activités) */
.activity-geo-results .geo-proposal{ flex-direction:column; align-items:stretch; gap:4px; padding:8px 10px; border:1px dashed var(--c-orange-moyen-8); border-radius:6px; }
.geo-proposal-title{ color:var(--c-orange-moyen-8); }
.geo-proposal-line{ font-size:var(--fs-sm); }
.geo-proposal-choices{ display:flex; flex-direction:column; gap:4px; }
/* Panneau Activités en deux zones (2026-10-04, maquette de Thierry) :
   Activité (dont la Vignette) / Destination (Association, Fiche,
   Géolocalisation) — cadre, titre et teinte propres à chaque objet. */
.activity-zone{ display:flex; flex-direction:column; gap:12px; padding:10px 12px 12px; border:1px solid var(--zone-color); border-radius:8px; background:color-mix(in srgb, var(--zone-color) 7%, transparent); }
.activity-zone-activity{ --zone-color:#6a9fc0; }
.activity-zone-dest{ --zone-color:#7fae6a; }
.activity-zone-dest.is-creating{ --zone-color:#ff7a1a; border-width:2px; }
.activity-zone-title{ font:var(--fs-sm)/1.4 var(--text-font-family); letter-spacing:0.08em; text-transform:uppercase; color:var(--zone-color); }
.activity-zone-dest.is-creating .activity-zone-title{ font-size:var(--fs-md); }
.activity-zone-link{ align-self:center; font-size:var(--fs-sm); opacity:0.7; margin:-4px 0; }
.activity-facts{ display:flex; flex-wrap:wrap; gap:4px 16px; font-size:var(--fs-sm); }
.activity-fact-label{ opacity:0.65; }
.activity-dest-similar{ display:flex; flex-wrap:wrap; align-items:center; gap:6px; }
.activity-dest-similar:empty{ display:none; }
.activity-dest-similar-title{ font-size:var(--fs-sm); opacity:0.8; }
.activity-dest-id-line{ margin:0; font-size:var(--fs-sm); opacity:0.7; }
.activity-dest-id-line .activity-dest-id{ height:auto; padding:0; }
.activity-dest-status-row{ gap:8px; align-items:flex-end; flex-wrap:wrap; }
/* Fiche de la destination (2026-10-09, lot D) : ID / Libellé (le plus
   large) / Type / Pays sur une ligne ; Statut / Priorité / Année / Mois /
   famille sur la suivante. Pays en lecture seule. */
.activity-dest-main-row{ gap:8px; align-items:flex-end; flex-wrap:nowrap; }
.activity-dest-main-row .activity-dest-f-id{ flex:0 0 auto; min-width:44px; }
.activity-dest-main-row .activity-dest-f-label{ flex:2.5 1 0; min-width:0; }
.activity-dest-main-row .activity-dest-f-type{ flex:1.2 1 0; min-width:0; }
#activityDetailPanel .activity-dest-main-row .activity-dest-f-type{ overflow:hidden; }
#activityDetailPanel .activity-dest-main-row .activity-dest-f-type select{ width:100%; min-width:0; max-width:100%; }
.activity-dest-main-row .activity-dest-f-label input{ width:100%; min-width:0; box-sizing:border-box; }
.activity-dest-main-row .activity-dest-f-country_name{ flex:1 1 0; min-width:0; }
.activity-dest-country{ display:flex; align-items:center; height:38px; font-size:var(--fs-md); opacity:0.8; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
#activityDetailPanel .activity-dest-status-row .activity-dest-f-status{ flex:0 0 112px; }
#activityDetailPanel .activity-dest-status-row .activity-dest-f-priority{ flex:0 0 62px; }
#activityDetailPanel .activity-dest-status-row .activity-dest-f-year{ flex:0 0 70px; }
#activityDetailPanel .activity-dest-status-row .activity-dest-f-month{ flex:0 0 54px; }
#activityDetailPanel .activity-dest-status-row .modal-field input, #activityDetailPanel .activity-dest-status-row .modal-field select{ width:100%; min-width:0; box-sizing:border-box; }
/* Pavé Activité repliable (2026-10-09, lot D). */
.activity-zone-toggle{ all:unset; cursor:pointer; font:var(--fs-sm)/1.4 var(--text-font-family); letter-spacing:0.08em; text-transform:uppercase; color:var(--zone-color); }
.activity-zone-toggle:focus-visible{ outline:1px solid var(--zone-color); }
.combo-pinned-action{ font-weight:600; border-bottom:1px solid color-mix(in srgb, var(--c-gris-moyen-3) 30%, transparent); }
.activity-geo-open-link{ align-self:flex-end; margin-bottom:14px; text-decoration:none; font-size:var(--fs-2xl); }
.activities-pending-counter{ font-size:var(--fs-sm); color:var(--c-orange-moyen-8); margin-left:auto; white-space:nowrap; }
.activities-pending-counter + .users-selection-summary{ margin-left:12px; }
/* Association : recherche, 🔍 et « + » toujours sur une ligne. */
#activityDetailPanel .activity-summary-row{ flex-wrap:nowrap; }
#activityDetailPanel .activity-summary-row .activity-dest-combo{ flex:1 1 auto; min-width:0; }
/* Pas de rebond ni de défilement parasite (2026-10-04, R20 de Thierry,
   règle de toutes les pages) : une zone défilante s'arrête net en butée,
   sans rebond élastique (trackpad Mac, iPad) ; le geste ne se propage jamais
   à la page ni au conteneur parent (ni au « retour arrière » du navigateur
   par balayage horizontal). Une zone sans rien à défiler ne bouge pas. */
html, body { overscroll-behavior: none; }
/* Pas de règle « * » (R32, 2026-10-05) : appliquée aux champs de saisie des
   cellules (eux-mêmes défilants), elle bloquait le geste parti d'un champ au
   lieu de le passer à la grille — les grilles larges ne défilaient plus.
   Seules les zones défilantes (style ci-dessus, overscroll-behavior: none) et
   la page l'ont. */
/* Tout copier (2026-10-04, R18) : en haut à droite du détail d'une étape et du journal. */
.ig-detail-panel, .integration-log-wrap{ position:relative; }
.ig-copy-btn{ position:absolute; top:4px; right:4px; z-index:2; padding:2px 10px; font-size:var(--fs-xs); }
.integration-log-wrap .ig-copy-btn{ display:none; }
.integration-log-wrap:has(#integrationLog[style*="block"]) .ig-copy-btn{ display:inline-flex; }
.ig-detail-panel > .ig-detail-summary{ padding-right:110px; }
/* R19 (2026-10-04) : les lignes de la boîte de relance ne sont jamais coupées à droite. */
.ig-next, .ig-next-body, .ig-next-body > *{ min-width:0; }
.ig-sub{ white-space:normal; overflow-wrap:anywhere; max-width:100%; }
/* Pavé Vignette compact (2026-10-05, R13) : informations à gauche, vignette
   actuelle à droite, taille fixe. */
.activity-cover-compact{ display:flex; gap:12px; align-items:flex-start; }
.activity-cover-compact-info{ flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:4px; font-size:var(--fs-sm); }
.activity-cover-compact-info .activity-cover-status{ margin:0; }
.activity-cover-current{ flex:0 0 160px; margin:0; }
.activity-cover-current img{ width:160px; height:auto; display:block; border-radius:4px; }
.activity-cover-current .activity-cover-zoom{ padding:0; border:0; background:none; cursor:zoom-in; }
.activity-cover-detect{ opacity:.75; }
.activity-cover-revoir{ align-self:flex-start; background:none; border:0; padding:0; color:var(--ember); cursor:pointer; font:inherit; font-size:var(--fs-sm); }
.activity-cover-comment-field textarea{ width:100%; box-sizing:border-box; }
/* Mosaïque des vignettes de l'écran Intégration (2026-10-05, R23) : deux
   groupes ; carte ouverte sur deux colonnes, carte réglée sur une ;
   rangement sans trous ; « ▸ Revoir » agrandit la carte sur place. */
.ig-cover-group{ margin:10px 0; }
.ig-cover-group-title{ font-size:var(--fs-md); margin:6px 0; }
.ig-cover-unlinked{ margin:4px 0 0; padding-left:20px; font-size:var(--fs-sm); }
details.ig-cover-group > summary{ cursor:pointer; }
.ig-cover-wrap{ container-type:inline-size; }
.ig-cover-mosaic{ display:grid; grid-template-columns:repeat(auto-fill, minmax(260px, 1fr)); grid-auto-flow:dense; gap:10px; container-type:inline-size; }
.ig-card{ display:flex; flex-direction:column; gap:6px; min-width:0; padding:8px 10px; border:1px solid color-mix(in srgb, var(--bone) 15%, transparent); border-radius:6px; font-size:var(--fs-sm); }
.ig-card.is-open, .ig-card.is-expanded{ grid-column:span 2; }
@container (max-width: 540px){ .ig-card.is-open, .ig-card.is-expanded{ grid-column:auto; } }
.ig-card.is-error{ border-color:var(--ig-error); }
.ig-card-head{ display:flex; gap:6px; align-items:baseline; }
.ig-card-num, .ig-card-count{ opacity:.6; }
.ig-card-count{ margin-left:auto; }
.ig-card-compact{ display:flex; gap:10px; align-items:flex-start; }
.ig-card-info{ flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:4px; }
.ig-card-current{ flex:0 0 120px; margin:0; }
.ig-card-current img{ width:120px; height:auto; display:block; border-radius:4px; }
/* Carte réglée allégée (2026-10-07, R36) : la vignette seule, pleine largeur. */
.ig-card.is-settled > .ig-card-current{ flex:none; width:100%; }
.ig-card.is-settled > .ig-card-current img{ width:100%; max-height:200px; object-fit:contain; }
.ig-cover-mosaic{ align-items:start; }
/* hidden respecté (R36) : .activity-cover-pave (display:flex) l'emportait,
   la comparaison « Revoir » / « Détails » restait toujours dépliée. */
.ig-card-full[hidden], .activity-cover-full[hidden]{ display:none; }
.ig-card-legend{ display:flex; flex-direction:column; gap:2px; }
.ig-card .ig-card-revoir{ align-self:flex-start; }
.ig-card-current .activity-cover-zoom{ padding:0; border:0; background:none; cursor:zoom-in; }
.ig-card-current .activity-cover-empty{ width:120px; min-height:80px; font-size:var(--fs-xs); }
.ig-cover-comment{ width:100%; box-sizing:border-box; font-size:var(--fs-sm); }
.trip-map-ctrl.trip-map-save[hidden]{ display:none; }
.trip-map-ctrl.trip-map-save{ font:var(--fs-sm)/1.2 var(--text-font-family); padding:4px 8px; }

/* ✓ ✕ d'un champ de réglage (2026-10-07, R31 point 13). */
.settings-field-confirm{ display:inline-flex; gap:4px; margin-left:6px; vertical-align:middle; }
.settings-field-confirm[hidden]{ display:none; }
.settings-field-confirm .users-row-confirm-btn, .settings-field-confirm .users-row-cancel-btn{ display:inline-flex; }
.activity-fact-field{ display:inline-flex; align-items:center; gap:6px; }
.activity-fact-field input.admin-select, .activity-fact-field select.admin-select{ font-size:var(--fs-sm); padding:2px 6px; min-width:0; }
.activity-fact-field input[type=number]{ width:64px; }
.activity-fact-field .activity-panel-name{ width:240px; max-width:100%; }
/* R46 (2026-10-07) : zones défilantes restées sans overscroll-behavior depuis
   R32 (la règle « * » les couvrait) — le panneau de détail des grilles
   (Activités, Voyages) rebondissait au glissé horizontal du trackpad.
   Le panneau ne défile jamais horizontalement (overflow-y:auto rendait
   overflow-x « auto » implicite) : rien n'y bouge de côté. */
.voyages-split-detail{ overflow-x:hidden; overscroll-behavior:none; }
.trip-summary-content, .integration-log, .ig-messages, .account-submenu, .share-bulk-file-list,
.logs-message-inner.expanded, .theme-font-picker-menu, .trip-summary-search-results{ overscroll-behavior:none; }

/* Calcul en cours du guide Intégration (2026-10-07, R43). */
.ig-settle{ position:sticky; top:0; z-index:4; display:flex; flex-direction:column; gap:4px; margin:0 0 8px; padding:6px 10px; border-radius:6px; background:color-mix(in srgb, var(--ember) 22%, var(--settings-bg)); font-size:var(--fs-sm); }
.ig-settle-bar{ display:block; height:3px; border-radius:2px; background:linear-gradient(90deg, transparent 0%, var(--ember) 40%, transparent 80%) 0 0 / 200% 100%; animation:ig-settle 1.1s linear infinite; }
@keyframes ig-settle{ from{ background-position:100% 0; } to{ background-position:-100% 0; } }
.ig-step.is-checking .ig-state{ opacity:.7; font-style:italic; }

/* Recherche globale (2026-10-07, R12). */
.global-search{ position:relative; padding:10px 18px 0; }
.global-search input{ width:100%; box-sizing:border-box; font-size:var(--fs-lg); padding:7px 10px; }
.global-search-results{ position:absolute; left:18px; right:18px; top:calc(100% + 4px); z-index:60; max-height:min(70vh, 560px); overflow-y:auto; overscroll-behavior:none;
  background:var(--settings-bg); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius:8px; box-shadow:0 12px 30px color-mix(in srgb, var(--black-pure) 45%, transparent); padding:6px; }
.global-search-results[hidden]{ display:none; }
.gs-group + .gs-group{ border-top:1px solid color-mix(in srgb, var(--bone) 12%, transparent); margin-top:4px; padding-top:4px; }
.gs-group-title{ font:600 var(--fs-xs)/1.6 var(--text-font-family); letter-spacing:.06em; text-transform:uppercase; opacity:.75; padding:2px 6px; }
.gs-count{ opacity:.8; font-weight:400; }
.gs-item{ display:flex; flex-wrap:wrap; align-items:baseline; gap:2px 8px; width:100%; text-align:left; background:none; border:none; color:inherit; padding:5px 8px; border-radius:5px; cursor:pointer; font:inherit; }
.gs-item:hover, .gs-item.is-active{ background:color-mix(in srgb, var(--c-orange-moyen-2) 22%, transparent); }
.gs-name{ font-size:var(--fs-md); }
.gs-sub{ flex-basis:100%; font-size:var(--fs-xs); opacity:.65; }
.gs-item mark{ background:color-mix(in srgb, var(--c-orange-moyen-3) 45%, transparent); color:inherit; border-radius:2px; }
.gs-approx{ font-size:var(--fs-2xs); padding:0 6px; border:1px solid currentColor; border-radius:8px; opacity:.7; }
.gs-more{ background:none; border:none; color:var(--ember); cursor:pointer; padding:3px 8px; font-size:var(--fs-sm); }
.gs-none{ padding:8px; font-size:var(--fs-sm); opacity:.8; }

.gs-coverage{ margin:6px 6px 2px; font-size:var(--fs-xs); opacity:.6; border-top:1px solid color-mix(in srgb, var(--bone) 12%, transparent); padding-top:4px; }

/* ↻ des champs de recherche (2026-10-08, R51). */
.srw{ position:relative; display:inline-flex; min-width:0; }
.srw > input{ width:100%; box-sizing:border-box; }
.srw > input{ padding-left:36px !important; }
.srw.srw-solo{ display:block; margin-bottom:10px; }
.srw.srw-solo > input{ margin-bottom:0; }
.sessions-action-bar .srw-solo, .help-search-bar .srw-solo{ flex:1; margin-bottom:0; }
.users-toolbar-filters .srw{ flex:1; min-width:160px; }
.users-toolbar-filters .srw > input{ flex:1; min-width:0; }
.global-search .srw{ display:block; }
.srw-btn{ position:absolute; left:4px; top:50%; transform:translateY(-50%); z-index:1; width:28px; height:28px; padding:0; border:none; border-radius:50%;
  background:color-mix(in srgb, var(--bone) 12%, transparent); color:inherit; font-size:var(--fs-icon); line-height:28px; cursor:pointer; }
/* R56 : pas de ✕ natif dans les champs de recherche (on efface en vidant le texte). */
input[type="search"]::-webkit-search-cancel-button{ -webkit-appearance:none; appearance:none; display:none; }
.srw-btn:hover{ background:color-mix(in srgb, var(--c-orange-moyen-2) 35%, transparent); }
.srw-btn[hidden]{ display:none; }
/* ✕ qui vide le champ (2026-10-11 soir, remplace « plus de ✕ » de R56). */
.srw > input{ padding-right:32px !important; }
.srw-clear{ position:absolute; right:6px; top:50%; transform:translateY(-50%); z-index:1; width:22px; height:22px; padding:0; border:none; border-radius:50%;
  background:transparent; color:inherit; opacity:.6; font-size:var(--fs-sm); line-height:22px; cursor:pointer; }
.srw-clear:hover{ opacity:1; background:color-mix(in srgb, var(--bone) 12%, transparent); }
.srw-clear[hidden]{ display:none; }

/* Guide d'Intégration : badge de mode et description en infobulle (2026-10-08, R49). */
.ig-mode{ display:inline-block; margin-left:6px; padding:0 7px; border-radius:9px; font:600 var(--fs-2xs)/16px var(--text-font-family); letter-spacing:.04em; vertical-align:1px; }
.ig-mode.is-auto{ background:color-mix(in srgb, var(--c-vert-moyen-2) 22%, transparent); color:var(--c-vert-moyen-8); }
.ig-mode.is-here{ background:color-mix(in srgb, var(--c-bleu-moyen-2) 22%, transparent); color:var(--c-bleu-clair-2); }
.ig-mode.is-manual{ background:color-mix(in srgb, var(--c-orange-moyen-3) 24%, transparent); color:var(--c-or-clair-3); }
.ig-label-text[data-tooltip]{ position:relative; cursor:help; }
.ig-label-text[data-tooltip]::after{ white-space:normal; width:340px; left:0; right:auto; top:calc(100% + 6px); bottom:auto; transform:none; text-transform:none; letter-spacing:0; line-height:1.4; }

/* Filtre global (2026-10-08, R12 redéfini). */
.gf-chip{ display:inline-flex; align-items:center; gap:4px; margin:4px 6px; padding:2px 4px 2px 10px; border-radius:12px; font-size:var(--fs-sm);
  background:color-mix(in srgb, var(--ember) 28%, transparent); border:1px solid color-mix(in srgb, var(--ember) 60%, transparent); white-space:nowrap; }
.gf-chip[hidden]{ display:none; }
.gf-chip-clear{ background:none; border:none; color:inherit; cursor:pointer; font-size:var(--fs-sm); padding:0 6px; }
/* Image du voyage sur N0 (2026-10-08, R50) : case carrée, photo ou icône de
   secours (emoji du continent sur fond teinté). */
.n0-trip-img{ flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center; overflow:hidden; border-radius:8px;
  background:color-mix(in srgb, var(--ember, var(--c-orange-moyen-2)) 18%, transparent); }
.n0-trip-img.is-72{ width:72px; height:72px; font-size:var(--fs-6xl); }
.n0-trip-img.is-64{ width:64px; height:64px; font-size:var(--fs-5xl); }
.n0-trip-img img{ width:100%; height:100%; object-fit:cover; display:block; }

/* Étape 11 : choix rapide de l'image du voyage (2026-10-08, R50). */
.ig-trip-image-pick{ display:flex; flex-direction:column; gap:4px; margin-top:4px; }
.ig-trip-image-grid{ display:flex; flex-wrap:wrap; gap:6px; }
.ig-trip-image-opt{ position:relative; width:56px; height:56px; padding:0; border:2px solid transparent; border-radius:6px; overflow:hidden; background:color-mix(in srgb, var(--bone) 8%, transparent); cursor:pointer; }
.ig-trip-image-opt img{ width:100%; height:100%; object-fit:cover; display:block; }
.ig-trip-image-opt span{ position:absolute; left:2px; bottom:1px; font-size:var(--fs-2xs); color:var(--white); text-shadow:0 0 3px var(--black-pure); }
.ig-trip-image-opt.is-current{ border-color:var(--ember); }

/* Chantier Photos (2026-10-08) : panneau de détail d'une photo (latéral,
   par-dessus la grille ou la visionneuse), grille de l'onglet Photos,
   réglages Système, barre d'information de N2. Rien n'est créé tant que le
   chantier est inactif. */
.pm-panel{ position:fixed; top:0; right:0; bottom:0; width:460px; max-width:100vw; z-index:1100; display:flex; flex-direction:column;
  background: color-mix(in srgb, var(--settings-bg) 98%, transparent); color: var(--settings-value-color); border-left:1px solid color-mix(in srgb, var(--settings-bg), white 15%);
  box-shadow: -8px 0 24px color-mix(in srgb, var(--black-pure) 35%, transparent); font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size:calc(var(--fs-md) * var(--ui-scale, 1)); overscroll-behavior:none; outline:none; }
.pm-panel.is-wide{ width:820px; }
.pm-head{ display:flex; align-items:center; gap:6px; padding:10px 12px; border-bottom:1px solid color-mix(in srgb, var(--settings-bg), white 10%); }
.pm-title{ flex:1; min-width:0; font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.pm-pending{ color: var(--ember); white-space:nowrap; }
.pm-icon{ background:none; border:1px solid color-mix(in srgb, var(--settings-bg), white 18%); color:inherit; border-radius:4px; min-width:26px; height:26px; cursor:pointer; font-size:var(--fs-md); }
.pm-icon.pm-ok{ color:var(--c-vert-moyen-9); } .pm-icon.pm-cancel{ color: var(--ember); }
.pm-summary{ display:flex; gap:10px; padding:10px 12px; border-bottom:1px solid color-mix(in srgb, var(--settings-bg), white 8%); }
.pm-summary img{ width:96px; height:96px; object-fit:cover; border-radius:4px; flex:none; }
.pm-summary-text{ line-height:1.55; color: var(--settings-label-color); }
.pm-filled{ display:flex; align-items:center; gap:6px; padding:8px 12px; cursor:pointer; }
.pm-review{ margin:0 12px 8px; padding:6px 8px; border-left:3px solid var(--ember); background: color-mix(in srgb, var(--ember) 12%, transparent); }
.pm-msg{ margin:0 12px 8px; color:var(--c-vert-moyen-9); } .pm-msg.is-error{ color: var(--ember); }
.pm-loading{ padding:16px; }
.pm-body{ flex:1; overflow-y:auto; overscroll-behavior:none; padding:0 12px 16px; }
.pm-cat > summary{ cursor:pointer; padding:8px 0; font-weight:600; color: var(--settings-label-color); }
.pm-count{ font-weight:normal; opacity:.7; }
.pm-field{ display:grid; grid-template-columns: 150px 1fr; gap:2px 8px; align-items:center; padding:3px 0 3px 6px; border-left:3px solid transparent; }
.pm-field.is-override{ border-left-color: var(--ember); }
.pm-field.is-pending{ border-left-color:var(--c-orange-moyen); }
.pm-k{ color: var(--settings-label-color); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.pm-v{ min-width:0; font:inherit; color:inherit; background: color-mix(in srgb, var(--settings-bg), white 5%); border:1px solid color-mix(in srgb, var(--settings-bg), white 14%); border-radius:3px; padding:3px 6px; }
.pm-v.pm-ro{ border-color:transparent; overflow-wrap:anywhere; }
.pm-tools{ grid-column:2; display:flex; flex-wrap:wrap; gap:6px; align-items:center; }
.pm-tools:empty{ display:none; }
.pm-badge{ color: var(--ember); font-size:var(--fs-xs); }
.pm-hint{ font-size:var(--fs-xs); opacity:.7; }
.pm-mini{ background:none; border:1px solid color-mix(in srgb, currentColor 45%, transparent); color:inherit; border-radius:3px; font:inherit; font-size:var(--fs-xs); padding:1px 6px; cursor:pointer; }
.pm-hist-list{ grid-column:2; display:flex; flex-direction:column; gap:2px; }
.pm-hist-item{ text-align:left; background:none; border:none; color:inherit; font:inherit; cursor:pointer; padding:2px 4px; }
.pm-hist-item span{ opacity:.65; font-size:var(--fs-xs); }
.pm-hist-item:hover{ background: color-mix(in srgb, var(--settings-bg), white 8%); }
.pm-grid .pm-thumb{ width:52px; height:52px; object-fit:cover; display:block; border-radius:3px; }
.pm-grid tr.is-selected td{ background: color-mix(in srgb, var(--ember) 16%, transparent); }
.pm-grid tbody tr{ cursor:pointer; }
.pm-dot{ display:inline-block; width:9px; height:9px; border-radius:50%; background:var(--c-vert-moyen-9); }
.pm-dot.is-override{ background: var(--ember); }
.pm-replace{ display:flex; flex-wrap:wrap; gap:8px 14px; align-items:center; padding:8px 0; }
.pm-replace-keys{ display:flex; flex-wrap:wrap; gap:4px 12px; width:100%; }
.pm-replace-scope{ opacity:.75; }
.pm-main-add, .pm-crop-row{ display:flex; flex-wrap:wrap; gap:6px; align-items:center; margin:4px 0; }
.pm-crop-row .pm-crop-f{ width:70px; }
.pm-sys-block .settings-row-text{ width:100%; }
.pm-n2-bar{ position:sticky; bottom:0; z-index:5; display:flex; align-items:center; justify-content:space-between; gap:12px; padding:6px 14px; margin-top:8px;
  background: color-mix(in srgb, var(--settings-bg) 88%, transparent); color: var(--settings-value-color); font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size:var(--fs-md); border-radius:4px; }
.pm-n2-detail{ background:none; border:1px solid color-mix(in srgb, var(--settings-bg), white 20%); color:inherit; border-radius:4px; font:inherit; padding:2px 10px; cursor:pointer; }
.photo-grid.pm-n2 figure img{ -webkit-touch-callout:none; -webkit-user-select:none; user-select:none; }
@media (max-width: 700px){ .pm-panel, .pm-panel.is-wide{ width:100vw; } .pm-field{ grid-template-columns: 1fr; } .pm-tools, .pm-hist-list{ grid-column:1; } }

/* R64, R66, R67, R63 (2026-10-08) : onglet Photos (chantier actif).
   R64 : le bloc est un conteneur flex en colonne, comme le panneau qu'il
   remplace ; sinon la zone défilante n'a pas de hauteur propre et rien ne
   défile. R66 : une seule rangée de filtres, la recherche prend le reste.
   R67 : polices des thèmes partout (les grilles gardent la police des
   grilles). R63 : lignes de hauteur fixe pour l'affichage par fenêtre. */
#pmGridWrap{ display:flex; flex-direction:column; gap:12px; flex:1 1 auto; min-height:0; min-width:0; }
.pm-toolbar{ align-items:center; }
.pm-toolbar #pmSearch{ flex:1 1 220px; min-width:160px; }
.pm-toolbar .settings-save-btn{ padding:7px 12px; }
.pm-grid{ --frozen-w1: 34px; }
.pm-grid tbody tr[data-pm-id]{ height:60px; }
.pm-grid tbody td{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.pm-grid .photo-tag-svg, .pm-grid svg{ width:15px; height:15px; color: var(--ember); }
.pm-replace{ font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); }
.pm-replace-keys{ display:flex; flex-direction:column; gap:4px; width:100%; }
.pm-replace label{ display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.pm-key-raw{ margin-left:6px; opacity:.5; font-size:.85em; }
.pm-k .pm-key-raw{ display:block; margin-left:0; }
.pm-decoded{ opacity:.85; }
.pm-hist-clear{ text-align:left; background:none; border:none; color: var(--ember); font:inherit; font-size:.9em; cursor:pointer; padding:4px; }
.pm-panel .pm-title, .pm-panel .pm-cat > summary{ font-family:var(--title-font-family, var(--text-font-family)); }
.pm-panel .pm-v, .pm-panel .pm-mini, .pm-panel .pm-icon{ font-family:inherit; }
.pm-grid tbody tr[data-pm-id] td{ height:60px; box-sizing:border-box; padding-top:4px; padding-bottom:4px; }
.pm-grid .pm-thumb{ width:50px; height:50px; }
/* R54 (2026-10-08) : « 🖼️ Vignette » à gauche, « 🏠 Accueil » à droite, sur la
   même ligne ; chacun limité à la moitié de la vignette (texte tronqué sur
   une photo en portrait étroite), jamais superposés. */
.cover-btn, .home-cover-btn{ max-width: calc(50% - 14px); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; box-sizing:border-box; padding: 6px 9px; }
/* Cases étroites (≈ 176-215 px, grille dense ou iPad) : boutons plus
   compacts pour que les deux libellés tiennent en entier côte à côte. La
   grille fixe déjà la largeur des cases (pistes de grille, carré) :
   l'endiguement en largeur ne change pas la mise en page. */
.photo-grid figure{ container-type: inline-size; }
@container (max-width: 215px){
  .cover-btn, .home-cover-btn{ top:5px; font-size:var(--fs-2xs); letter-spacing:0; padding:5px 6px; max-width: calc(50% - 6px); }
  .cover-btn{ left:5px; } .home-cover-btn{ right:5px; }
}
/* Chantier Photos (reste à faire, 2026-10-08) : 🛈 sur les vignettes du pavé
   Vignette et de la mosaïque d'Intégration ; ligne « renseignées seulement »
   + « Ouvrir dans l'onglet Photos » ; aperçu des données principales. */
.activity-cover-fig:has(.pm-fig-detail){ position:relative; }
.pm-fig-detail{ position:absolute; top:4px; right:4px; z-index:2; width:26px; height:26px; padding:0; border:none; border-radius:50%; background:color-mix(in srgb, var(--ink) 75%, transparent); color:var(--bone); font-size:var(--fs-lg); cursor:pointer; }
.pm-filled-row{ display:flex; align-items:center; justify-content:space-between; gap:8px; padding-right:12px; }
.pm-main-preview{ margin:8px 0; padding:8px 10px; border:1px solid color-mix(in srgb, var(--bone) 15%, transparent); border-radius:6px; max-width:360px; }
/* R66 bis : rangée « Mise à jour en masse » sous la rangée de recherche. */
.pm-replace{ display:flex; flex-wrap:wrap; align-items:center; gap:8px 12px; padding:8px 0; border-top:1px solid color-mix(in srgb, var(--bone) 12%, transparent); }
.pm-replace-title{ font-weight:600; }
.pm-toolbar .filter-combo-wrap{ min-width:200px; }
/* R70 : vue d'ensemble — le point de l'activité ouverte pulse (en plus du doré). */
.trip-map-mode{ font-size:var(--fs-md); padding:2px 7px; }
@keyframes tripMapPulse{ 0%{ box-shadow:0 0 0 0 color-mix(in srgb, var(--c-or-moyen-2) 75%, transparent); } 70%{ box-shadow:0 0 0 14px color-mix(in srgb, var(--c-or-moyen-2) 0%, transparent); } 100%{ box-shadow:0 0 0 0 color-mix(in srgb, var(--c-or-moyen-2) 0%, transparent); } }
.is-overview .trip-map-marker.is-active .trip-map-dot{ animation: tripMapPulse 1.6s ease-out infinite; }
@media (prefers-reduced-motion: reduce){ .is-overview .trip-map-marker.is-active .trip-map-dot{ animation:none; } }
/* R58 complété : réglages en attente dans la fenêtre de paramétrage détachée. */
.detached-pending-bar{ position:sticky; top:0; z-index:3; display:flex; flex-wrap:wrap; align-items:center; gap:8px 12px; padding:8px 10px; margin-bottom:8px; border-radius:6px;
  background: color-mix(in srgb, var(--ember) 22%, var(--settings-bg)); font-family:var(--text-font-family); font-size:var(--fs-md); }
.detached-pending-bar[hidden]{ display:none; }
/* Structure (2026-10-08) : activité support d'un voyage sans activité. */
.activity-support-badge{ display:inline-block; margin-left:6px; padding:0 6px; border-radius:8px; font-size:var(--fs-xs); background:color-mix(in srgb, var(--c-vert-moyen-2) 18%, transparent); color:var(--c-vert-moyen-8); white-space:nowrap; }
/* Voyage sans activité (2026-10-08, Structure) : pas de bandeau
   d'activités ni de niveau activité ; la grille des photos est la page. */
body.trip-flat{ --strip-h: 0px; }
body.trip-flat .stripbar{ display:none; }
/* R77 (2026-10-08) : étape 0 « Prérequis » du guide d'Intégration. */
.ig-prereq{ margin:8px 0 10px; padding:8px 10px; border-radius:6px; border:1px solid color-mix(in srgb, var(--bone) 15%, transparent); }
.ig-prereq.is-bad{ border-color: color-mix(in srgb, var(--ember) 60%, transparent); background: color-mix(in srgb, var(--ember) 8%, transparent); }
.ig-prereq-title{ font-weight:600; display:flex; align-items:center; gap:8px; }
.ig-pre-list{ list-style:none; margin:6px 0 0; padding:0; display:flex; flex-direction:column; gap:4px; }
.ig-pre-item{ display:flex; flex-wrap:wrap; align-items:center; gap:6px; }
.ig-pre-item.is-bad .ig-pre-mark{ color: var(--ember); } .ig-pre-item.is-ok .ig-pre-mark{ color:var(--c-vert-moyen-8); } .ig-pre-item.is-warn .ig-pre-mark{ color:var(--c-orange-moyen); }

/* Commandes des cartes Google (2026-10-10, maquette validée par Thierry,
   maquettes/cartes_commandes_maquette.png) : mêmes commandes, même taille,
   même style et même emplacement sur N1, N2 et la carte de paramétrage des
   activités (addSiteMapControls() dans app.js). */
.smap-row{ display:flex; flex-direction:column; align-items:flex-start; gap:8px; margin:10px; }
.smap-col{ display:flex; flex-direction:column; align-items:center; gap:8px; margin:10px; }
.smap-ctrl, .smap-zoom, .smap-mode{ box-sizing:border-box; background:color-mix(in srgb, var(--night) 82%, transparent); color:var(--bone); border:1px solid color-mix(in srgb, var(--bone) 30%, transparent);
  box-shadow:0 1px 4px color-mix(in srgb, var(--black-pure) 35%, transparent); }
.smap-ctrl{ position:relative; display:flex; align-items:center; justify-content:center; width:34px; height:34px; padding:0; border-radius:17px;
  cursor:pointer; font:var(--fs-icon)/1 var(--text-font-family); }
.smap-ctrl svg{ width:17px; height:17px; }
.smap-ctrl:hover{ background:color-mix(in srgb, var(--night) 95%, transparent); color:var(--c-orange-moyen-10); }
.smap-route{ user-select:none; -webkit-user-select:none; touch-action:none; -webkit-touch-callout:none; }
.smap-route.is-pressed{ background:var(--c-or-moyen-2); color:var(--night); border-color:var(--c-or-moyen-2); }
/* Bascule Activité | Voyage (N2) : vue active en doré. */
.smap-mode{ display:flex; gap:2px; height:34px; padding:3px; border-radius:17px; }
.smap-mode .smap-mode-btn{ width:auto; height:26px; gap:5px; padding:0 10px; border:none; border-radius:13px; background:none; box-shadow:none;
  color:var(--sable); font:var(--fs-sm)/1 var(--text-font-family); }
.smap-mode .smap-mode-btn svg{ width:14px; height:14px; }
.smap-mode .smap-mode-btn.is-active{ background:var(--c-or-moyen-2); color:var(--night); cursor:default; }
/* Curseur de zoom vertical (pastille et couleur du curseur des vignettes). */
.smap-zoom{ display:flex; flex-direction:column; align-items:center; gap:6px; width:34px; padding:6px 0; border-radius:17px; --smap-track-h:90px; }
.trip-map-n1 .smap-zoom, .activity-geo-map .smap-zoom{ --smap-track-h:110px; }
@media (max-width: 1100px){ .trip-mini-map .smap-zoom{ --smap-track-h:40px; } }
.smap-zoom-step{ width:26px; height:20px; padding:0; border:none; background:none; color:var(--sable); cursor:pointer; font:var(--fs-xl)/1 var(--text-font-family); }
.smap-zoom-step:hover{ color:var(--c-orange-moyen-10); }
.smap-zoom-track{ position:relative; width:14px; height:var(--smap-track-h); cursor:pointer; touch-action:none; outline:none; }
.smap-zoom-track::before{ content:''; position:absolute; left:50%; top:0; bottom:0; width:3px; transform:translateX(-50%); border-radius:2px; background:color-mix(in srgb, var(--bone) 35%, transparent); }
.smap-zoom-fill{ position:absolute; left:50%; bottom:0; width:3px; height:var(--smap-zoom-pct, 0%); transform:translateX(-50%); border-radius:2px; background:var(--c-orange-moyen-2); }
.smap-zoom-thumb{ position:absolute; left:50%; bottom:var(--smap-zoom-pct, 0%); width:14px; height:14px; box-sizing:border-box; border-radius:7px;
  background:var(--c-orange-moyen-2); border:2px solid var(--bone); transform:translate(-50%, 50%); }
.smap-zoom-track:focus-visible .smap-zoom-thumb{ box-shadow:0 0 0 3px color-mix(in srgb, var(--c-or-moyen-2) 60%, transparent); }
/* Vignette de l'AUTRE vue (en bas à gauche). */
.smap-type{ position:relative; width:44px; height:44px; margin:0 0 6px 10px; padding:0; border:2px solid var(--bone); border-radius:8px; overflow:visible;
  cursor:pointer; box-shadow:0 1px 5px color-mix(in srgb, var(--black-pure) 45%, transparent); background:none; }
.smap-type .smap-type-preview{ position:absolute; inset:0; border-radius:6px; }
.smap-type.is-plan .smap-type-preview{ background:
  radial-gradient(ellipse 34% 26% at 30% 40%, #cfe0b4 0 60%, transparent 61%),
  radial-gradient(ellipse 30% 34% at 72% 66%, #d7e5c0 0 60%, transparent 61%),
  linear-gradient(160deg, #f3efe4, #e8e2d2); }
.smap-type.is-sat .smap-type-preview{ background:
  radial-gradient(ellipse 34% 26% at 30% 40%, #4c5a2e 0 60%, transparent 61%),
  radial-gradient(ellipse 30% 34% at 72% 66%, #5e5a35 0 60%, transparent 61%),
  linear-gradient(160deg, #6b6440, #3f4a2a 60%, #2e3a24); }
.smap-type b{ position:absolute; left:0; right:0; bottom:0; border-radius:0 0 6px 6px; font:600 var(--fs-4xs)/var(--fs-md) var(--text-font-family); text-align:center;
  color:var(--white); background:color-mix(in srgb, var(--night) 65%, transparent); }
.smap-type:hover{ border-color:var(--c-or-moyen-2); }
/* Infobulles : vers l'intérieur de la carte. */
.smap-row [data-tooltip]::after, .smap-type[data-tooltip]::after{ left:calc(100% + 8px); top:50%; transform:translateY(-50%); }
.smap-mode [data-tooltip]::after{ left:0; top:calc(100% + 10px); transform:none; }
/* Bascule N2 repliée (2026-10-10) : icône du mode en cours sous ↺, choix
   déployé à droite au survol, au focus ou au toucher. */
.smap-mode-wrap{ position:relative; }
.smap-mode-wrap .smap-mode{ position:absolute; left:42px; top:0; display:none; white-space:nowrap; z-index:2; }
.smap-mode-wrap:hover .smap-mode, .smap-mode-wrap:focus-within .smap-mode, .smap-mode-wrap.is-open .smap-mode{ display:flex; }
.smap-mode-wrap.is-dismissed .smap-mode{ display:none; } /* refermé dès le choix, même sous la souris */
.smap-mode-wrap::before{ content:''; position:absolute; left:34px; top:0; width:10px; height:34px; } /* pont de survol vers le choix */
.smap-col [data-tooltip]::after{ right:calc(100% + 8px); left:auto; top:50%; transform:translateY(-50%); }
/* Carte de paramétrage (Maps JavaScript, 2026-10-10). */
.param-map{ position:relative; width:100%; height:240px; border-radius:8px; overflow:hidden; margin-top:4px; background:var(--c-or-clair-2); }
.activity-geo-map .param-map{ height:600px; border-radius:6px; margin-top:0; }
.param-map.is-mounted:empty{ display:flex; align-items:center; justify-content:center; }
.param-map-dot{ pointer-events:none; }
.param-map-inner{ position:absolute; inset:0; } /* V.3 : la carte unique, déplacée d'un panneau à l'autre */
.trip-map-canvas:fullscreen, .param-map:fullscreen, .param-map-inner:fullscreen{ border-radius:0; }

/* Panneau de destination, retour d'essai du 2026-10-10 : champ unique (ID
   devant), une ligne Type / Pays / Statut / Priorité / Année / Mois /
   famille ; initiales TZ AZ NZ HZ quand la place manque (requête de
   conteneur) ; bouton « Ouvrir dans Google Maps » au style de ↺. */
#activityDetailPanel{ container-type:inline-size; container-name:actpanel; }
#activityDetailPanel .activity-summary-row .activity-dest-f-id{ flex:0 0 auto; min-width:44px; }
#activityDetailPanel .activity-dest-status-row{ flex-wrap:wrap; }
#activityDetailPanel .activity-dest-status-row .activity-dest-f-type{ flex:1 1 130px; min-width:0; }
#activityDetailPanel .activity-dest-status-row .activity-dest-f-type select{ width:100%; min-width:0; }
#activityDetailPanel .activity-dest-status-row .activity-dest-f-country_name{ flex:1 1 100px; min-width:0; }
.activity-dest-flag .flag-short{ display:none; }
@container actpanel (max-width: 900px){
  .activity-dest-flag .flag-long{ display:none; }
  .activity-dest-flag .flag-short{ display:inline; }
}
.activity-geo-open-link.activity-geo-clear-btn{ display:inline-flex; align-items:center; justify-content:center; gap:2px; width:auto; padding:0 8px;
  color:var(--bone); text-decoration:none; font-size:var(--fs-md); }
.activity-geo-open-link.activity-geo-clear-btn:not([href]){ visibility:hidden; }

/* Opérations SQL (2026-10-10) : Mon compte › Système › Opérations SQL. */
.sqlops-views{ display:flex; gap:6px; margin:0 0 10px; }
[data-tabpanel="sqlops"]{ font-family:var(--text-font-family); font-size:var(--fs-sm); }
.sqlops-view, .sqlops-run, .sqlops-close{ font-family:inherit; }
.sqlops-run, .sqlops-view{ font-size:var(--fs-sm); letter-spacing:0.02em; color:var(--action-button-text-color); background:color-mix(in srgb, var(--bone) 8%, transparent); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); padding:6px 12px; border-radius:16px; cursor:pointer; white-space:nowrap; }
.sqlops-run:hover{ background:color-mix(in srgb, var(--bone) 15%, transparent); }
.sqlops-run:disabled{ opacity:0.35; cursor:not-allowed; }
.sqlops-table th:nth-child(4){ width:420px; }
.sqlops-id-cell{ white-space:normal; word-break:break-all; color:var(--ember); }
.sqlops-detail-id{ font-size:var(--fs-sm); opacity:0.85; margin-bottom:2px; }
.sqlops-detail-id code{ color:var(--ember); }
a.sqlops-run{ display:inline-block; text-decoration:none; }
.sqlops-run.is-migration:not(:disabled){ border-color:var(--ember); color:var(--ember); }
.sqlops-result-scroll, .sqlops-list-scroll{ flex:none; min-height:0; }
.sqlops-view.is-active{ border-color:var(--ember); color:var(--ember); }
.sqlops-list-scroll{ max-height:38vh; }
.sqlops-row{ cursor:pointer; }
.sqlops-row.selected td{ background:color-mix(in srgb, var(--c-orange-moyen-12) 12%, transparent); }
.sqlops-status{ white-space:nowrap; }
.sqlops-status.is-ecart{ color:var(--c-orange-moyen-13); }
.sqlops-status.is-echec, .sqlops-status.is-invalide{ color:var(--ig-error, var(--c-rouge-moyen-7)); }
.sqlops-missing, .sqlops-id{ font-size:var(--fs-xs); opacity:0.75; }
.sqlops-title-cell{ white-space:normal; }
.sqlops-empty, .sqlops-note{ opacity:0.75; font-style:italic; }
.sqlops-detail{ margin-top:12px; padding:12px 14px; border:1px solid color-mix(in srgb, var(--c-orange-moyen-12) 30%, transparent); border-radius:8px; }
.sqlops-detail-head{ display:flex; align-items:flex-start; justify-content:space-between; gap:10px; }
.sqlops-detail-head h4{ margin:0 0 6px; color:var(--ember); font-size:var(--fs-md); }
.sqlops-close{ background:transparent; border:0; color:inherit; font-size:var(--fs-2xl); cursor:pointer; }
.sqlops-explain{ white-space:pre-line; margin-bottom:8px; }
.sqlops-dl{ display:grid; grid-template-columns:150px 1fr; gap:8px; margin:3px 0; }
.sqlops-dl > span:first-child{ color:var(--settings-label-color); }
.sqlops-sql{ margin:8px 0; }
.sqlops-sql summary{ cursor:pointer; color:var(--ember); }
.sqlops-sql pre{ max-height:320px; overflow:auto; overscroll-behavior:none; padding:8px; background:color-mix(in srgb, var(--black-pure) 25%, transparent); border-radius:6px; font-size:var(--fs-sm); white-space:pre; }
.sqlops-detail-actions{ margin:8px 0; }
.sqlops-run-head{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin:10px 0 6px; }
.sqlops-download{ color:var(--ember); }
.sqlops-error{ color:var(--ig-error, var(--c-rouge-moyen-7)); margin:6px 0; white-space:pre-wrap; }
.sqlops-expects{ width:auto; margin:6px 0 10px; }
.sqlops-q-title{ margin:12px 0 4px; font-size:var(--fs-sm); }
.sqlops-q-title span{ font-weight:normal; opacity:0.75; }
.sqlops-result-scroll{ max-height:320px; }
.sqlops-result td, .sqlops-result th{ white-space:nowrap; font-size:var(--fs-sm); }
.sqlops-null{ opacity:0.5; font-style:italic; }
.sqlops-q-cell span{ opacity:0.7; }
.sqlops-note{ opacity:0.7; font-size:var(--fs-xs); }
.sqlops-more{ font-size:var(--fs-sm); opacity:0.75; margin-top:4px; }

/* Écran Zones (modèle propre, 2026-10-10) : arbre région → pays → zone. */
.zones-row.zones-level-0 td{ background:color-mix(in srgb, var(--c-orange-moyen-12) 8%, transparent); }
.zones-row.zones-level-0 .zones-name{ font-weight:600; }
.zones-indent-1{ padding-left:18px !important; }
.zones-indent-2{ padding-left:36px !important; }
.zones-row.is-unused .zones-name{ font-style:italic; opacity:0.75; }
.zones-unused-badge{ font-size:var(--fs-xs); color:var(--c-orange-moyen-13); border:1px solid color-mix(in srgb, var(--c-orange-moyen-13) 50%, transparent); border-radius:10px; padding:0 6px; margin-left:4px; }
.activities-status-lieu{ opacity:0.75; margin-left:4px; }
.activity-geo-source{ font-size:var(--fs-sm); opacity:0.75; align-self:center; }
.activity-same-place{ margin-top:6px; }
/* Participants (2026-10-11, point 2b) : cases sur une ligne, à la hauteur
   d'un champ, texte au format des champs (pas en petites capitales). */
.modal-field .voyage-participants-flags{ display:flex; gap:18px; flex-wrap:wrap; align-items:center; min-height:38px; }
.modal-field .voyage-participants-flags label.voyage-participant{ display:inline-flex; align-items:center; gap:6px; margin:0; font-size:var(--fs-md); text-transform:none; letter-spacing:0; opacity:1; }
/* Lecture seule (2026-10-11, décision 3g de Thierry) : valeur gérée ou
   calculée par le système. Un champ modifiable a un cadre ; un champ en
   lecture seule n'en a pas : même gabarit et même fond qu'un champ, cadre
   invisible, texte légèrement atténué, sélectionnable. Couleurs, police et
   taille héritées du thème (aucune valeur en dur). S'applique aussi à tout
   champ readonly des écrans d'administration (ex. Racine du dossier). */
.ro-value{ display:flex; align-items:center; min-height:38px; box-sizing:border-box; padding:0 10px; border:1px solid transparent; border-radius:6px; background:var(--ctl-bg); color:color-mix(in srgb, var(--settings-default-text-color) 72%, transparent); font-family:inherit; font-size:var(--field-font-size); user-select:text; cursor:default; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }
.ro-value.is-mono{ font-family:var(--text-font-family); }
.users-modal.account-modal input[readonly],
.users-modal.account-modal textarea[readonly]{ border-color:transparent !important; color:color-mix(in srgb, var(--settings-default-text-color) 72%, transparent); cursor:default; }
.users-modal.account-modal input[readonly]:focus{ border-color:transparent !important; outline:none; }
.pm-v.pm-ro{ color:color-mix(in srgb, var(--settings-default-text-color) 72%, transparent); }
.ro-cell{ color:color-mix(in srgb, var(--settings-default-text-color) 72%, transparent); user-select:text; }

/* Infobulles : règle générale de Thierry (2026-10-10) — retirées sur les
   icônes évidentes (✕, ↺, plein écran, + / −, curseurs, cases « tout
   sélectionner », crayon, cœur, partage, téléchargement, commentaires,
   corbeille, ⓘ, ▲ ▼, œil) ; gardées seulement quand le geste ou l'effet
   n'est pas devinable (appui maintenu sur le tracé, N° cliquable, Ultra,
   résolution, tags, Vignette / Accueil, détacher, légendes…). Masquées
   ici, en CSS, pour qu'elles disparaissent aussi des pages de voyage déjà
   publiées (sans republication) ; aria-label conservé. */
  [data-i18n-tooltip="modal_close_title"]::after,
  [data-i18n-tooltip="lb_close_photo_aria"]::after,
  [data-i18n-tooltip="cell_reset_title"]::after,
  [data-i18n-tooltip="lb_fullscreen_title"]::after,
  [data-i18n-tooltip="lb_fullscreen_exit_title"]::after,
  [data-i18n-tooltip="lb_zoom_slider_tooltip"]::after,
  [data-i18n-tooltip="cell_size_slider_tooltip"]::after,
  [data-i18n-tooltip="users_select_all_title"]::after,
  [data-i18n-tooltip="listvalues_select_all_title"]::after,
  [data-i18n-tooltip="voyages_select_all_title"]::after,
  [data-i18n-tooltip="destinations_select_all_title"]::after,
  [data-i18n-tooltip="countries_select_all_title"]::after,
  [data-i18n-tooltip="associations_select_all_title"]::after,
  [data-i18n-tooltip="activities_select_all_title"]::after,
  [data-i18n-tooltip="activity_name_edit_title"]::after,
  [data-i18n-tooltip="voyage_title_edit_title"]::after,
  [data-i18n-tooltip="voyage_date_edit_title"]::after,
  [data-i18n-tooltip="account_maximize_title"]::after,
  [data-i18n-tooltip="lb_like_title"]::after,
  [data-i18n-tooltip="lb_share_title"]::after,
  [data-i18n-tooltip="lb_save_title"]::after,
  [data-i18n-tooltip="lb_comments_title"]::after,
  [data-i18n-tooltip="lb_comment_action_title"]::after,
  [data-i18n-tooltip="lb_comment_delete"]::after,
  [data-i18n-tooltip="lb_exif_action_title"]::after,
  .cell-reset-btn:not(.theme-color-system-reset):not(.theme-font-system-reset)[data-tooltip]::after,
  #lbFsBtn[data-tooltip]::after,
  .associations-move-btn[data-tooltip]::after,
  .logs-message-toggle[data-tooltip]::after,
  .settings-key-toggle[data-tooltip]::after,
  th[data-col^="with_"][data-tooltip]::after,
  .activity-geo-clear-btn[data-tooltip]::after{ content:none !important; display:none !important; }

/* Bloc « Position appliquée / proposée » du panneau de destination
   (retour d'essai du 2026-10-10 : titre, critère, choix et ligne du bas
   mélangeaient tailles et styles). UNE police, celle des textes du thème
   (--text-font-family / -weight / -style, réglables dans Thèmes), une
   taille ; seuls le titre (couleur) et le nom du lieu (gras) se
   distinguent. */
.activity-geo-results .geo-proposal, #activityGeoMsg{ font-family:var(--text-font-family); font-weight:var(--text-font-weight); font-style:var(--text-font-style); font-size:var(--fs-sm); line-height:1.45; }
.activity-geo-results .geo-proposal *{ font-family:inherit; font-size:inherit; font-style:inherit; line-height:inherit; }
.activity-geo-results .geo-proposal-title{ font-weight:600; }
.activity-geo-results .dest-geo-result-name{ font-weight:600; }
.activity-geo-results .dest-suggest-why, .activity-geo-results .geo-imprecise{ font-size:inherit; }

/* Pastilles de suggestion sous le champ Destination (2026-10-10) : pleine
   + nombre d'activités si utilisée, en pointillés sinon (avec ✕). */
.activity-dest-chips{ display:flex; flex-wrap:wrap; gap:6px; margin:-6px 0 12px; }
.activity-dest-chips:empty{ display:none; }
.dest-chip{ display:inline-flex; align-items:center; border-radius:14px; font:var(--fs-sm)/1 var(--text-font-family); }
.dest-chip.is-used{ background:color-mix(in srgb, var(--bone) 14%, transparent); border:1px solid color-mix(in srgb, var(--bone) 28%, transparent); }
.dest-chip.is-unused{ background:transparent; border:1px dashed color-mix(in srgb, var(--bone) 45%, transparent); }
.dest-chip button{ all:unset; cursor:pointer; color:var(--settings-default-text-color); padding:5px 10px; }
.dest-chip .dest-chip-pick:hover{ color:var(--dust-gold); }
.dest-chip .dest-chip-count{ display:inline-block; min-width:16px; margin-left:6px; padding:1px 5px; border-radius:8px; background:color-mix(in srgb, var(--c-or-moyen-2) 25%, transparent); text-align:center; font-size:var(--fs-xs); }
.dest-chip .dest-chip-del{ padding:5px 9px 5px 2px; opacity:0.6; }
.dest-chip .dest-chip-del:hover{ opacity:1; color:var(--c-orange-moyen-8); }

/* Zones géographiques en liste simple (2026-10-11, point 3) : filtres
   compacts avant la recherche, qui prend la place restante. */
[data-tabpanel="pays"] .users-toolbar-filters select{ flex:0 1 auto; max-width:220px; }
[data-tabpanel="pays"] .users-toolbar-filters .srw-solo{ flex:1 1 220px; }
.voyage-detail-block{ margin:4px 0 10px; padding-top:10px; border-top:1px solid color-mix(in srgb, var(--settings-default-text-color) 14%, transparent); }
.voyage-detail-block:first-child{ border-top:none; padding-top:0; margin-top:0; }
/* Identité (maquette 2h) : ID étroit, Nom large, Année et Mois sur la même ligne. */
.voyage-detail-identity{ display:grid; grid-template-columns:minmax(56px, 0.5fr) minmax(0, 4fr) minmax(80px, 1fr) minmax(120px, 1.4fr); gap:14px; }
.voyage-detail-identity .modal-field{ min-width:0; }
.voyage-detail-block > .settings-admin-label{ font-size:var(--fs-sm); margin:0 0 10px; }
.voyage-storage-path{ display:grid; grid-template-columns:64px minmax(0,1fr) auto; gap:8px; align-items:center; margin-bottom:6px; }
.voyage-storage-key{ font-size:var(--fs-xs); text-transform:uppercase; letter-spacing:0.05em; opacity:0.7; }

/* Recherche « à la Gmail » (2026-10-11, 3h) : « ? » à droite du champ,
   encadré de rappel de la syntaxe. Couleurs et police du thème. */
.grid-search-help{ position:relative; flex:0 0 auto; align-self:center; }
.grid-search-help > summary{ list-style:none; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; width:24px; height:24px; border-radius:50%; border:1px solid var(--ctl-border); background:var(--ctl-bg); color:var(--settings-default-text-color); font:inherit; user-select:none; }
.grid-search-help > summary::-webkit-details-marker{ display:none; }
.grid-search-help[open] > summary{ border-color:var(--dust-gold); }
.grid-search-help-box{ position:absolute; right:0; top:calc(100% + 6px); z-index:40; width:min(380px, 80vw); padding:10px 12px; border-radius:8px; border:1px solid var(--ctl-border); background:var(--settings-bg); color:var(--settings-default-text-color); font-family:var(--text-font-family); font-size:var(--field-font-size); line-height:1.5; box-shadow:0 6px 18px color-mix(in srgb, var(--settings-bg) 60%, black); }
.grid-search-help-box ul{ list-style:none; margin:6px 0; padding:0; display:grid; gap:4px; }
.grid-search-help-box code{ color:var(--dust-gold); font-family:var(--text-font-family); }
.grid-search-help-keys{ opacity:0.8; line-height:1.6; }
/* Zones : cases des participants (2026-10-11, point 4) ; via un voyage =
   lecture seule (case grisée, non modifiable). */
.zones-part-cell{ text-align:center; }
.zones-part-cell input.is-via:disabled{ opacity:0.55; cursor:default; }

/* Données principales au glisser-déposer (2026-10-11, nouveau 1b) :
   l'élément tenu suit le pointeur, les autres s'écartent ; séparateur =
   bande saisissable ; ✕ dans Interface › Photos. Couleurs du thème. */
/* Glisser-déposer (2026-10-11, refait) : l'élément tenu suit le pointeur,
   les autres s'écartent en douceur, .pm-drag-slot montre la place. */
.pm-drag-item{ cursor:grab; touch-action:none; user-select:none; position:relative; transition:transform 0.15s ease; }
.is-sorting, .is-sorting .pm-drag-item{ cursor:grabbing; }
.pm-drag-item.is-dragging{ transition:none; background:color-mix(in srgb, var(--settings-bg), currentColor 14%); box-shadow:0 4px 14px color-mix(in srgb, black 45%, transparent); border-radius:4px; z-index:3; }
.pm-drag-slot{ position:absolute; border:1px dashed var(--dust-gold); border-radius:4px; background:color-mix(in srgb, var(--dust-gold) 10%, transparent); pointer-events:none; z-index:1; transition:top 0.15s ease; }
.pm-drag-sep{ min-height:16px; margin:4px 0; border:none; display:flex; align-items:center; gap:8px; }
.pm-drag-sep-line{ flex:1; height:1px; background:currentColor; opacity:0.55; }
.pm-drag-handle{ display:inline-flex; align-items:center; margin-left:10px; color:currentColor; opacity:0.9; cursor:grab; flex:none; }
.pm-drag-item:hover .pm-drag-handle{ opacity:1; color:var(--dust-gold); }
.pm-main-editor .exif-row{ align-items:center; }
.pm-main-editor .exif-row .v{ flex:1; }
.pm-main-editor .pm-drag-rm{ margin-left:8px; }
.pm-summary-text.exif-body{ padding:0; flex:1; min-width:0; overflow:visible; }
.pm-summary-text.exif-body .exif-row{ padding:4px 0; }

/* Listes déroulantes en grille (2026-10-11, retour de Thierry sur Zones
   géographiques ; projet/standards/GRID_STANDARD.md § 9 complété) : la liste suit la
   largeur de SA colonne (texte tronqué « … », flèche toujours visible,
   jamais cachée sous la colonne suivante) et garde le fond transparent de
   la saisie en grille, comme les autres listes de cellule. */
.users-modal.account-modal .users-modal-table td select,
.users-modal.account-modal .users-modal-table td select.admin-select:not(.voyages-theme-select){
  width:100%; max-width:100%; min-width:0; box-sizing:border-box;
  text-overflow:ellipsis; white-space:nowrap; overflow:hidden;
  background-color:transparent; padding-right:24px;
}

/* Cas à trancher dans le bandeau « voyage désactivé » (2026-10-11, point 7) :
   lien souligné, dans la couleur du texte du bandeau. */
.voyage-cases-link{ margin-left:12px; border:0; background:none; color:inherit; font:inherit; text-decoration:underline; cursor:pointer; padding:0; }
.voyage-disabled-banner.is-pill .voyage-cases-link{ display:none; }

/* Pastilles des lieux connus du pays (2026-10-11, point 9) : largeur
   maximale, nom complet au survol. */
.dest-chip .dest-chip-pick{ display:inline-flex; align-items:center; max-width:220px; }
.dest-chip .dest-chip-name{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* Interrupteur de Publication (2026-10-11, point 14) : Actif = à droite,
   VERT (couleur « succès » du thème), mot « Actif » ; Inactif = à gauche,
   ORANGÉ (« attention »), mot « Inactif ». Couleur, position ET mot. */
.pub-switch{ display:inline-flex !important; align-items:center; gap:8px; cursor:pointer; user-select:none; text-transform:none; letter-spacing:normal; font-size:var(--field-font-size); margin:6px 0 0; }
.pub-switch input{ position:absolute; opacity:0; width:0; height:0; }
.pub-switch-track{ display:inline-block; position:relative; width:40px; height:22px; border-radius:22px; background:var(--color-warning); transition:background .15s ease; flex:none; }
.pub-switch-track::before{ content:''; position:absolute; left:3px; top:3px; width:16px; height:16px; border-radius:50%; background:var(--bone); transition:transform .15s ease; }
.pub-switch input:checked + .pub-switch-track{ background:var(--color-success); }
.pub-switch input:checked + .pub-switch-track::before{ transform:translateX(18px); }
.pub-switch input:focus-visible + .pub-switch-track{ outline:2px solid var(--dust-gold); outline-offset:2px; }
.pub-switch input:disabled + .pub-switch-track{ opacity:0.5; cursor:not-allowed; }
.pub-switch-word{ font-weight:600; text-transform:none !important; letter-spacing:normal !important; }
.pub-switch .pub-on{ color:var(--color-success); }
.pub-switch .pub-off{ color:var(--color-warning); }
.pub-switch input:checked ~ .pub-off, .pub-switch input:not(:checked) ~ .pub-on{ display:none; }

/* Panneau d'étape (2026-10-11, retour de Thierry, écarts du lot 3) : la
   ligne Latitude / Longitude / origine / Google Maps / ↺ tient dans le
   panneau (champs qui rétrécissent, passage à la ligne si besoin) ; le
   formulaire « Nouvelle zone » reste caché tant qu'on ne l'ouvre pas. */
#activityDetailPanel .activity-geo-coords-row{ flex-wrap:wrap; align-items:flex-end; }
#activityDetailPanel .activity-geo-coords-row .modal-field{ flex:1 1 110px; min-width:0; }
#activityDetailPanel .activity-geo-coords-row .modal-field input{ width:100%; min-width:0; box-sizing:border-box; }
.activity-zone-new[hidden]{ display:none !important; }

/* Outil de recadrage (2026-10-11, nouveau 3) : fenêtre au-dessus de tout,
   cadre au ratio de l'affichage (rond pour le profil), grille des tiers. */
.crop-backdrop{ position:fixed; inset:0; z-index:2147483646; display:flex; align-items:center; justify-content:center; background:color-mix(in srgb, black 72%, transparent); overscroll-behavior:none; }
.crop-dialog{ width:min(720px, 92vw); max-height:94vh; display:flex; flex-direction:column; gap:12px; padding:18px; border-radius:12px; background:var(--settings-bg); color:var(--settings-default-text-color); font-family:var(--text-font-family); font-size:var(--field-font-size); box-shadow:0 20px 60px color-mix(in srgb, black 60%, transparent); }
.crop-title{ font-family:var(--title-font-family); color:var(--settings-label-color); text-transform:uppercase; letter-spacing:0.06em; }
.crop-frame{ position:relative; width:100%; max-height:62vh; margin:0 auto; overflow:hidden; border-radius:8px; cursor:grab; touch-action:none; background:var(--dusk); outline:none; }
.crop-frame.is-round{ width:min(380px, 70vw); border-radius:50%; }
.crop-frame.is-dragging{ cursor:grabbing; }
.crop-frame:focus-visible{ box-shadow:0 0 0 2px var(--dust-gold); }
.crop-img{ display:block; width:100%; height:100%; object-fit:cover; user-select:none; pointer-events:none; }
.crop-grid{ position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(to right, transparent calc(33.33% - 0.5px), color-mix(in srgb, white 45%, transparent) calc(33.33% - 0.5px), color-mix(in srgb, white 45%, transparent) calc(33.33% + 0.5px), transparent calc(33.33% + 0.5px), transparent calc(66.66% - 0.5px), color-mix(in srgb, white 45%, transparent) calc(66.66% - 0.5px), color-mix(in srgb, white 45%, transparent) calc(66.66% + 0.5px), transparent calc(66.66% + 0.5px)),
             linear-gradient(to bottom, transparent calc(33.33% - 0.5px), color-mix(in srgb, white 45%, transparent) calc(33.33% - 0.5px), color-mix(in srgb, white 45%, transparent) calc(33.33% + 0.5px), transparent calc(33.33% + 0.5px), transparent calc(66.66% - 0.5px), color-mix(in srgb, white 45%, transparent) calc(66.66% - 0.5px), color-mix(in srgb, white 45%, transparent) calc(66.66% + 0.5px), transparent calc(66.66% + 0.5px)); }
.crop-zoom-row{ display:flex; align-items:center; gap:10px; }
.crop-zoom, .crop-stretch{ flex:1; accent-color:var(--dust-gold); }
.crop-zoom-val{ min-width:4.5em; text-align:right; }
/* Étirement (point 15) : libellés et valeurs de même largeur, curseurs alignés. */
.crop-zoom-label{ min-width:11em; }
.crop-stretch-val{ min-width:4.5em; }
/* Dézoom (point 20, 2026-10-11) : sous 100 %, la photo est plus petite
   que son cadre ; les bordures prennent la couleur de fond des vignettes
   du thème (--dusk, comme les cases de la grille des photos). */
.single, .activity-cover-zoom, .activity-cover-current .activity-cover-zoom, .ig-card-current .activity-cover-zoom{ background:var(--dusk); overflow:hidden; }
.crop-hint{ color:color-mix(in srgb, var(--settings-default-text-color) 70%, transparent); }
.crop-actions{ display:flex; align-items:center; gap:8px; }
.crop-spacer{ flex:1; }
/* Bouton « Recadrer » sur la tuile N1 (administrateur), en haut à droite. */
.activity-card .single .activity-crop-btn{ position:absolute; top:10px; right:42px; z-index:6; margin:0; width:26px; height:26px; padding:0; border-radius:13px; display:inline-flex; align-items:center; justify-content:center; vertical-align:baseline; background:color-mix(in srgb, var(--card-num-bg) 60%, transparent); color:var(--bone); opacity:0; }
.activity-card:hover .single .activity-crop-btn, .activity-card .single .activity-crop-btn:focus-visible{ opacity:1; }
.activity-cover-crop{ margin-left:8px; }

/* Paramétrage ouvert depuis l'accueil N0 (point 21, 2026-10-11) : la page
   voyage chargée dans le calque ne montre que la fenêtre « Mon compte » et
   ses fenêtres filles ; le reste est invisible et le fond transparent
   laisse voir N0 derrière. Le calque reste invisible tant que la fenêtre
   n'est pas ouverte (aucune page voyage aperçue). */
html.account-embed, html.account-embed body{ background:transparent; }
html.account-embed body{ visibility:hidden; }
/* Calques flottants (appendFloat, data-float-layer) : tous visibles. La
   liste écrite à la main oubliait le menu des listes de choix (nommé
   « .dark-select-group » par erreur) et les suggestions des combos : depuis
   N0, une liste s'ouvrait invisible et paraissait figée (retour du 12/10,
   test 11d). */
html.account-embed body > :is(.users-modal-backdrop, .crop-backdrop, .comment-editor-backdrop, .activity-cover-overlay, [data-float-layer]){ visibility:visible; }
.n0-account-frame{ position:fixed; inset:0; width:100%; height:100%; border:0; z-index:10000; background:transparent; color-scheme:normal; opacity:0; }
.n0-account-frame.is-ready{ opacity:1; }
html:has(body.n0-account-open), body.n0-account-open{ overflow:hidden; }

/* Panneau d'étape (2026-10-11, retour de Thierry) : liste Zone et bouton
   « + Nouvelle zone » sur la même ligne et à la même hauteur ; la mention
   « (choisie à la main) » / zone proposée sous la liste, rattachée au champ.
   « Rechercher à nouveau » : petit bouton sous le champ Lieu. */
#activityDetailPanel .activity-zone-f{ margin:4px 0 10px; }
#activityDetailPanel .activity-zone-line{ display:flex; align-items:stretch; gap:8px; }
#activityDetailPanel .activity-zone-line select{ flex:1 1 auto; min-width:0; }
#activityDetailPanel .activity-zone-line .settings-save-btn{ flex:0 0 auto; display:inline-flex; align-items:center; gap:4px; margin:0; white-space:nowrap; }
#activityDetailPanel .activity-zone-note{ display:block; margin-top:4px; }
#activityDetailPanel .activity-zone-note:empty{ display:none; }
#activityDetailPanel .activity-summary-row{ align-items:flex-start; }
#activityDetailPanel .etape-lieu-research-btn{ align-self:flex-start; margin-top:6px; white-space:nowrap; }
.activity-geo-results .geo-replace-btn{ margin-top:6px; }

/* Coefficients de focale (2026-10-11) : nombre de photos de l'appareil et,
   sans coefficient, « à renseigner » (avec la valeur suggérée). */
.pm-crop-row .pm-crop-info{ margin:0; }
.pm-crop-row .pm-crop-info.is-missing{ color:var(--c-orange-moyen-8); }


/* Colonnes numériques cadrées à droite, en-tête compris (2026-10-11 soir,
   charte des grilles § 1) : classe is-num posée par gridAlignNumeric(). */
.users-modal-table th.is-num, .users-modal-table td.is-num{ text-align:right; }
.users-modal-table td.is-num input{ text-align:right; }
.users-modal-table th.is-num{ padding-right:12px; }

/* Interface › Photos (2026-10-11 soir) : deux colonnes aérées — à gauche
   l'ordre des données, à droite les appareils ; l'une sous l'autre quand la
   place manque. Polices et couleurs du thème uniquement. */
.pm-settings-cols{ display:grid; grid-template-columns:repeat(auto-fit, minmax(440px, 1fr)); gap:28px 40px; align-items:start; margin-top:8px; }
.pm-settings-cols .pm-sys-block{ min-width:0; display:flex; flex-direction:column; gap:10px; }
.pm-settings-cols .settings-desc{ white-space:normal; overflow-wrap:anywhere; max-width:none; line-height:1.45; }
.pm-settings-cols .pm-main-preview{ max-width:420px; }
.pm-main-add select, .pm-main-add input, .pm-cam-table input, .pm-cam-orphan select{ font-family:var(--text-font-family); font-size:var(--fs-sm); }
.pm-cam-head{ display:flex; align-items:center; gap:10px; min-height:26px; }
.pm-cam-table{ border-collapse:collapse; width:100%; font-family:var(--text-font-family); font-size:var(--fs-sm); }
.pm-cam-table th{ text-align:left; font-weight:normal; color:var(--settings-label-color); padding:4px 8px 6px 0; border-bottom:1px solid color-mix(in srgb, var(--bone) 15%, transparent); }
.pm-cam-table td{ padding:4px 8px 4px 0; vertical-align:middle; color:var(--settings-value-color); }
.pm-cam-table th.is-num, .pm-cam-table td.is-num{ text-align:right; }
.pm-cam-table .pm-cam-model{ display:block; min-width:150px; }
.pm-cam-table .pm-cam-label{ width:100%; min-width:270px; box-sizing:border-box; }
.pm-cam-table .pm-cam-factor{ width:70px; text-align:right; }
.pm-cam-table tr.is-missing .pm-cam-factor{ border-color:var(--c-orange-moyen-8); }
.pm-cam-state-row td{ padding-top:0; }
.pm-cam-state{ font-size:var(--fs-xs); color:var(--settings-label-color); }
.pm-cam-state.is-missing{ color:var(--c-orange-moyen-8); }
.pm-cam-state.is-suggestion{ font-style:italic; }
.pm-cam-state-row .pm-cam-apply{ margin-left:10px; padding:2px 10px; font-size:var(--fs-xs); }
.pm-cam-orphans{ border:1px solid var(--c-orange-moyen-8); border-radius:6px; padding:10px 12px; display:flex; flex-direction:column; gap:8px; }
.pm-cam-orphans-title{ color:var(--c-orange-moyen-8); font-family:var(--text-font-family); font-size:var(--fs-sm); }
.pm-cam-orphan{ display:flex; flex-wrap:wrap; align-items:center; gap:8px; }
.pm-cam-arrow{ color:var(--settings-label-color); }
.pm-cam-table .ro-value, .pm-cam-orphan .ro-value{ font-family:var(--text-font-family); font-size:var(--fs-sm); padding:6px 10px; min-height:0; line-height:1.3; }

/* Page de derrière en pause (2026-10-12) : fenêtre Compte ouverte (hors
   onglet Thèmes), la page ne défile plus (pas de défilement qui se poursuit
   derrière en bout de grille) et ses animations s'arrêtent (bandeau
   défilant, pulsations de la carte). */
html:has(body.bg-paused), body.bg-paused{ overflow:hidden; }
body.bg-paused > :not(.users-modal-backdrop):not(.crop-backdrop):not(.comment-editor-backdrop):not(.activity-cover-overlay),
body.bg-paused > :not(.users-modal-backdrop):not(.crop-backdrop):not(.comment-editor-backdrop):not(.activity-cover-overlay) *{ animation-play-state:paused !important; }
/* Même pause sur l'accueil N0 derrière le calque de paramétrage. */
body.n0-account-open > :not(.n0-account-frame),
body.n0-account-open > :not(.n0-account-frame) *{ animation-play-state:paused !important; }

/* Boutons ✓ ✕ ronds — charte (2026-10-12, retour de Thierry : trop
   discrets sur fond sombre). Partout : panneaux de détail, blocs
   d'Interface et de Système (.panel-row-*), champs de réglage
   (.settings-field-confirm) et lignes de grilles (.users-row-*). Un tiers
   plus grands, fond teinté (vert derrière ✓, rouge derrière ✕), contour de
   la même couleur, teinte renforcée au survol. Couleurs du thème. Voir
   projet/standards/GRID_STANDARD.md § « Boutons ✓ ✕ ». */
/* 40 px (12/10, retour de Thierry : à 48 px ils étaient coupés de 6 px en
   haut des panneaux Voyages et Étapes) ; la place réservée de l'en-tête
   reste à 48 px (--confirm-head-h). */
:root{ --confirm-btn-size:40px; --confirm-btn-size-row:28px; --confirm-head-h:48px; }
.panel-row-confirm-btn, .panel-row-cancel-btn{ width:var(--confirm-btn-size); height:var(--confirm-btn-size); font-size:var(--fs-3xl); border-width:1.5px; }
.users-row-confirm-btn, .users-row-cancel-btn{ width:var(--confirm-btn-size-row); height:var(--confirm-btn-size-row); font-size:var(--fs-lg); border-width:1.5px; }
/* ✓ ✕ de ligne (12/10, test 10i) : jamais plus hauts que la ligne (31 px) —
   cellule à 1 px de marge, boutons centrés (pas sur la ligne de base). */
table.users-modal-table.has-dirty-col td.users-row-actions{ padding:1px 4px !important; }
.users-row-confirm-btn, .users-row-cancel-btn{ vertical-align:middle; }
.users-row-actions{ width:72px; min-width:72px; max-width:72px; }
.panel-row-confirm-btn, .users-row-confirm-btn{ color:var(--c-vert-moyen-3); border-color:color-mix(in srgb, var(--c-vert-moyen-2) 75%, transparent);
  background:color-mix(in srgb, var(--c-vert-moyen-2) 22%, var(--night)); }
.panel-row-confirm-btn:hover, .users-row-confirm-btn:hover{ background:color-mix(in srgb, var(--c-vert-moyen-2) 45%, var(--night)); border-color:var(--c-vert-moyen-3); color:var(--white); }
.panel-row-cancel-btn, .users-row-cancel-btn{ color:var(--c-rouge-moyen-6); border-color:color-mix(in srgb, var(--c-rouge-moyen-2) 75%, transparent);
  background:color-mix(in srgb, var(--c-rouge-moyen-2) 22%, var(--night)); }
.panel-row-cancel-btn:hover, .users-row-cancel-btn:hover{ background:color-mix(in srgb, var(--c-rouge-moyen-2) 45%, var(--night)); border-color:var(--c-rouge-moyen-6); color:var(--white); }
.panel-row-actions{ gap:8px; }
.pm-cam-head{ min-height:var(--confirm-head-h); }
/* Rien ne bouge quand ✓ ✕ paraissent (12/10, retour de Thierry, test 11f :
   le bloc Étape / Lieu descendait de 31 px). Place réservée : l'en-tête du
   panneau a toujours la hauteur des boutons, et les boutons, cachés sans
   être retirés (visibility), n'apportent aucune hauteur (centrés sur une
   boîte de hauteur nulle) ni ne changent la largeur du titre. Même chose
   pour les ✓ ✕ d'un champ de réglage. Voir projet/standards/GRID_STANDARD.md § « Rien ne
   bouge ». */
.voyages-split-detail-head{ min-height:var(--confirm-head-h); box-sizing:border-box; }
/* ✓ ✕ centrés sur la boîte de contenu de l'en-tête : 6 px en haut (au lieu
   de 2) pour qu'ils ne dépassent jamais du panneau (12/10, point F). */
.voyages-split-detail-head{ padding-top:6px; }
.panel-row-actions{ display:inline-flex; visibility:hidden; height:0; align-self:center; align-items:center; }
.panel-row-actions.is-visible{ visibility:visible; }
.settings-field-confirm[hidden]{ display:inline-flex; visibility:hidden; }


/* Vérification des connexions (Système, 2026-10-12). */
.conn-table{ width:100%; margin:4px 0 8px; border-collapse:collapse; table-layout:fixed; }
.conn-table th:nth-child(1){ width:32px; }
.conn-table th:nth-child(2){ width:240px; }
.conn-table th{ text-align:left; font-size:var(--fs-xs); opacity:0.7; font-weight:600; padding:4px 8px; }
.conn-table td{ vertical-align:top; font-size:var(--fs-sm); padding:5px 8px; border-top:1px solid color-mix(in srgb, var(--bone) 12%, transparent); }
.conn-dot{ width:24px; text-align:center; }
.conn-name{ white-space:nowrap; font-weight:600; }
.conn-err .conn-detail{ color:var(--c-rouge-moyen-6); }
.conn-ms{ opacity:0.6; font-size:var(--fs-xs); }
.conn-actions{ display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.conn-msg{ font-size:var(--fs-sm); opacity:0.85; }

/* ✕ de remise à zéro des listes de choix (2026-10-12, standard, voir
   wireSelectClear() dans app.js et projet/standards/GRID_STANDARD.md § « ✕ des listes ») :
   posée dans le cadre de la liste, à gauche de la flèche (marges négatives,
   largeur nette nulle), place toujours réservée. */
.sel-clear{ display:inline-flex; align-items:center; justify-content:center; flex:none; align-self:center; vertical-align:middle;
  position:relative; z-index:1; width:20px; height:20px; margin:0 28px 0 -48px; padding:0; border:none; border-radius:50%;
  background:transparent; color:inherit; opacity:0.6; cursor:pointer; font-size:var(--fs-sm); line-height:1; }
.sel-clear:hover{ opacity:1; background:color-mix(in srgb, var(--bone) 15%, transparent); }
@supports (anchor-name: --a){
  .sel-clear:not(.combo-clear){ position:absolute; margin:0; top:anchor(center); left:anchor(right); translate:calc(-100% - 28px) -50%; }
}
select.has-sel-clear{ padding-right:48px !important; }
select:has(> option:first-child:checked) + .sel-clear, select:disabled + .sel-clear, select[hidden] + .sel-clear{ visibility:hidden; }
/* Combo (champ où l'on tape) : ✕ dans le champ, visible s'il a un texte. */
.filter-combo-wrap{ position:relative; }
.combo-clear{ position:absolute; right:8px; top:50%; transform:translateY(-50%); margin:0; }
.filter-combo-input{ padding-right:30px !important; }
.filter-combo-input:placeholder-shown + .combo-clear{ visibility:hidden; }

/* Guide d'Intégration en trois parties (2026-10-12, lot 6 du processus
   continu) : traitements sous une barre, vérifications avec liens,
   activation. Ligne de progression en direct à hauteur réservée. */
.ig-part{ margin:12px 0; padding:10px 12px; border:1px solid color-mix(in srgb, var(--bone) 14%, transparent); border-radius:8px; }
.ig-part-title{ font-weight:700; font-size:var(--fs-md); margin-bottom:8px; }
.ig-chain-bar{ height:8px; border-radius:4px; background:color-mix(in srgb, var(--bone) 12%, transparent); overflow:hidden; }
.ig-chain-bar > span{ display:block; height:100%; background:var(--c-vert-moyen-2); transition:width .3s ease; }
.ig-chain.is-error .ig-chain-bar > span{ background:var(--c-rouge-moyen-2); }
.ig-chain-text{ margin-top:6px; font-size:var(--fs-sm); }
.ig-chain-live{ min-height:1.5em; font-size:var(--fs-xs); opacity:0.75; }
/* Suivi des traitements (13/10, AT.3-4) : action en cours en grand, barre
   et chiffres lisibles, historique dessous (défile au-delà de 12 lignes). */
.ig-live:empty{ display:none; }
.ig-live{ margin:10px 0 4px; padding:12px 14px; border-radius:8px; border:1px solid color-mix(in srgb, var(--bone) 16%, transparent); background:color-mix(in srgb, var(--bone) 5%, transparent); }
.ig-live.is-running{ border-color:color-mix(in srgb, var(--ember) 70%, transparent); background:color-mix(in srgb, var(--ember) 10%, transparent); }
.ig-live-now{ display:flex; align-items:center; gap:10px; font-size:var(--fs-lg); font-weight:700; line-height:1.35; }
.ig-live-dot{ flex:none; width:10px; height:10px; border-radius:50%; background:var(--ember); animation:igLivePulse 1.2s ease-in-out infinite; }
@keyframes igLivePulse{ 0%,100%{ opacity:1; } 50%{ opacity:.3; } }
.ig-live:hover .ig-live-dot{ animation-play-state:paused; }
.ig-live-progress{ display:flex; align-items:center; gap:12px; margin-top:10px; }
.ig-live-bar{ flex:1; height:14px; border-radius:7px; background:color-mix(in srgb, var(--bone) 14%, transparent); overflow:hidden; }
.ig-live-bar > span{ display:block; height:100%; background:var(--ember); transition:width .3s ease; }
.ig-live-count{ flex:none; min-width:12ch; text-align:right; font-size:var(--fs-md); font-weight:700; font-variant-numeric:tabular-nums; }
.ig-live-history-title{ margin-top:12px; font-size:var(--fs-xs); text-transform:uppercase; letter-spacing:.06em; opacity:.7; }
.ig-live:not(.is-running) .ig-live-history-title{ margin-top:0; }
.ig-live-history{ list-style:none; margin:4px 0 0; padding:0; max-height:13em; overflow-y:auto; overscroll-behavior:none; font-size:var(--fs-sm); }
.ig-live-history li{ display:flex; gap:10px; padding:3px 0; border-top:1px solid color-mix(in srgb, var(--bone) 8%, transparent); opacity:.85; }
.ig-live-history time{ flex:none; font-variant-numeric:tabular-nums; opacity:.7; }
/* Choix du voyage (AT.1) : saisie filtrée standard, assez large. */
.ig-trip-combo{ min-width:280px; }
.ig-trip-combo .filter-combo-input{ width:100%; }
.ig-chain-ok{ margin:6px 0 0; color:var(--c-vert-moyen-3); font-size:var(--fs-sm); }
.ig-checklist{ list-style:none; margin:0; padding:0; }
.ig-check{ display:grid; grid-template-columns:22px max-content 1fr; gap:4px 8px; align-items:baseline; padding:6px 0; border-top:1px solid color-mix(in srgb, var(--bone) 10%, transparent); }
.ig-checklist .ig-check:first-child{ border-top:none; }
.ig-check .ig-inline-action{ grid-column:2 / -1; }
.ig-check-label{ font-weight:600; font-size:var(--fs-sm); }
.ig-check-detail{ font-size:var(--fs-sm); opacity:0.85; }
.ig-check-detail.is-error{ color:var(--c-rouge-moyen-6); opacity:1; }
.ig-check-icon{ text-align:center; font-weight:700; }
.ig-check.is-done .ig-check-icon{ color:var(--c-vert-moyen-3); }
.ig-check.is-warn .ig-check-icon{ color:var(--c-orange-moyen-5); }
.ig-check.is-error .ig-check-icon{ color:var(--c-rouge-moyen-6); }
.ig-all-toggle{ margin:4px 0 8px; }

/* Système › Environnement (maquette 10k, 2026-10-12) : trois parties
   séparées par un filet, un bloc encadré par source, numéroté, avec sa
   pastille d'état. */
.env-section{ font-family:var(--text-font-family); padding:4px 0 18px; border-bottom:1px solid color-mix(in srgb, var(--bone) 14%, transparent); margin-bottom:16px; }
.env-section:last-child{ border-bottom:none; }
.env-section-title{ font-size:var(--fs-lg); font-weight:700; letter-spacing:0.04em; text-transform:uppercase; color:var(--settings-tab-active-color); }
.env-section-desc{ margin:4px 0 12px; font-size:var(--fs-sm); opacity:0.85; }
.env-grid{ display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:14px; }
@media (max-width: 1100px){ .env-grid{ grid-template-columns:minmax(0, 1fr); } }
.env-card{ border:1px solid color-mix(in srgb, var(--bone) 16%, transparent); border-radius:8px; padding:12px 14px; min-width:0; }
.env-card-wide{ margin-bottom:14px; }
.env-card-head{ display:flex; align-items:center; gap:10px; margin-bottom:4px; }
.env-card-title{ font-weight:700; font-size:var(--fs-md); }
.env-num{ flex:none; width:22px; height:22px; border-radius:50%; border:1px solid color-mix(in srgb, var(--settings-tab-active-color) 70%, transparent); color:var(--settings-tab-active-color); display:inline-flex; align-items:center; justify-content:center; font-size:var(--fs-xs); font-weight:700; }
.env-pill{ margin-left:auto; flex:none; border-radius:10px; padding:1px 9px; font-size:var(--fs-xs); border:1px solid color-mix(in srgb, var(--bone) 30%, transparent); color:color-mix(in srgb, var(--bone) 75%, transparent); white-space:nowrap; }
.env-pill:empty{ visibility:hidden; }
.env-pill::before{ content:"● "; }
.env-pill.is-ok{ color:var(--c-vert-moyen-3); border-color:color-mix(in srgb, var(--c-vert-moyen-2) 70%, transparent); }
.env-pill.is-warn{ color:var(--c-orange-moyen-5); border-color:color-mix(in srgb, var(--c-orange-moyen-5) 70%, transparent); }
.env-card-desc{ margin:0 0 10px; font-size:var(--fs-sm); opacity:0.85; }
.env-row{ display:grid; grid-template-columns:150px minmax(0, 1fr); gap:8px; align-items:center; margin:6px 0; }
.env-row > label{ font-size:var(--fs-sm); opacity:0.85; }
.env-field{ display:flex; gap:8px; align-items:center; min-width:0; }
.env-field > input.admin-select, .env-field > .env-ro{ flex:1 1 0; width:auto; min-width:0; }
.env-ro{ padding:6px 8px; border-radius:6px; }
.env-ro-small{ font-size:var(--fs-xs); word-break:break-all; }
.env-row-top{ align-items:start; }
.env-row-top > label{ padding-top:8px; }
.env-num-input{ flex:0 0 110px !important; width:110px !important; }
.env-unit{ font-size:var(--fs-sm); opacity:0.85; }
.env-key-help{ margin:-2px 0 2px 158px; font-size:var(--fs-xs); opacity:0.7; }
.env-card .env-row .switch{ flex:none; }
.env-card .settings-row{ padding-left:0; padding-right:0; }
.env-card .settings-key-field input.admin-select{ width:100%; min-width:0; }
.sqlops-backup-now{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin:4px 0 12px; }
.sqlops-backup-now > a.settings-save-btn{ text-decoration:none; }
/* Barre latérale verticale à sa largeur naturelle (au moins 128 px) : la
   largeur en gras des onglets étant réservée (data-text), elle ne change
   jamais au choix d'un onglet ; « Environnement » ne déborde plus. */
.settings-layout:not(.account-toplevel) > .settings-sidebar{ flex:0 0 auto; min-width:128px; max-width:220px; }
.env-card .env-field > input.admin-select{ width:100% !important; max-width:none !important; }
/* Clés Google dans leur bloc : libellé et aide au-dessus, champ dessous. */
.env-card .settings-row{ flex-direction:column; align-items:stretch; gap:6px; }
.env-card .settings-key-field{ display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.env-card .settings-key-field input.admin-select{ flex:1 1 220px; width:auto !important; }

/* Centre d'opérations (Système, 2026-10-12, maquette du pilotage). */
.ops-todo{ border:1px solid color-mix(in srgb, var(--c-orange-moyen-5) 55%, transparent); background:color-mix(in srgb, var(--c-orange-moyen-5) 8%, transparent); border-radius:8px; padding:8px 12px; margin-bottom:10px; }
.ops-todo-title{ font-weight:700; color:var(--c-orange-moyen-5); margin-bottom:4px; font-size:var(--fs-sm); }
.ops-todo-title.is-ok{ color:var(--c-vert-moyen-3); margin:0; }
.ops-todo-line{ display:flex; align-items:center; gap:8px; font-size:var(--fs-sm); padding:2px 0; }
.ops-todo-go{ margin-left:auto; flex-shrink:0; white-space:nowrap; background:none; border:none; color:var(--settings-tab-active-color); cursor:pointer; font:inherit; }
/* Erreur signalée (15/10, BW) : détails atténués, « Corrigée » à côté de « voir ». */
.ops-err-meta{ opacity:.7; }
.ops-todo-go + .ops-todo-go{ margin-left:8px; }
.ops-dot{ display:inline-block; width:9px; height:9px; border-radius:50%; background:color-mix(in srgb, var(--bone) 35%, transparent); flex:none; }
.ops-dot.is-ok{ background:var(--c-vert-moyen-2); }
.ops-dot.is-warn{ background:var(--c-orange-moyen-5); }
.ops-dot.is-err{ background:var(--c-rouge-moyen-2); }
.ops-toolbar{ align-items:center; }
.ops-chips{ display:flex; gap:6px; flex-wrap:wrap; }
.ops-chip{ border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); background:transparent; color:inherit; border-radius:14px; padding:3px 11px; font:inherit; font-size:var(--fs-xs); cursor:pointer; }
.ops-chip.is-active{ border-color:var(--settings-tab-active-color); color:var(--settings-tab-active-color); }
.ops-cron{ margin-left:auto; font-size:var(--fs-xs); opacity:0.85; display:flex; align-items:center; gap:6px; white-space:nowrap; }
.ops-layout{ display:grid; grid-template-columns:minmax(0, 1fr) 340px; gap:14px; align-items:start; }
@media (max-width: 1200px){ .ops-layout{ grid-template-columns:minmax(0, 1fr); } }
.ops-table tr.ops-fam td{ font-size:var(--fs-xs); font-weight:700; letter-spacing:0.05em; text-transform:uppercase; color:var(--settings-tab-active-color); padding-top:12px; }
.ops-table tr[data-ops-id]{ cursor:pointer; }
.ops-where{ font-size:var(--fs-xs); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius:4px; padding:0 5px; white-space:nowrap; }
.ops-sensitive{ font-size:var(--fs-xs); color:var(--c-orange-moyen-5); }
.ops-late{ color:var(--c-orange-moyen-5); }
.ops-last{ font-size:var(--fs-xs); }
.ops-launcher{ font-size:var(--fs-xs); opacity:0.8; white-space:nowrap; }
.ops-detail{ border:1px solid color-mix(in srgb, var(--bone) 16%, transparent); border-radius:8px; padding:12px 14px; position:sticky; top:0; }
.ops-d-title{ font-weight:700; color:var(--settings-tab-active-color); font-size:var(--fs-md); margin-bottom:6px; }
.ops-d-label{ font-size:var(--fs-xs); font-weight:700; letter-spacing:0.05em; text-transform:uppercase; opacity:0.7; margin:12px 0 4px; }
.ops-d-text{ margin:0; font-size:var(--fs-sm); }
.ops-d-list{ margin:0; padding-left:18px; font-size:var(--fs-sm); }
.ops-cmd{ margin:0 0 6px; padding:8px; border-radius:6px; background:color-mix(in srgb, var(--black-pure) 35%, transparent); font-size:var(--fs-xs); white-space:pre-wrap; word-break:break-all; }
.ops-d-history{ font-size:var(--fs-xs); border-collapse:collapse; width:100%; }
.ops-d-history td{ padding:2px 6px 2px 0; vertical-align:top; }
.ops-d-history .is-ok{ color:var(--c-vert-moyen-3); }
.ops-d-history .is-echec{ color:var(--c-rouge-moyen-6); }
.ops-d-history .is-averti{ color:var(--c-orange-moyen-5); }
.ops-list{ min-width:0; overflow-x:auto; overscroll-behavior:none; }
.ops-list .ops-table{ width:100%; table-layout:fixed; min-width:0 !important; }
.ops-list .ops-table td{ overflow:hidden; text-overflow:ellipsis; }
.ops-todo, .ops-detail, .ops-toolbar{ font-family:var(--text-font-family); }
.ops-run, .ops-open{ padding:2px 10px !important; font-size:var(--fs-xs) !important; border-radius:12px; }
.ops-list .ops-table td:first-child{ overflow:visible; text-overflow:clip; }
.ig-chain-actions{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-top:6px; }
/* Liste « À trancher » commune (lot 3). */
.ig-cases-all{ margin:14px 0; padding:10px 12px; border:1px solid color-mix(in srgb, var(--bone) 14%, transparent); border-radius:8px; font-family:var(--text-font-family); }
.ig-cases-all:empty{ display:none; }
.ig-cases-filters{ margin:6px 0 8px; }
.ig-cases-list{ list-style:none; margin:0; padding:0; max-height:420px; overflow:auto; overscroll-behavior:none; }
.ig-cases-list li{ display:flex; gap:8px; align-items:baseline; flex-wrap:wrap; padding:5px 0; border-top:1px solid color-mix(in srgb, var(--bone) 10%, transparent); font-size:var(--fs-sm); }
.ig-case-trip{ font-weight:600; }
.ig-case-why{ opacity:0.85; }
.ig-cases-list .ig-replay{ margin-left:auto; }
/* Lancement par périmètre (lot 4). */
.ig-batch{ margin:0 0 12px; padding:10px 12px; border:1px solid color-mix(in srgb, var(--bone) 14%, transparent); border-radius:8px; font-family:var(--text-font-family); }
.ig-batch:empty{ display:none; }
.ig-batch-scopes{ display:flex; gap:12px; align-items:center; flex-wrap:wrap; font-size:var(--fs-sm); }
.ig-batch-head{ display:flex; gap:12px; align-items:center; flex-wrap:wrap; margin-top:8px; font-size:var(--fs-sm); font-weight:600; }
.ig-batch-list{ list-style:none; margin:6px 0 0; padding:0; max-height:260px; overflow:auto; overscroll-behavior:none; font-size:var(--fs-sm); }
.ig-batch-list li{ display:flex; gap:8px; align-items:baseline; padding:2px 0; }
.ig-batch-icon{ width:18px; text-align:center; }
.ig-batch-list li.is-done .ig-batch-icon{ color:var(--c-vert-moyen-3); }
.ig-batch-list li.is-error .ig-batch-icon{ color:var(--c-rouge-moyen-6); }
.ig-batch-skipped{ margin-top:6px; font-size:var(--fs-sm); }
.ig-batch-skipped ul{ margin:4px 0 0; padding-left:18px; }
.ig-upload-line{ display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin:4px 0 8px; } .ig-terminal-fallback{ margin-top:6px; font-size:var(--fs-sm); }

/* Système › E-mails (lots M2 et M3). */
.em-views{ margin-bottom:10px; }
.em-layout{ display:grid; grid-template-columns:minmax(0, 1fr) minmax(380px, 46%); gap:14px; align-items:start; }
@media (max-width: 1200px){ .em-layout{ grid-template-columns:minmax(0, 1fr); } }
.em-list{ min-width:0; overflow-x:auto; overscroll-behavior:none; font-family:var(--text-font-family); }
.em-list .em-table{ width:100%; table-layout:fixed; min-width:0 !important; }
.em-table tr[data-em-id]{ cursor:pointer; }
.em-table td.num{ text-align:right; }
.em-table td.em-fail{ color:var(--c-rouge-moyen-6); font-weight:700; }
.em-editor{ border:1px solid color-mix(in srgb, var(--bone) 16%, transparent); border-radius:8px; padding:12px 14px; font-family:var(--text-font-family); min-width:0; }
.em-head{ display:flex; align-items:center; gap:10px; min-height:var(--confirm-head-h); }
.em-head .panel-row-actions{ margin-left:auto; }
.em-editor .env-row{ grid-template-columns:110px minmax(0, 1fr); }
.em-body-row{ align-items:start; }
.em-editor textarea.admin-select{ width:100%; box-sizing:border-box; resize:vertical; font-family:var(--text-font-family); }
.em-vars{ flex-wrap:wrap; }
.em-period{ flex-wrap:wrap; gap:12px; font-size:var(--fs-sm); }
.em-actions{ display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin:10px 0; }
.em-preview{ width:100%; height:440px; border:1px solid color-mix(in srgb, var(--bone) 16%, transparent); border-radius:8px; background:var(--white); }
.em-log-table .em-st-failed{ color:var(--c-rouge-moyen-6); font-weight:700; }
.em-log-table .em-st-sent{ color:var(--c-vert-moyen-3); }
.em-log-table .em-err-row td{ font-size:var(--fs-xs); opacity:0.85; padding-top:0; }
/* Reprise des anciennes saisies (réintégration, 12/10). */
.ig-reprise-box{ border:1px solid color-mix(in srgb, var(--settings-tab-active-color) 45%, transparent); border-radius:8px; padding:8px 10px; margin:6px 0 10px; }
.ig-reprise-list{ list-style:none; margin:6px 0; padding:0; max-height:300px; overflow:auto; overscroll-behavior:none; font-size:var(--fs-sm); }
.ig-reprise-list li{ display:flex; flex-wrap:wrap; gap:4px 12px; align-items:baseline; padding:4px 0; border-top:1px solid color-mix(in srgb, var(--bone) 10%, transparent); }
.ig-reprise-list .ig-replay{ margin-left:auto; }
.ig-reprise-chg{ opacity:0.9; }

/* Agencement sur le site (point 8, 2026-10-12). */
.layout-toggle{ position:fixed; left:14px; bottom:46px; z-index:960; padding:6px 12px; border-radius:16px; border:1px solid color-mix(in srgb, var(--ember) 60%, transparent);
  background:color-mix(in srgb, var(--night) 88%, transparent); color:var(--bone); font-family:var(--text-font-family); font-size:var(--fs-sm); cursor:pointer; }
.layout-toggle.is-on{ background:var(--ember); color:var(--white); }
.layout-bar{ display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin:0 0 10px; font-family:var(--text-font-family); }
.layout-bar .settings-save-btn.is-on{ border-color:var(--ember); color:var(--ember); }
.layout-count{ margin-left:auto; font-size:var(--fs-sm); }
.layout-hint{ flex-basis:100%; font-size:var(--fs-xs); opacity:0.7; }
body.layout-on #activityGrid figure{ cursor:grab; user-select:none; }
body.layout-on #activityGrid figure.is-selected{ outline:3px solid var(--ember); outline-offset:2px; }
body.layout-on #activityGrid figure.is-hidden-photo, body.layout-on #activityGrid figure.is-missing-photo{ opacity:0.35; filter:grayscale(1); }
body.layout-on #activityGrid figure.is-shortcut::after{ content:"↪"; position:absolute; top:6px; left:6px; width:22px; height:22px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  background:color-mix(in srgb, var(--night) 75%, transparent); color:var(--bone); font-size:var(--fs-sm); }
.shortcut-badge{ position:absolute; left:6px; bottom:6px; padding:1px 6px; border-radius:8px; background:color-mix(in srgb, var(--night) 75%, transparent); color:var(--bone); font-size:var(--fs-xs); }
body.layout-on #diaporamaGrid .activity-card{ cursor:grab; } /* renommage-ok */
body.layout-on #diaporamaGrid .activity-card.is-hidden-etape{ opacity:0.4; } /* renommage-ok */
body.layout-dragging, body.layout-dragging *{ cursor:grabbing !important; user-select:none; }
.layout-ghost{ position:fixed; z-index:2147482500; pointer-events:none; padding:4px 10px; border-radius:10px; background:var(--ember); color:var(--white); font-family:var(--text-font-family); font-size:var(--fs-sm); }
.layout-marker{ position:fixed; z-index:2147482400; pointer-events:none; width:4px; border-radius:2px; background:var(--ember); }
.layout-marker.is-horizontal{ width:auto; height:4px; }
.layout-lasso{ position:fixed; z-index:2147482400; pointer-events:none; border:1px dashed var(--ember); background:color-mix(in srgb, var(--ember) 12%, transparent); }
.layout-panel{ position:fixed; top:0; right:0; bottom:0; width:320px; z-index:950; background:var(--night); color:var(--bone); font-family:var(--text-font-family);
  box-shadow:-4px 0 18px color-mix(in srgb, var(--black-pure) 35%, transparent); transition:transform .2s ease; }
.layout-panel.is-collapsed{ transform:translateX(320px); }
.layout-handle{ position:absolute; left:-26px; top:50%; width:26px; height:56px; border:none; border-radius:8px 0 0 8px; background:var(--night); color:var(--bone); cursor:pointer; }
.layout-panel-body{ height:100%; overflow:auto; overscroll-behavior:none; padding:14px; box-sizing:border-box; }
.layout-panel-head{ display:flex; align-items:center; justify-content:space-between; margin-bottom:10px; text-transform:uppercase; letter-spacing:0.05em; color:var(--ember); font-weight:700; font-size:var(--fs-sm); }
.layout-rows{ list-style:none; margin:0; padding:0; }
.layout-row{ display:grid; grid-template-columns:14px 52px minmax(0, 1fr) auto 22px; gap:8px; align-items:center; padding:6px; margin-bottom:6px; border:1px solid color-mix(in srgb, var(--bone) 14%, transparent); border-radius:8px; }
.layout-row.is-current{ border-color:var(--ember); }
.layout-row.is-off{ opacity:0.55; }
.layout-grip{ cursor:grab; opacity:0.6; }
.layout-thumb{ width:52px; height:36px; border-radius:4px; overflow:hidden; background:color-mix(in srgb, var(--bone) 10%, transparent); }
.layout-thumb img{ width:100%; height:100%; object-fit:cover; display:block; }
.layout-name{ min-width:0; cursor:pointer; }
.layout-name b{ display:block; font-size:var(--fs-sm); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.layout-name small{ font-size:var(--fs-xs); opacity:0.7; }
.layout-del{ border:none; background:none; color:inherit; opacity:0.6; cursor:pointer; font-size:var(--fs-md); }
.layout-del:hover{ opacity:1; }
.layout-drop-new, .layout-drop-trash{ margin-top:8px; padding:10px; border:1px dashed color-mix(in srgb, var(--bone) 30%, transparent); border-radius:8px; text-align:center; font-size:var(--fs-sm); }
.layout-panel .is-drop-over{ outline:2px solid var(--ember); background:color-mix(in srgb, var(--ember) 18%, transparent); }
.layout-panel-note{ font-size:var(--fs-xs); opacity:0.65; margin-top:10px; }
.activity-empty-cover{ display:block; width:100%; aspect-ratio:16 / 9; background:color-mix(in srgb, var(--bone) 12%, transparent); }
/* Onglet Photos : colonnes d'agencement (point 8, A6). */
#pmTbody .pm-etape{ width:100%; max-width:100%; height:24px; font-size:var(--fs-xs); }
#pmTbody .pm-pos{ width:52px; height:24px; text-align:right; font-size:var(--fs-xs); }
.layout-bar .settings-save-btn.is-danger{ border-color:var(--c-rouge-moyen); color:var(--c-rouge-moyen); }

/* Système › Services Google (2026-10-12, projet/etudes/ETUDE_SUIVI_GOOGLE.md). */
.gu-box{ display:flex; flex-direction:column; gap:14px; font-family:var(--text-font-family); }
.gu-title{ margin:0; font-size:var(--fs-md); }
.gu-intro{ margin:0; font-size:var(--fs-sm); opacity:0.85; line-height:1.5; }
.gu-alert{ margin:0; padding:8px 12px; border:1px solid var(--c-orange-moyen); border-radius:8px; color:var(--c-orange-moyen); font-size:var(--fs-sm); }
.gu-cards{ display:grid; grid-template-columns:repeat(3, minmax(0, 1fr)); gap:14px; }
.gu-card, .gu-panel{ border:1px solid color-mix(in srgb, var(--bone) 18%, transparent); border-radius:10px; padding:12px 14px; min-width:0; }
.gu-card.is-off{ opacity:0.55; }
.gu-card-head{ display:flex; align-items:center; gap:8px; font-size:var(--fs-sm); }
.gu-soon{ font-size:var(--fs-2xs); padding:1px 6px; border-radius:6px; background:color-mix(in srgb, var(--bone) 12%, transparent); }
.gu-card-sub{ font-size:var(--fs-xs); opacity:0.7; margin:2px 0 10px; }
.gu-line{ display:flex; justify-content:space-between; align-items:baseline; gap:8px; font-size:var(--fs-sm); margin-top:8px; }
.gu-big{ font-size:var(--fs-2xl); }
.gu-big.is-warn, .gu-warn{ color:var(--c-orange-moyen); }
.gu-bar{ height:6px; border-radius:3px; background:color-mix(in srgb, var(--bone) 12%, transparent); overflow:hidden; margin-top:4px; }
.gu-bar i{ display:block; height:100%; }
.gu-card-note{ font-size:var(--fs-xs); opacity:0.75; margin:10px 0 0; line-height:1.5; }
.gu-charts, .gu-bottom{ display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:14px; }
.gu-panel-head{ display:flex; justify-content:space-between; align-items:baseline; gap:10px; font-size:var(--fs-sm); margin-bottom:8px; }
.gu-legend{ font-size:var(--fs-xs); opacity:0.75; display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; }
.gu-legend i{ display:inline-block; width:9px; height:9px; border-radius:2px; margin-left:6px; }
.gu-legend i.gu-legend-thr{ height:2px; background:var(--c-orange-moyen); }
.gu-chart{ width:100%; height:190px; display:block; }
.gu-chart .gu-grid{ stroke:color-mix(in srgb, var(--bone) 12%, transparent); stroke-width:1; }
.gu-chart .gu-thr{ stroke-width:1; stroke-dasharray:5 4; }
.gu-chart .gu-axis{ fill:color-mix(in srgb, var(--bone) 60%, transparent); font-size:var(--fs-3xs); font-family:var(--text-font-family); }
.gu-history{ max-height:340px; overflow:auto; overscroll-behavior:none; }
.gu-table{ width:100%; }
.gu-table .gu-month td{ font-weight:700; }
.gu-input{ width:90px; text-align:right; font:inherit; font-size:var(--fs-sm); color:inherit; background:color-mix(in srgb, var(--bone) 8%, transparent); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); border-radius:6px; padding:3px 6px; }
.gu-links{ font-size:var(--fs-sm); margin:10px 0 0; }
.gu-link{ margin-right:14px; color:var(--ember); font-size:var(--fs-sm); }
.gu-etapes-link{ margin-left:auto; font-size:var(--fs-xs); opacity:0.7; color:inherit; text-decoration:none; white-space:nowrap; }
.gu-etapes-link b{ color:var(--ember); font-weight:400; }
.gu-etapes-link.is-warn{ opacity:1; color:var(--c-orange-moyen); }
body.settings-modal-open .layout-toggle, body.settings-modal-open .layout-panel{ visibility:hidden; }

/* Écran Intégration refait (14ᵉ message, 12/10 soir, maquette
   integration_refonte_maquette.html) : deux polices seulement (titres du
   thème, texte du thème), une phrase d'état, un bouton, quatre étapes, les
   points à régler ; le reste replié (.ig-fold). */
.settings-tabpanel[data-tabpanel="integration"], .settings-tabpanel[data-tabpanel="integration"] :is(button, select, input, summary, code, pre, a){ font-family:var(--text-font-family); }
.integration-guide.ig-v2{ gap:14px; padding:16px 18px; }
.ig-top{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
.ig-top-gap{ flex:1 1 auto; }
.ig-reprise-head{ display:flex; align-items:center; gap:14px; flex-wrap:wrap; }
.ig-reprise-head > div{ flex:1 1 320px; }
.settings-tabpanel[data-tabpanel="integration"] .ig-trip-select{ height:38px; padding:0 10px; border-radius:8px; border:1px solid color-mix(in srgb, var(--bone) 28%, transparent); background:color-mix(in srgb, var(--bone) 6%, transparent); color:var(--settings-label-color); font-family:var(--title-font-family); font-size:var(--fs-lg); max-width:100%; }
.ig-pill{ display:inline-flex; align-items:center; height:26px; padding:0 12px; border-radius:999px; border:1px solid currentColor; font-size:var(--fs-xs); white-space:nowrap; }
.ig-pill.is-ok{ color:var(--ig-ok); } .ig-pill.is-warn{ color:var(--ig-warn); } .ig-pill.is-err{ color:var(--ig-error); }
.ig-pill.is-off{ color:var(--settings-default-text-color); opacity:0.75; }
.ig-link{ background:none; border:0; padding:0; color:var(--ember); font-size:var(--fs-sm); text-decoration:underline; text-underline-offset:3px; cursor:pointer; }
.ig-state{ display:flex; flex-direction:column; gap:10px; padding:18px 20px; border-radius:12px; border:1px solid color-mix(in srgb, var(--bone) 16%, transparent); background:color-mix(in srgb, var(--bone) 5%, transparent); }
.ig-state-title{ margin:0; font-family:var(--title-font-family); font-weight:400; font-size:var(--fs-4xl); color:var(--settings-label-color); }
.ig-state-text{ margin:0; font-size:var(--fs-md); line-height:1.55; max-width:62em; }
.ig-state .ig-chain-live:empty{ display:none; }
.ig-visible{ display:flex; gap:8px; align-items:baseline; font-size:var(--fs-sm); opacity:0.85; }
.ig-visible.is-on > span{ color:var(--ig-ok); } .ig-visible.is-off > span{ opacity:0.6; }
.ig-state-actions{ display:flex; flex-wrap:wrap; gap:10px; align-items:center; min-height:44px; }
.ig-hero-btn{ min-height:44px; padding:0 22px; font-size:var(--fs-md); display:inline-flex; align-items:center; }
.ig-tiles{ display:grid; grid-template-columns:repeat(4, minmax(0, 1fr)); gap:10px; }
.ig-tile{ display:flex; flex-direction:column; gap:4px; padding:12px 14px; border-radius:10px; border:1px solid color-mix(in srgb, var(--bone) 14%, transparent); background:color-mix(in srgb, var(--bone) 4%, transparent); }
.ig-tile-n{ font-size:var(--fs-xs); opacity:0.6; }
.ig-tile-t{ font-size:var(--fs-md); color:var(--settings-label-color); }
.ig-tile-s{ font-size:var(--fs-xs); opacity:0.7; }
.ig-tile.is-done{ border-color:color-mix(in srgb, var(--ig-ok) 45%, transparent); } .ig-tile.is-done .ig-tile-s{ color:var(--ig-ok); opacity:1; }
.ig-tile.is-now{ border-color:var(--ember); background:color-mix(in srgb, var(--ember) 10%, transparent); } .ig-tile.is-now .ig-tile-s{ color:var(--ember); opacity:1; }
.ig-tile.is-warn{ border-color:color-mix(in srgb, var(--ig-warn) 55%, transparent); } .ig-tile.is-warn .ig-tile-s{ color:var(--ig-warn); opacity:1; }
.ig-tile.is-err{ border-color:color-mix(in srgb, var(--ig-error) 55%, transparent); } .ig-tile.is-err .ig-tile-s{ color:var(--ig-error); opacity:1; }
@media (max-width:720px){ .ig-tiles{ grid-template-columns:repeat(2, minmax(0, 1fr)); } }
.ig-v2 .ig-reprise-box{ margin:0; padding:12px 16px; border-radius:12px; border-color:color-mix(in srgb, var(--ig-warn) 55%, transparent); background:color-mix(in srgb, var(--ig-warn) 7%, transparent); }
.ig-reprise-fold > summary{ cursor:pointer; font-size:var(--fs-sm); color:var(--ember); margin-top:6px; }
.ig-points{ display:flex; flex-direction:column; gap:8px; padding:14px 18px; border-radius:12px; border:1px solid color-mix(in srgb, var(--bone) 14%, transparent); }
.ig-points-title{ font-size:var(--fs-md); font-weight:600; color:var(--settings-label-color); }
.ig-good > summary{ cursor:pointer; font-size:var(--fs-sm); opacity:0.8; }
.ig-fold{ margin:12px 0 0; padding:10px 14px; border-radius:10px; border:1px solid color-mix(in srgb, var(--bone) 12%, transparent); color:var(--settings-default-text-color); }
.ig-fold > summary{ cursor:pointer; font-size:var(--fs-sm); opacity:0.8; }
.ig-fold[open] > summary{ margin-bottom:10px; }
.ig-fold .ig-batch, .ig-fold .ig-cases-all{ margin:0; border:0; padding:0; }
.integration-guide.ig-tech{ border:0; padding:0; background:none; }

/* Intégration — retours du 12/10 (point D) : UNE couleur d'accent (orangé),
   réservée à ce qu'il reste à faire ; vert quand tout est bon ; titres et
   boutons secondaires dans la couleur du texte. Le pavé du voyage porte son
   détail (« Voir le détail ») ; « Plusieurs voyages » le remplace. */
.settings-tabpanel[data-tabpanel="integration"]{ --ig-accent:var(--c-orange-moyen-8); --ig-good:var(--c-vert-moyen-3); }
.settings-tabpanel[data-tabpanel="integration"] .integration-guide{ --ig-warn:var(--ig-accent); --ig-ok:var(--ig-good); }
.settings-tabpanel[data-tabpanel="integration"] :is(.ig-state-title, .ig-tile-t, .ig-points-title, .ig-trip-select, .ig-part-title){ color:var(--settings-default-text-color); }
.settings-tabpanel[data-tabpanel="integration"] .ig-btn{ background:transparent; border:1px solid color-mix(in srgb, var(--settings-default-text-color) 35%, transparent); color:var(--settings-default-text-color); }
.settings-tabpanel[data-tabpanel="integration"] .ig-btn:hover:not(:disabled){ border-color:var(--settings-default-text-color); }
.settings-tabpanel[data-tabpanel="integration"] .ig-btn.is-primary{ background:var(--ig-accent); border-color:var(--ig-accent); color:var(--night); font-weight:600; }
.settings-tabpanel[data-tabpanel="integration"] .ig-btn.is-primary:hover:not(:disabled){ background:color-mix(in srgb, var(--ig-accent) 85%, var(--white)); }
.settings-tabpanel[data-tabpanel="integration"] .ig-btn:disabled{ opacity:0.6; cursor:progress; }
.settings-tabpanel[data-tabpanel="integration"] .ig-link{ color:var(--settings-default-text-color); opacity:0.85; }
.settings-tabpanel[data-tabpanel="integration"] .ig-link:hover{ opacity:1; }
.ig-tile.is-now{ border-color:var(--ig-accent); background:color-mix(in srgb, var(--ig-accent) 8%, transparent); } .ig-tile.is-now .ig-tile-s{ color:var(--ig-accent); }
.ig-state.is-ok{ border-color:color-mix(in srgb, var(--ig-good) 40%, transparent); }
.ig-points.is-ok{ border-color:color-mix(in srgb, var(--ig-good) 30%, transparent); }
.ig-points-title.is-ok{ font-weight:400; opacity:0.9; }
.ig-v2 .ig-reprise-box{ border-color:color-mix(in srgb, var(--ig-accent) 45%, transparent); background:color-mix(in srgb, var(--ig-accent) 6%, transparent); }
.ig-v2 .ig-reprise-box.is-done{ border-color:color-mix(in srgb, var(--ig-good) 40%, transparent); background:none; color:var(--ig-good); font-size:var(--fs-sm); }
.ig-reprise-count{ margin-top:4px; font-size:var(--fs-sm); color:var(--ig-good); }
.ig-reprise-fold > summary{ color:var(--settings-default-text-color); opacity:0.85; text-decoration:underline; text-underline-offset:3px; }
/* Contrôles en ordre : leurs actions en lien discret, jamais un gros bouton. */
.ig-good .ig-inline-action :is(.settings-save-btn, button, a){ background:none; border:0; padding:0; min-height:0; height:auto; font-size:var(--fs-sm); font-weight:400; color:var(--settings-default-text-color); opacity:0.75; text-decoration:underline; text-underline-offset:3px; box-shadow:none; }
.ig-good .ig-inline-action{ margin-top:2px; }
/* Pavé du voyage + son détail. */
.ig-card{ border:1px solid color-mix(in srgb, var(--bone) 18%, transparent); border-radius:12px; background:color-mix(in srgb, var(--bone) 4%, transparent); }
.ig-card > .integration-guide.ig-v2{ border:0; background:none; }
.ig-detail-fold{ margin:0 18px; padding:10px 0 14px; border-top:1px solid color-mix(in srgb, var(--bone) 12%, transparent); }
.ig-detail-fold > summary{ cursor:pointer; font-size:var(--fs-sm); color:var(--settings-default-text-color); opacity:0.85; }
.ig-detail-fold[open] > summary{ margin-bottom:12px; }
.ig-batch-toggle{ height:38px; }
/* Plusieurs voyages : tuiles de catégorie et liste des voyages. */
.ig-batch-state.is-on{ color:var(--ig-good); } .ig-batch-state.is-off{ opacity:0.7; }
.settings-tabpanel[data-tabpanel="integration"] .ig-replay{ color:var(--settings-default-text-color); opacity:0.8; }
/* Tout est bon : « Ouvrir le voyage en ligne » en vert calme, jamais en alerte. */
.settings-tabpanel[data-tabpanel="integration"] .ig-btn.is-calm{ border-color:color-mix(in srgb, var(--ig-good) 55%, transparent); color:var(--ig-good); }
/* « ← Retour à … » (12/10, point E, « aller droit au travail ») : au bout de
   la barre des onglets, sans rien déplacer. */
#accountTopTabs .work-return-chip{ margin-left:auto; align-self:center; height:32px; padding:0 14px; border-radius:999px; cursor:pointer; font-family:var(--text-font-family); font-size:var(--fs-sm); color:var(--night); background:var(--c-orange-moyen-8); border:1px solid var(--c-orange-moyen-8); font-weight:600; white-space:nowrap; }
#accountTopTabs .work-return-chip:hover{ background:color-mix(in srgb, var(--c-orange-moyen-8) 85%, var(--white)); }


/* Smart Search, lot S1 (13/10, /auth/recherche.php, administrateurs) : une
   seule scène — le globe en fond, la question au centre, les résultats
   par-dessus. Polices et couleurs du thème ; accent orangé pour l'action. */
.ss-body{ margin:0; min-height:100vh; background:var(--night); color:var(--bone); font-family:var(--text-font-family); overflow-x:hidden; }
.ss-globe{ position:fixed; inset:0; z-index:0; }
.ss-globe.is-off{ background:radial-gradient(circle at 50% 40%, color-mix(in srgb, var(--bone) 8%, transparent), transparent 60%); }
.ss-head{ position:fixed; top:0; left:0; right:0; z-index:3; display:flex; align-items:center; gap:10px; padding:14px 20px; }
.ss-home{ color:var(--bone); opacity:0.8; text-decoration:none; font-size:var(--fs-sm); margin-right:auto; }
.ss-home:hover{ opacity:1; }
.ss-light{ display:inline-flex; gap:6px; }
.ss-chip{ height:32px; padding:0 14px; border-radius:999px; cursor:pointer; font-family:var(--text-font-family); font-size:var(--fs-sm); color:var(--bone);
  background:color-mix(in srgb, var(--night) 55%, transparent); border:1px solid color-mix(in srgb, var(--bone) 30%, transparent); backdrop-filter:blur(6px); }
.ss-chip:hover{ border-color:var(--bone); }
.ss-chip.is-on{ border-color:var(--c-orange-moyen-8); color:var(--c-orange-moyen-8); }
.ss-chip.is-accent{ background:var(--c-orange-moyen-8); border-color:var(--c-orange-moyen-8); color:var(--night); font-weight:600; }
.ss-main{ position:relative; z-index:2; max-width:1180px; margin:0 auto; padding:16vh 20px 60px; pointer-events:none; }
.ss-main > *{ pointer-events:auto; }
.ss-title{ margin:0 0 18px; text-align:center; font-family:var(--title-font-family); font-weight:400; font-size:var(--fs-7xl); color:var(--bone); text-shadow:0 2px 18px var(--night), 0 0 4px var(--night); }
.ss-tabs[hidden]{ display:none; }
.ss-form{ display:flex; justify-content:center; }
.ss-input{ width:min(760px, 100%); height:52px; padding:0 22px; border-radius:999px; font-family:var(--text-font-family); font-size:var(--fs-xl); color:var(--bone);
  background:color-mix(in srgb, var(--night) 70%, transparent); border:1px solid color-mix(in srgb, var(--bone) 35%, transparent); backdrop-filter:blur(8px); outline:none; }
.ss-input:focus{ border-color:var(--c-orange-moyen-8); }
.ss-understood{ min-height:1.4em; margin:12px auto 0; max-width:760px; text-align:center; font-size:var(--fs-sm); color:color-mix(in srgb, var(--bone) 80%, transparent); }
.ss-msg{ min-height:1.2em; margin:6px auto 0; max-width:760px; text-align:center; font-size:var(--fs-sm); }
.ss-msg.is-error{ color:var(--c-rouge-moyen-6); } .ss-msg.is-warn{ color:var(--c-orange-moyen-8); } .ss-msg.is-success{ color:var(--c-vert-moyen-3); }
.ss-tabs{ display:flex; justify-content:center; gap:8px; margin:18px 0 12px; }
.ss-tab{ height:34px; padding:0 16px; border-radius:999px; cursor:pointer; font-family:var(--text-font-family); font-size:var(--fs-md); color:var(--bone); background:color-mix(in srgb, var(--night) 60%, transparent); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); }
.ss-tab.is-on{ border-color:var(--c-orange-moyen-8); color:var(--c-orange-moyen-8); }
.ss-results{ background:color-mix(in srgb, var(--night) 72%, transparent); backdrop-filter:blur(10px); border-radius:14px; }
.ss-results:empty{ display:none; }
.ss-empty{ margin:0; padding:22px; text-align:center; font-size:var(--fs-md); opacity:0.85; }
.ss-trips{ display:grid; grid-template-columns:repeat(auto-fill, minmax(240px, 1fr)); gap:10px; padding:14px; max-height:56vh; overflow:auto; overscroll-behavior:none; }
.ss-trip{ display:flex; flex-direction:column; gap:4px; padding:12px 14px; border-radius:10px; text-decoration:none; color:var(--bone); border:1px solid color-mix(in srgb, var(--bone) 18%, transparent); background:color-mix(in srgb, var(--bone) 4%, transparent); }
.ss-trip:hover{ border-color:var(--c-orange-moyen-8); }
.ss-trip.is-off{ opacity:0.7; }
.ss-trip-name{ font-family:var(--title-font-family); font-size:var(--fs-2xl); }
.ss-trip-sub{ font-size:var(--fs-xs); opacity:0.75; }
.ss-actions{ display:flex; justify-content:center; padding:12px; }
.ss-mosaic{ display:grid; grid-template-columns:repeat(auto-fill, minmax(170px, 1fr)); gap:6px; padding:0 12px 12px; max-height:58vh; overflow:auto; overscroll-behavior:none; }
.ss-tile{ position:relative; margin:0; border-radius:8px; overflow:hidden; cursor:pointer; background:color-mix(in srgb, var(--bone) 6%, transparent); aspect-ratio:4 / 3; }
.ss-tile img{ width:100%; height:100%; object-fit:cover; display:block; transition:transform .25s ease; }
.ss-tile:hover img{ transform:scale(1.04); }
.ss-tile figcaption{ position:absolute; left:0; right:0; bottom:0; padding:16px 8px 6px; font-size:var(--fs-2xs); color:var(--white); /* theme-ok : texte blanc sur photo */
  background:linear-gradient(transparent, color-mix(in srgb, var(--black-pure) 70%, transparent)); }
.ss-badges{ float:right; }
.ss-pin{ position:absolute; top:6px; right:6px; width:28px; height:28px; border-radius:50%; border:0; cursor:pointer; font-size:var(--fs-sm); opacity:0; background:color-mix(in srgb, var(--night) 70%, transparent); transition:opacity .15s ease; }
.ss-tile:hover .ss-pin, .ss-pin.is-on{ opacity:1; }
.ss-show{ position:fixed; inset:0; z-index:10; display:flex; align-items:center; justify-content:center; background:color-mix(in srgb, var(--black-pure) 92%, transparent); }
.ss-show[hidden]{ display:none; }
.ss-show img{ max-width:94vw; max-height:86vh; object-fit:contain; }
.ss-show-cap{ position:absolute; bottom:18px; left:0; right:0; text-align:center; font-size:var(--fs-sm); color:var(--bone); }
.ss-show-btn{ position:absolute; width:48px; height:48px; border-radius:50%; cursor:pointer; font-size:var(--fs-3xl); color:var(--bone); background:color-mix(in srgb, var(--night) 60%, transparent); border:1px solid color-mix(in srgb, var(--bone) 30%, transparent); }
.ss-show-btn.is-prev{ left:20px; top:50%; } .ss-show-btn.is-next{ right:20px; top:50%; } .ss-show-btn.is-close{ right:20px; top:20px; }
.ss-tile img.is-missing{ visibility:hidden; }
/* Pastille en défaut d'Environnement : mène à l'écran concerné (12/10). */
.env-pill.is-link{ cursor:pointer; text-decoration:underline; text-underline-offset:3px; }
/* Trait de redimensionnement d'une colonne (écart 8k, 13/10) : suit la
   souris pendant le glissé, la largeur s'applique au lâcher. */
.col-resize-guide{ position:fixed; width:2px; margin-left:-1px; background:var(--ember); z-index:10050; pointer-events:none; will-change:transform; }

/* Barre d'outils des grilles (standard du 13/10, retour de Thierry J6) :
   filtres, puces de filtre et champ de recherche ont la MÊME hauteur, la
   même police, la même taille et le même interligne. */
:root{ --toolbar-ctl-h:32px; }
.users-modal.account-modal :is(.users-toolbar-filters select, .users-toolbar-filters .filter-combo-input, .srw > input[id$="Search"], .ops-chip, .em-toolbar select){
  height:var(--toolbar-ctl-h); box-sizing:border-box; font-family:var(--text-font-family); font-size:var(--fs-sm); line-height:normal; }
.users-modal.account-modal .ops-chip{ display:inline-flex; align-items:center; padding:0 12px; }

/* Gabarits d'écran et composant de grille standard (13/10, projet/standards/GRID_STANDARD.md
   § 9-10). Gabarit B (grille + éditeur dessous) : titre, phrase d'aide,
   pavé de réglages communs, barre d'outils, grille, éditeur. Aéré. */
/* Pas de display ici (13/10, bug) : un panneau d'onglet reste caché tant
   qu'il n'est pas actif (.settings-tabpanel / .active). */
.std-screen{ flex-direction:column; gap:18px; }
.std-title{ margin:0; font-family:var(--title-font-family); font-weight:400; font-size:var(--fs-4xl); color:var(--settings-default-text-color); }
.std-intro{ margin:-8px 0 0; font-size:var(--fs-sm); opacity:0.8; }
.std-card{ padding:18px 22px; border-radius:12px; border:1px solid color-mix(in srgb, var(--bone) 14%, transparent); background:color-mix(in srgb, var(--bone) 3%, transparent); }
.std-card[hidden]{ display:none; }
.std-card-title{ font-family:var(--title-font-family); font-size:var(--fs-2xl); color:var(--settings-default-text-color); margin-bottom:12px; }
.std-field{ display:flex; flex-direction:column; gap:6px; font-size:var(--fs-sm); }
.std-field > span:first-child, .std-field-label{ font-size:var(--fs-xs); opacity:0.75; }
.std-field input, .std-field select, .std-field textarea{ font-family:var(--text-font-family); font-size:var(--fs-md); }
.em-common-grid{ display:grid; grid-template-columns:minmax(200px, 1fr) minmax(220px, 1fr) 140px; gap:18px; align-items:end; }
.std-toolbar{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin-bottom:10px; }
.std-toolbar .em-views{ margin:0; }
.std-filters{ flex:0 1 auto; align-items:center; }
.std-filters .std-search{ min-width:220px; }
.std-grid-box .std-scroll{ border:1px solid color-mix(in srgb, var(--bone) 12%, transparent); border-radius:10px; }
.std-grid tbody tr[data-std-id]{ cursor:pointer; }
.std-grid tbody tr.std-group-row td{ font-family:var(--title-font-family); font-size:var(--fs-lg); padding-top:14px; color:var(--settings-default-text-color); background:color-mix(in srgb, var(--bone) 3%, transparent); }
.std-grid:focus{ outline:none; }
.std-note{ margin:8px 0 0; }
/* Interrupteurs DANS une grille : taille compacte (standard, toutes les grilles). */
.users-modal-table td .switch, .switch.switch-compact{ width:28px; height:16px; }
.users-modal-table td .switch .switch-track::before, .switch.switch-compact .switch-track::before{ width:12px; height:12px; top:2px; left:2px; }
.users-modal-table td .switch input:checked + .switch-track::before, .switch.switch-compact input:checked + .switch-track::before{ transform:translateX(12px); }
/* Éditeur du modèle (sous la grille). */
.em-ed-head{ display:flex; align-items:flex-start; justify-content:space-between; gap:16px; flex-wrap:wrap; margin-bottom:14px; }
.em-ed-actions{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.em-ed-actions .sqlops-views{ margin:0; }
.em-ed-body{ display:grid; grid-template-columns:minmax(0, 1fr) minmax(0, 1fr); gap:22px; }
.em-ed-form{ display:flex; flex-direction:column; gap:14px; }
.em-ed-preview{ display:flex; flex-direction:column; gap:6px; }
.em-ed-preview .em-preview{ width:100%; min-height:420px; border:0; border-radius:10px; background:var(--bone); }
.settings-save-btn.is-accent{ background:var(--c-orange-moyen-8); border-color:var(--c-orange-moyen-8); color:var(--night); font-weight:600; }
.em-err{ color:var(--c-rouge-moyen-6); }
@media (max-width:900px){ .em-ed-body, .em-common-grid{ grid-template-columns:1fr; } }
.users-modal.account-modal .settings-save-btn.is-accent:not(:disabled){ background:var(--c-orange-moyen-8); border-color:var(--c-orange-moyen-8); color:var(--night); opacity:1; }
.std-intro, .std-field, .std-card{ font-family:var(--text-font-family); }
.em-period{ display:flex; align-items:center; gap:16px; flex-wrap:wrap; }
.em-period label{ display:inline-flex; align-items:center; gap:6px; }
/* N0 relié à Smart Search (13/10, S2). */
.n0-smart-item{ font-weight:600; text-decoration:none; }
.n0-smart-link{ color:inherit; text-decoration:underline; text-underline-offset:3px; }
.ss-viewer .ss-pin{ display:none; }
.ss-chip[hidden]{ display:none; }


/* Mini-carte flottante N1 (13/10, option A : sans Google, coin bas droit,
   au-dessus du bandeau défilant de 34 px ; par-dessus la page). */
.mini-map-n1{ position:fixed; right:16px; bottom:calc(34px + 12px); z-index:40; width:260px; height:200px; border-radius:10px; overflow:hidden; cursor:pointer;
  background:color-mix(in srgb, var(--night) 88%, transparent); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent);
  box-shadow:0 6px 24px color-mix(in srgb, var(--black-pure) 45%, transparent); }
.mini-map-n1[hidden]{ display:none; }
.mini-map-n1 svg{ display:block; }
.mini-map-n1 .mm-land{ fill:color-mix(in srgb, var(--bone) 16%, transparent); stroke:color-mix(in srgb, var(--bone) 55%, transparent); stroke-width:1; }
.mini-map-n1 .mm-pt{ fill:var(--ember, var(--c-orange-moyen-2)); stroke:var(--white); stroke-width:1; opacity:0.85; transition:r .15s ease; }
.mini-map-n1 .mm-pt.is-on{ fill:var(--c-or-moyen-2); r:8; opacity:1; }
.mini-map-n1 .mm-label{ position:absolute; left:10px; right:36px; bottom:8px; color:var(--bone); font:600 var(--fs-xs)/1.2 var(--text-font-family); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; pointer-events:none; }
.mini-map-n1 .mm-close{ position:absolute; top:6px; right:6px; width:24px; height:24px; border:none; border-radius:12px; cursor:pointer;
  background:color-mix(in srgb, var(--night) 70%, transparent); color:var(--bone); font-size:var(--fs-sm); line-height:1; }

/* Logo zemb.com (piste 3, 13/10) dans la barre du haut de N0 (home.js). */
.n0-topbar .site-logo{ display:flex; align-items:center; height:100%; padding-left:16px; color:var(--dusk); text-decoration:none; }
.n0-topbar .site-logo svg{ height:30px; width:auto; display:block; }

/* Mode mesure (13/10) : compteur discret en bas, au centre, au-dessus du
   bandeau défilant ; par-dessus la page. */
.perf-counter{ position:fixed; left:50%; transform:translateX(-50%); bottom:calc(34px + 8px); z-index:60; padding:3px 10px; border-radius:999px; pointer-events:none;
  background:color-mix(in srgb, var(--night) 80%, transparent); color:var(--bone); font:500 var(--fs-2xs)/1.4 var(--text-font-family); white-space:nowrap; }
.perf-switch{ display:flex; align-items:center; gap:10px; cursor:pointer; }

/* Consentements (13/10, point Q ; consent.js) : encart à la connexion et
   bloc de Mon profil › Consentements, d'après la maquette validée. */
.consent-backdrop{ position:fixed; inset:0; z-index:400; display:flex; align-items:center; justify-content:center; padding:24px; background:color-mix(in srgb, var(--ink) 60%, transparent); }
.consent-modal{ width:100%; max-width:720px; max-height:88vh; overflow:auto; overscroll-behavior:none; padding:26px 28px; border-radius:12px; background:var(--settings-bg); color:var(--bone); box-shadow:0 20px 45px color-mix(in srgb, var(--ink) 35%, transparent); }
.consent-modal-title{ margin:0 0 16px; font-family:var(--title-font-family); font-weight:400; font-size:var(--fs-4xl); }
.consent-modal-foot{ display:flex; justify-content:flex-end; gap:10px; margin-top:16px; }
.consent-person + .consent-person{ margin-top:18px; }
.consent-person-name{ font:600 var(--fs-sm)/1.4 var(--text-font-family); letter-spacing:.06em; text-transform:uppercase; margin-bottom:8px; opacity:.85; }
.consent-card{ padding:18px 22px; border-radius:12px; border:1px solid color-mix(in srgb, var(--bone) 14%, transparent); background:color-mix(in srgb, var(--bone) 3%, transparent); }
.consent-card + .consent-card{ margin-top:14px; }
.consent-card-title{ margin:0 0 10px; font-family:var(--title-font-family); font-weight:500; font-size:var(--fs-2xl); }
.consent-text{ font-size:var(--fs-sm); line-height:1.6; }
.consent-text p{ margin:0 0 8px; }
.consent-text ul{ margin:0 0 4px; padding-left:18px; opacity:.9; }
.consent-actions{ display:flex; align-items:center; flex-wrap:wrap; gap:10px; margin-top:12px; min-height:var(--toolbar-ctl-h, 32px); }
.consent-btn{ padding:8px 18px; border-radius:6px; cursor:pointer; font:600 var(--fs-sm)/1.2 var(--text-font-family); background:transparent; color:var(--bone); border:1px solid color-mix(in srgb, var(--bone) 30%, transparent); }
.consent-btn.is-yes{ background:var(--c-orange-moyen-2); border-color:var(--c-orange-moyen-2); color:var(--night); }
.consent-btn[hidden]{ display:none; }
.consent-pill{ display:inline-flex; align-items:center; gap:6px; font-size:var(--fs-sm); }
.consent-pill::before{ content:''; width:8px; height:8px; border-radius:50%; background:currentColor; }
.consent-pill.is-yes{ color:var(--c-vert-moyen-2, var(--bone)); }
.consent-pill.is-no{ color:var(--c-rouge-moyen-2, var(--bone)); }
.consent-when{ font-size:var(--fs-xs); opacity:.75; }
.consent-link{ background:none; border:none; padding:0; cursor:pointer; color:inherit; text-decoration:underline; font-size:var(--fs-xs); }
.consent-note{ font-size:var(--fs-xs); opacity:.75; margin:12px 0 0; line-height:1.5; }
.consent-protect{ margin-top:14px; font-size:var(--fs-xs); line-height:1.6; }
.consent-protect summary{ cursor:pointer; font-weight:600; font-size:var(--fs-sm); }
.consent-protect ul{ margin:6px 0; padding-left:18px; }
.consent-protect-sub{ font-weight:600; margin-top:8px; }
.profile-consents{ margin-top:24px; }
.profile-consents[hidden]{ display:none; }

/* Grille standard (13/10, point Q.9 de Thierry) : chaque colonne garde sa
   largeur sur toutes les lignes ; un contenu long est coupé (…), il ne
   pousse jamais la colonne suivante ; interrupteurs, pastilles et nombres
   alignés les uns sous les autres. */
.std-grid{ table-layout:fixed; }
.std-grid tbody td{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.std-grid tbody tr.std-group-row td{ white-space:normal; }
.std-grid .std-check{ width:36px; text-align:center; }

/* Utilisateurs (13/10, point Q) : grille standard + fiche à droite. */
.std-split{ display:grid; grid-template-columns:minmax(0, 1fr) minmax(420px, 560px); gap:18px; align-items:start; min-height:0; }
.std-detail{ min-width:0; }
.std-detail-inner{ display:flex; flex-direction:column; gap:14px; }
.std-count{ font-size:var(--fs-xs); opacity:.8; }
.user-cell{ display:inline-flex; align-items:center; gap:8px; min-width:0; }
.user-cell > span:last-child{ overflow:hidden; text-overflow:ellipsis; }
.user-ava{ flex:none; width:28px; height:28px; border-radius:50%; object-fit:cover; display:inline-flex; align-items:center; justify-content:center; background:var(--c-or-moyen-2); color:var(--night); font:600 var(--fs-2xs)/1 var(--text-font-family); }
.user-ava.is-small{ width:24px; height:24px; }
.user-ava.is-big{ width:52px; height:52px; font-size:var(--fs-lg); }
.user-head{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; padding-bottom:12px; border-bottom:1px solid color-mix(in srgb, var(--bone) 12%, transparent); }
.user-head-txt{ flex:1 1 160px; min-width:0; }
.user-head-name{ font-family:var(--title-font-family); font-size:var(--fs-3xl); color:var(--settings-default-text-color); }
.user-head-sub{ font-size:var(--fs-xs); opacity:.8; }
.user-head-actions{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.user-grid{ display:grid; grid-template-columns:repeat(3, minmax(0, 1fr)); gap:12px 14px; }
.user-grid .std-field.is-wide{ grid-column:span 2; }
.std-field{ display:flex; flex-direction:column; gap:4px; font-size:var(--fs-xs); min-width:0; }
.std-field > input, .std-field > select, .std-field > textarea{ width:100%; box-sizing:border-box; }
.field-required-star{ color:var(--c-orange-moyen-2); }
.user-switches{ display:flex; flex-wrap:wrap; gap:18px 28px; margin-bottom:12px; }
.user-switch{ display:inline-flex; align-items:center; gap:10px; cursor:pointer; font-size:var(--fs-sm); }
.user-ros{ display:grid; grid-template-columns:repeat(4, minmax(0, 1fr)); gap:10px; }
.user-ro{ display:flex; flex-direction:column; gap:3px; font-size:var(--fs-xs); }
.user-history-link{ float:right; font-size:var(--fs-xs); }
.user-consent-table{ width:100%; border-collapse:collapse; font-size:var(--fs-xs); table-layout:fixed; }
.user-consent-table th{ text-align:left; font-weight:500; opacity:.75; padding:4px 6px 6px 0; }
.user-consent-table td{ padding:5px 6px 5px 0; vertical-align:top; }
.user-history{ font-size:var(--fs-xs); margin-top:8px; }
.user-history ul{ margin:0; padding-left:16px; }
.user-consent-actions{ display:flex; gap:10px; }
.consent-pill.is-none{ color:color-mix(in srgb, var(--bone) 60%, transparent); }
@media (max-width: 1200px){ .std-split{ grid-template-columns:minmax(0, 1fr); } .user-grid{ grid-template-columns:repeat(2, minmax(0, 1fr)); } }

/* Petit logo du coin (13/10, Y) : soleil, horizon, « zemb.com » dessous
   (zemb-logo-petit.svg, Jost 300). Place réservée : rien ne bouge. */
.crumb-logo{ display:block; height:var(--logo-small-h, 44px); aspect-ratio:470 / 240; }
.crumb-logo svg{ display:block; width:100%; height:100%; }
.crumb-logo .zl-accent{ fill:var(--c-orange-moyen-2); }
.crumbs .crumb-logo{ color:var(--dusk); }

/* Système › Identité (13/10, W.4). */
.id-cards{ display:grid; grid-template-columns:repeat(auto-fill, minmax(280px, 1fr)); gap:16px; }
.id-previews{ display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-bottom:12px; }
.id-prev{ display:flex; align-items:center; justify-content:center; height:110px; border-radius:8px; padding:10px; }
.id-prev.is-light{ background:var(--bone); }
.id-prev.is-dark{ background:var(--night); }
.id-prev img{ max-width:100%; max-height:100%; }
.id-links{ font-size:var(--fs-xs); }
/* Place réservée des ✓ ✕ de la fiche Utilisateurs (rien ne bouge). */
.user-confirm{ display:inline-flex; justify-content:flex-end; min-width:calc(var(--confirm-btn-size, 40px) * 2 + 6px); }

/* ---- N0 épuré (13/10, Y2, Y5, Y6, W5 ; maquette n0_epure validée) ----
   Bandes latérales comme N1 / N2 : fond de page --canvas-bg, scène centrale
   à la largeur du site (--text-max). Globe en horizon au bas de la scène,
   rien ne le cache ; textes clairs la nuit, foncés le jour. */
body.n0-page.n0-epure{ background:var(--canvas-bg); --n0-ink:var(--bone); --n0-ink-soft:color-mix(in srgb, var(--bone) 72%, transparent); }
body.n0-page.n0-epure.ss-day{ --n0-ink:var(--night); --n0-ink-soft:color-mix(in srgb, var(--night) 70%, transparent); }
.n0-epure .n0-scene{ position:fixed; top:0; bottom:0; left:50%; transform:translateX(-50%); width:min(100%, var(--text-max)); z-index:0; overflow:hidden; }
.n0-epure .n0-sky{ position:absolute; inset:0; background:radial-gradient(ellipse at 50% 110%, color-mix(in srgb, var(--c-bleu-moyen) 22%, var(--black-pure)), var(--black-pure) 70%); }
.n0-epure .n0-sky::after{ content:''; position:absolute; inset:0; background:url('/assets/globe/night-sky.png') center / cover; opacity:.55; }
/* Ciel de jour « Lever de soleil » (13/10, AR, variante B2 de
   maquettes/ciel-de-jour-n0.html, choix de Thierry) : le soleil se lève
   derrière la Terre (lueur centrée sur le sommet du globe, --n0-globe-top
   posé par le globe), dégradé ivoire → abricot vers l'horizon. */
:root{ --sky-dawn-1:#f5e9d6; --sky-dawn-2:#f2cf9a; --sky-dawn-3:#eca35e; --sky-dawn-4:#e4884a; --sky-sun:#ffe2a0; --sky-halo:#fff6dc; }
.n0-epure.ss-day .n0-sky{ background:
  radial-gradient(circle max(75vw, 75vh) at 50% var(--n0-globe-top, 70%), color-mix(in srgb, var(--sky-sun) 95%, transparent) 0%, color-mix(in srgb, var(--sky-sun) 45%, transparent) 25%, transparent 100%),
  linear-gradient(to bottom, var(--sky-dawn-1) 0%, var(--sky-dawn-2) 45%, var(--sky-dawn-3) 80%, var(--sky-dawn-4) 100%); }
.n0-epure.ss-day .n0-sky::after{ display:none; }
.n0-epure .n0-globe{ position:absolute; inset:0; cursor:grab; }
.n0-epure .n0-globe:active{ cursor:grabbing; }
/* Contenu par-dessus le globe : seuls les éléments utiles captent la souris,
   le reste de la zone libre fait tourner le globe. */
.n0-epure .n0-wrap{ position:relative; z-index:2; pointer-events:none; width:min(1100px, 100%, var(--text-max)); max-width:none; box-sizing:border-box; margin:0 auto; padding:64px 24px 40px; color:var(--n0-ink); }
.n0-epure .n0-wrap > *{ pointer-events:auto; }
.n0-epure .n0-wrap > .n0-ss:empty{ pointer-events:none; }
.n0-epure .n0-topbar{ background:transparent; border-bottom:none; width:min(100%, var(--text-max)); max-width:none; z-index:5; color:var(--n0-ink); }
.n0-epure #n0Stats, .n0-epure .n0-ai-badge, .n0-epure .n0-links, .n0-epure #n0SearchResults, .n0-epure .n0-footer{ display:none !important; }
.n0-epure .n0-logo{ display:flex; justify-content:center; color:var(--n0-ink); margin-bottom:6px; }
.n0-epure .n0-logo svg{ height:clamp(80px, 11vh, 124px); width:auto; }
.n0-epure .n0-title{ color:var(--n0-ink); text-shadow:none; }
/* « Où partons-nous ? » plus grand (13/10, Z.1) : taille fluide, toujours
   sur une ligne, jamais plus large que la colonne de la page (8,3 em ≈ la
   largeur du texte en Raleway italique). */
.n0-epure .n0-title{ font-size:var(--fs-h1-n0); white-space:nowrap; margin-left:-20px; margin-right:-20px; }
.n0-epure .n0-search-wrap{ max-width:760px; margin-left:auto; margin-right:auto; }
.n0-epure .n0-statline{ margin:12px 0 0; text-align:center; font-size:var(--fs-sm); color:var(--n0-ink-soft); }
.n0-epure .n0-all-link{ background:none; border:none; padding:0; cursor:pointer; font:inherit; color:var(--c-orange-moyen-8); text-decoration:underline; }
.n0-epure.ss-day .n0-all-link{ color:color-mix(in srgb, var(--c-orange-moyen-2) 55%, var(--night)); } /* AR : lisible sur le ciel « Lever de soleil » (contraste ≥ 4,5) */
/* Sous le champ, au clic seulement : récents et idées. */
.n0-epure .n0-suggest{ max-width:760px; margin:-6px auto 0; padding:14px 18px 16px; box-sizing:border-box; border-radius:0 0 16px 16px; background:color-mix(in srgb, var(--night) 94%, transparent); border:1px solid color-mix(in srgb, var(--bone) 18%, transparent); border-top:0; color:var(--bone); text-align:left; position:relative; z-index:3; }
.n0-epure .n0-suggest[hidden]{ display:none; }
.n0-epure .n0-suggest #n0Recent{ margin:0 0 10px; }
.n0-epure .n0-ideas{ display:flex; flex-wrap:wrap; gap:8px; margin-top:6px; }
.n0-epure .n0-idea{ padding:4px 12px; border-radius:14px; cursor:pointer; font:var(--fs-sm) var(--text-font-family); color:var(--bone); background:transparent; border:1px solid color-mix(in srgb, var(--bone) 28%, transparent); }
.n0-epure .n0-idea:hover{ border-color:var(--c-orange-moyen-8); }
/* Bascule d'éclairage (haut à droite). */
.n0-light{ display:inline-flex; align-items:center; gap:4px; margin-left:auto; margin-right:12px; padding:3px 10px; border-radius:12px; border:1px solid color-mix(in srgb, currentColor 30%, transparent); font-size:var(--fs-xs); }
.n0-light-btn{ background:none; border:none; padding:0 2px; cursor:pointer; font:inherit; color:inherit; opacity:.65; }
.n0-light-btn.is-on{ opacity:1; color:var(--c-orange-moyen-8); }
.n0-epure .n0-topbar .lang-switcher{ margin-left:0; }
/* Résultats sur N0 même. */
.n0-epure .n0-ss{ margin-top:18px; }
.n0-epure .ss-understood{ color:var(--n0-ink-soft); }
.n0-epure .ss-tabs{ align-items:center; flex-wrap:wrap; }
.n0-epure .ss-group-pick{ display:inline-flex; align-items:center; gap:6px; margin-left:12px; font-size:var(--fs-sm); color:var(--n0-ink); }
.n0-epure .ss-group-pick[hidden]{ display:none; }
.n0-epure .ss-group-pick select{ height:32px; border-radius:999px; padding:0 10px; font:var(--fs-sm) var(--text-font-family); color:var(--bone); background:color-mix(in srgb, var(--night) 70%, transparent); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); }
.n0-epure .ss-results{ text-align:left; }
.n0-epure .ss-mosaic, .n0-epure .ss-trips{ max-height:none; overflow:visible; }
.ss-group{ padding:6px 12px 0; }
.ss-group-head{ display:flex; align-items:center; gap:10px; padding:8px 0; color:var(--bone); }
.ss-group-toggle{ background:none; border:none; color:inherit; cursor:pointer; font-size:var(--fs-md); padding:0 4px; }
.ss-group-title{ font-family:var(--title-font-family); font-size:var(--fs-2xl); }
.ss-group-sub, .ss-group-n{ font-size:var(--fs-xs); opacity:.75; }
.ss-group-link{ margin-left:auto; font-size:var(--fs-xs); color:var(--c-orange-moyen-8); }
.ss-group .ss-mosaic{ padding:0 0 10px; }
/* Peu de résultats (13/10, X) : cadre resserré et centré, cartes centrées. */
.n0-ss.is-few{ max-width:min(100%, var(--ss-few-w)); margin-left:auto; margin-right:auto; }
.n0-ss.is-few .ss-tabs{ justify-content:flex-start; }
.n0-ss.is-few .ss-trips, .n0-ss.is-few .ss-mosaic{ grid-template-columns:repeat(var(--ss-few-n), minmax(0, var(--ss-few-unit))); justify-content:center; }
/* Carte de voyage (standard unique N0 / Smart Search, U.1). */
.ss-trips{ grid-template-columns:repeat(auto-fill, minmax(230px, 1fr)); gap:14px; }
.n0-card{ display:flex; flex-direction:column; gap:4px; color:var(--bone); }
.n0-card.is-off{ opacity:.6; }
.n0-card-link{ display:flex; flex-direction:column; gap:8px; color:inherit; text-decoration:none; }
.n0-card .n0-trip-img.is-card{ width:100%; height:auto; aspect-ratio:4 / 3; border-radius:10px; overflow:hidden; font-size:var(--fs-6xl); display:flex; align-items:center; justify-content:center; background:color-mix(in srgb, var(--bone) 8%, transparent); }
.n0-card .n0-trip-img.is-card img{ width:100%; height:100%; object-fit:cover; display:block; }
.n0-card-title{ font-family:var(--title-font-family); font-size:var(--fs-2xl); line-height:1.15; }
.n0-card-sub, .n0-card-who{ font-size:var(--fs-xs); opacity:.8; }
.n0-card-off{ margin-left:6px; padding:0 6px; border-radius:6px; border:1px solid color-mix(in srgb, var(--bone) 40%, transparent); font-size:var(--fs-2xs); }
.n0-card-found{ align-self:flex-start; background:none; border:none; padding:0; cursor:pointer; font:600 var(--fs-xs) var(--text-font-family); color:var(--c-orange-moyen-8); text-decoration:underline; }
.n0-card-strip{ display:grid; grid-template-columns:repeat(4, 1fr); gap:3px; }
.n0-card-strip img{ width:100%; aspect-ratio:1; object-fit:cover; border-radius:4px; display:block; }
/* « Consultés récemment » dans le panneau sombre sous le champ. */
.n0-epure .n0-suggest .n0-section-label{ color:color-mix(in srgb, var(--bone) 70%, transparent); text-align:left; }
.n0-epure .n0-suggest .n0-recent-list{ display:flex; flex-direction:column; gap:4px; }
.n0-epure .n0-suggest .n0-recent-item{ background:transparent; border:none; box-shadow:none; padding:4px 0; color:var(--bone); min-height:0; }
.n0-epure .n0-suggest .n0-recent-item .n0-trip-img{ width:40px; height:30px; font-size:var(--fs-md); border-radius:4px; }
.n0-epure .n0-suggest .n0-recent-name{ color:var(--bone); }
.n0-epure .n0-suggest .n0-recent-year, .n0-epure .n0-suggest .n0-recent-arrow{ color:color-mix(in srgb, var(--bone) 70%, transparent); }

/* Smart Search : corrections et mode comparatif (13/10, T et AA). */
.ss-fix{ position:absolute; top:6px; left:6px; display:none; gap:4px; flex-wrap:wrap; }
.ss-tile:hover .ss-fix, .ss-fix.is-open{ display:flex; }
.ss-fix-btn{ padding:2px 8px; border-radius:10px; cursor:pointer; font:600 var(--fs-2xs) var(--text-font-family); color:var(--bone); background:color-mix(in srgb, var(--night) 82%, transparent); border:1px solid color-mix(in srgb, var(--bone) 35%, transparent); }
.ss-fix-btn:hover, .ss-fix-btn.is-on{ border-color:var(--c-orange-moyen-8); color:var(--c-orange-moyen-8); }
.ss-compare{ display:grid; grid-template-columns:1fr 1fr; gap:12px; padding:12px; }
.ss-col-head{ font-family:var(--title-font-family); font-size:var(--fs-xl); color:var(--bone); padding:4px 0 8px; }
.ss-compare .ss-mosaic{ padding:0; grid-template-columns:repeat(auto-fill, minmax(130px, 1fr)); }
.ss-tile.is-both{ outline:2px solid var(--c-vert-moyen-2); outline-offset:-2px; }
/* Banc d'essai Smart Search (13/10, T.5). */
.ssb-pick{ display:flex; flex-wrap:wrap; align-items:center; gap:10px; margin-bottom:12px; }
.ssb-grid{ display:grid; grid-template-columns:repeat(auto-fill, minmax(110px, 1fr)); gap:6px; max-height:60vh; overflow:auto; overscroll-behavior:none; }
.ssb-tile{ position:relative; padding:0; border-radius:6px; overflow:hidden; cursor:pointer; aspect-ratio:4 / 3; border:2px solid transparent; background:color-mix(in srgb, var(--bone) 6%, transparent); opacity:.55; }
.ssb-tile img{ width:100%; height:100%; object-fit:cover; display:block; }
.ssb-tile.is-on{ opacity:1; border-color:var(--c-vert-moyen-2); }
.ssb-tile.is-guess{ border-style:dashed; }
.ssb-mark{ position:absolute; top:4px; right:6px; font-size:var(--fs-lg); color:var(--c-vert-moyen-3); text-shadow:0 1px 2px var(--night); }

/* ---- N0 (13/10, points A à D) ---- */
/* B : chiffres « signature », police de titre, libellés en petites capitales. */
.n0-epure .n0-statline{ display:flex; flex-direction:column; align-items:center; gap:8px; margin-top:20px; }
.n0-figs{ display:flex; justify-content:center; gap:clamp(18px, 3.5vw, 44px); }
.n0-fig{ display:flex; flex-direction:column; align-items:center; gap:2px; }
.n0-fig b{ font-family:var(--title-font-family); font-weight:500; font-size:var(--fs-5xl); line-height:1; color:var(--c-orange-moyen-8); }
.n0-epure.ss-day .n0-fig b{ color:color-mix(in srgb, var(--c-orange-moyen-2) 70%, var(--night)); } /* AR : contraste ≥ 3 (grands chiffres) sur le ciel de jour */
.n0-fig small{ font-size:var(--fs-sm); letter-spacing:.22em; color:var(--n0-ink-soft); }
/* C : mode globe — le globe remonte, le haut de la page se range. */
.n0-mini-sun, .n0-globe-back{ display:none; }
.n0-epure.n0-globe-mode .n0-mini-sun{ display:block; position:fixed; top:12px; left:max(16px, calc((100vw - min(100vw, var(--text-max))) / 2 + 20px)); z-index:7; color:var(--n0-ink); }
.n0-mini-sun .crumb-logo{ --logo-small-h: 38px; } /* Y : petit logo (≈ 74 px de large) ; le champ du mode globe commence à 108 px */
.n0-epure.n0-globe-mode .n0-logo, .n0-epure.n0-globe-mode .n0-title, .n0-epure.n0-globe-mode .n0-statline, .n0-epure.n0-globe-mode #n0Ss, .n0-epure.n0-globe-mode .n0-suggest{ opacity:0; pointer-events:none; }
.n0-epure .n0-logo, .n0-epure .n0-title, .n0-epure .n0-statline, .n0-epure #n0Ss{ transition:opacity .35s ease; }
.n0-epure.n0-globe-mode .n0-search-wrap{ position:fixed; top:10px; left:max(108px, calc((100vw - min(100vw, var(--text-max))) / 2 + 108px)); width:max(200px, min(380px, 40vw, calc(min(100vw, var(--text-max)) - 560px))); z-index:7; margin:0; } /* 13/10 (AN) : s'arrête avant la bascule d'éclairage, le bouton plein écran et le compte, à droite */
.n0-epure.n0-globe-mode .n0-wrap{ pointer-events:none; }
.n0-epure.n0-globe-mode .n0-globe-back{ display:block; position:fixed; bottom:24px; left:50%; transform:translateX(-50%); z-index:7; padding:8px 16px; border-radius:999px; cursor:pointer;
  font:600 var(--fs-sm) var(--text-font-family); color:var(--bone); background:color-mix(in srgb, var(--night) 85%, transparent); border:1px solid color-mix(in srgb, var(--bone) 30%, transparent); }
/* A : sans WebGL, image fixe du globe en horizon (Nuit / Jour). */
.n0-epure .n0-globe.is-off{ background:url('/assets/globe/n0-horizon-nuit.jpg') center bottom / 100% auto no-repeat; }
.n0-epure.ss-day .n0-globe.is-off{ background-image:url('/assets/globe/n0-horizon-jour.jpg'); }
.n0-gl-note{ position:fixed; left:16px; bottom:12px; z-index:6; background:none; border:none; cursor:pointer; font:var(--fs-2xs) var(--text-font-family); color:color-mix(in srgb, var(--night) 60%, transparent); text-decoration:underline; } /* sur la bande latérale claire */
.n0-gl-help{ position:fixed; right:16px; bottom:16px; z-index:8; width:min(380px, calc(100vw - 32px)); padding:16px 18px; border-radius:12px; box-sizing:border-box;
  background:color-mix(in srgb, var(--night) 96%, transparent); color:var(--bone); border:1px solid color-mix(in srgb, var(--bone) 18%, transparent); font-size:var(--fs-xs); line-height:1.5; }
.n0-gl-title{ font-family:var(--title-font-family); font-size:var(--fs-xl); margin-bottom:6px; }
.n0-gl-intro{ margin:0 0 10px; opacity:.85; }
.n0-gl-close{ position:absolute; top:8px; right:10px; background:none; border:none; color:inherit; cursor:pointer; font-size:var(--fs-md); }
.n0-gl-tabs{ display:flex; flex-wrap:wrap; gap:4px; margin-bottom:8px; }
.n0-gl-tab{ padding:3px 10px; border-radius:10px; cursor:pointer; font:var(--fs-2xs) var(--text-font-family); color:inherit; background:transparent; border:1px solid transparent; }
.n0-gl-tab.is-on{ border-color:color-mix(in srgb, var(--bone) 30%, transparent); background:color-mix(in srgb, var(--bone) 8%, transparent); }
.n0-gl-body ol{ margin:0 0 10px; padding-left:18px; }
.n0-gl-actions{ display:flex; align-items:center; gap:12px; }
.n0-gl-btn{ padding:5px 12px; border-radius:8px; cursor:pointer; font:var(--fs-2xs) var(--text-font-family); color:inherit; background:transparent; border:1px solid color-mix(in srgb, var(--bone) 30%, transparent); }
.n0-gl-link{ background:none; border:none; padding:0; cursor:pointer; font:var(--fs-2xs) var(--text-font-family); color:var(--c-orange-moyen-8); text-decoration:underline; }
/* Étiquette du point survolé sur le globe de N0 (13/10, F.2). */
.ss-globe-tip{ position:absolute; z-index:6; transform:translate(16px, -50%); padding:6px 12px; border-radius:8px; pointer-events:none; white-space:nowrap;
  font:600 var(--fs-xl) var(--text-font-family); color:var(--bone); background:var(--night); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); box-shadow:0 6px 18px color-mix(in srgb, var(--black-pure) 40%, transparent); }
.ss-globe-tip[hidden]{ display:none; }
/* Tri des groupes (13/10, V2) et « +N » d'une série (V4). */
.ss-actions{ gap:14px; flex-wrap:wrap; align-items:center; }
.ss-sort{ display:inline-flex; align-items:center; gap:4px; padding:3px; border-radius:999px; background:color-mix(in srgb, var(--night) 55%, transparent); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); }
.ss-sort-label{ padding:0 8px 0 10px; font-size:var(--fs-xs); color:color-mix(in srgb, var(--bone) 70%, transparent); text-transform:uppercase; letter-spacing:.08em; }
.ss-sort-btn{ height:26px; padding:0 12px; border:0; border-radius:999px; cursor:pointer; background:transparent; font:inherit; font-size:var(--fs-sm); color:var(--bone); }
.ss-sort-btn:hover{ background:color-mix(in srgb, var(--bone) 12%, transparent); }
.ss-sort-btn.is-on{ background:var(--c-orange-moyen-8); color:var(--night); font-weight:600; }
.ss-series{ position:absolute; left:6px; top:6px; min-width:34px; height:24px; padding:0 8px; border-radius:999px; border:0; cursor:pointer; font-size:var(--fs-xs); font-weight:600; color:var(--bone); background:color-mix(in srgb, var(--night) 75%, transparent); }
.ss-series:hover{ background:var(--c-orange-moyen-8); color:var(--night); }
/* Barre du haut de N0 voilée dès qu'on défile : les résultats ne passent
   plus « à travers » (13/10). Même hauteur : rien ne bouge. */
.n0-epure .n0-topbar{ transition:background-color .2s ease; }
body.n0-epure.n0-scrolled .n0-topbar{ background:color-mix(in srgb, var(--night) 82%, transparent); backdrop-filter:blur(8px); }
body.n0-epure.ss-day.n0-scrolled .n0-topbar{ background:color-mix(in srgb, var(--bone) 88%, transparent); }
/* ✕ du champ de recherche de N0 (13/10, J.2) : visible dès qu'il y a du
   texte, place réservée (rien ne bouge). */
.n0-search-clear{ flex:0 0 auto; width:28px; height:28px; padding:0; border:0; border-radius:50%; cursor:pointer; font-size:var(--fs-sm); line-height:1;
  color:var(--dusk); background:color-mix(in srgb, var(--dusk) 10%, transparent); }
.n0-search-clear:hover{ background:color-mix(in srgb, var(--dusk) 20%, transparent); }
body:not(.ss-has-results) .n0-search-input:placeholder-shown + .n0-search-clear{ visibility:hidden; } /* AU : visible tant que des résultats sont ouverts */
/* Filtre Cultes / Essentielles / Tout voir (13/10, I). */
.ss-tier-n{ opacity:.65; font-size:var(--fs-xs); margin-left:2px; }
.ss-sort-btn.is-on .ss-tier-n{ opacity:.8; }
/* Lieux du globe (13/10, J.3) : losange = pays (rond = ville, en WebGL). */
.ss-globe-country{ width:0; height:0; pointer-events:none; }
.ss-globe-country > i{ position:absolute; left:-6px; top:-6px; width:12px; height:12px; transform:rotate(45deg); border-radius:2px;
  background:var(--c-orange-moyen-8); border:1.5px solid var(--bone); box-sizing:border-box; transition:transform .12s ease; }
.ss-globe-country.is-off > i{ background:var(--bone); border-color:color-mix(in srgb, var(--night) 40%, transparent); }
.ss-globe-country.is-hover > i{ transform:rotate(45deg) scale(1.7); }
.ss-globe-country.is-back{ display:none; }
.ss-globe-card{ position:absolute; z-index:4; width:340px; display:flex; flex-direction:column; gap:10px; padding:14px 16px; border-radius:12px;
  background:color-mix(in srgb, var(--night) 96%, transparent); border:1px solid color-mix(in srgb, var(--c-orange-moyen-8) 40%, transparent);
  color:var(--bone); font-family:var(--text-font-family); font-size:var(--fs-sm); box-shadow:0 12px 32px color-mix(in srgb, var(--black-pure) 45%, transparent); }
.ss-globe-card[hidden]{ display:none; }
.ss-card-head{ display:flex; align-items:baseline; gap:8px; }
.ss-card-name{ font-family:var(--title-font-family); font-size:var(--fs-2xl); }
.ss-card-sub{ color:color-mix(in srgb, var(--bone) 65%, transparent); font-size:var(--fs-xs); }
.ss-card-close{ margin-left:auto; border:0; background:transparent; color:color-mix(in srgb, var(--bone) 65%, transparent); cursor:pointer; font-size:var(--fs-sm); }
.ss-card-close:hover{ color:var(--bone); }
.ss-card-one{ display:flex; gap:12px; align-items:center; }
.ss-card-txt{ display:flex; flex-direction:column; gap:2px; min-width:0; flex:1 1 auto; }
.ss-card-title{ color:var(--bone); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ss-card-meta{ color:color-mix(in srgb, var(--bone) 65%, transparent); font-size:var(--fs-xs); }
.ss-globe-card .n0-trip-img.is-mini{ width:84px; height:60px; flex:0 0 auto; border-radius:6px; overflow:hidden; display:flex; align-items:center; justify-content:center; background:color-mix(in srgb, var(--bone) 12%, transparent); font-size:var(--fs-2xl); }
.ss-globe-card .n0-trip-img.is-mini img{ width:100%; height:100%; object-fit:cover; display:block; }
.ss-card-list .n0-trip-img.is-mini{ width:64px; height:46px; }
.ss-card-actions{ display:flex; gap:8px; }
.ss-card-btn{ flex:1; text-align:center; padding:8px 0; border-radius:6px; cursor:pointer; text-decoration:none; font:inherit; color:var(--bone);
  background:transparent; border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); }
.ss-card-btn:hover{ border-color:var(--bone); }
.ss-card-btn.is-primary{ background:var(--c-orange-moyen-8); border-color:var(--c-orange-moyen-8); color:var(--night); font-weight:600; }
.ss-card-list{ display:flex; flex-direction:column; gap:6px; max-height:300px; overflow-y:auto; overscroll-behavior:none; }
.ss-card-row{ display:flex; gap:12px; align-items:center; padding:6px; border-radius:8px; text-decoration:none; background:color-mix(in srgb, var(--bone) 6%, transparent); }
.ss-card-row:hover{ background:color-mix(in srgb, var(--bone) 12%, transparent); }
.ss-card-row.is-off{ opacity:.6; }
.ss-card-go{ color:var(--c-orange-moyen-8); }
.ss-card-link{ align-self:flex-start; border:0; padding:0; background:transparent; cursor:pointer; font:inherit; font-size:var(--fs-xs); color:var(--c-orange-moyen-8); text-decoration:underline; }
/* Visionneuse standard ouverte depuis la recherche de N0 (13/10, point K) :
   page voyage auth/visionneuse.php dans un calque ; seule la visionneuse
   (et ses fenêtres filles) est visible, N0 reste derrière, en pause. */
html.viewer-embed body{ visibility:hidden; background:transparent; }
html.viewer-embed body > :is(.lightbox, .users-modal-backdrop, .crop-backdrop, .comment-editor-backdrop, [data-float-layer]){ visibility:visible; }
.n0-viewer-frame{ position:fixed; inset:0; width:100%; height:100%; border:0; z-index:10000; background:transparent; opacity:0; transition:opacity .15s ease; }
.n0-viewer-frame.is-ready{ opacity:1; }
html:has(body.n0-viewer-open), body.n0-viewer-open{ overflow:hidden; }
.lb-crumbs .lb-crumb-txt{ color:var(--settings-default-text-color); opacity:.85; }
/* N0, suite (13/10, N) : fiche opaque au-dessus des marqueurs (rien ne
   la traverse) ; losanges masqués pendant la montée ; légende ; pastille
   de critère ; ✕ de la sélection. */
.ss-globe-card{ z-index:20; background:var(--night); isolation:isolate; }
#ssGlobe.is-lifting .ss-globe-country{ display:none; }
.ss-globe-legend{ display:none; }
.n0-epure.n0-globe-mode .ss-globe-legend{ display:flex; position:absolute; left:20px; bottom:22px; z-index:6; gap:16px; padding:6px 12px; border-radius:8px;
  font-size:var(--fs-sm); color:var(--bone); background:color-mix(in srgb, var(--night) 70%, transparent); pointer-events:none; }
.ss-globe-legend span{ display:inline-flex; align-items:center; gap:7px; }
.ss-globe-legend .lg-country{ width:9px; height:9px; transform:rotate(45deg); border-radius:1px; background:var(--c-orange-moyen-8); border:1.5px solid var(--bone); box-sizing:border-box; }
.ss-globe-legend .lg-city{ width:8px; height:8px; border-radius:50%; background:var(--c-orange-moyen-8); }
.n0-chip{ display:inline-flex; align-items:center; gap:6px; flex:0 0 auto; max-width:45%; padding:3px 6px 3px 12px; border-radius:999px; font-size:var(--fs-md); font-weight:600;
  color:var(--night); background:var(--c-orange-moyen-8); white-space:nowrap; overflow:hidden; }
.n0-chip[hidden]{ display:none; }
.n0-chip-txt{ overflow:hidden; text-overflow:ellipsis; }
.n0-chip-x{ flex:0 0 auto; width:20px; height:20px; padding:0; border:0; border-radius:50%; cursor:pointer; font-size:var(--fs-2xs); line-height:1; color:var(--night); background:color-mix(in srgb, var(--night) 15%, transparent); }
.n0-chip-x:hover{ background:color-mix(in srgb, var(--night) 30%, transparent); }
.ss-close{ margin-left:auto; width:32px; height:32px; padding:0; border-radius:50%; cursor:pointer; font-size:var(--fs-sm); color:var(--bone);
  background:color-mix(in srgb, var(--night) 55%, transparent); border:1px solid color-mix(in srgb, var(--bone) 30%, transparent); }
.ss-close:hover{ border-color:var(--bone); }
/* Les marqueurs HTML de globe.gl ont des z-index de profondeur (jusqu'à
   100 et plus) : le globe forme sa propre couche, la fiche et l'étiquette
   passent toujours devant (N.3). */
#ssGlobe{ isolation:isolate; z-index:0; }

/* Retoucher (13/10, AE option A) : chemin de l'original, dans la visionneuse. */
.lb-retouch-pop{ position:absolute; top:64px; right:16px; z-index:30; width:min(560px, calc(100vw - 32px)); padding:14px 16px; border-radius:10px;
  background:var(--settings-bg); color:var(--bone); font:var(--fs-sm) var(--text-font-family); box-shadow:0 10px 30px color-mix(in srgb, var(--black-pure) 45%, transparent); }
.lb-retouch-pop[hidden]{ display:none; }
.lb-retouch-title{ font-size:var(--fs-md); margin-bottom:8px; }
.lb-retouch-path{ white-space:normal; overflow:visible; text-overflow:clip; word-break:break-all; height:auto; margin-bottom:8px; }
.lb-retouch-hint{ opacity:.8; margin:8px 0 0; }
.lb-retouch-close{ position:absolute; top:6px; right:8px; background:none; border:none; color:inherit; cursor:pointer; font-size:var(--fs-md); }


/* Résultats de N0 (13/10, AP) : quand des résultats sont ouverts, la page ne
   défile plus ; le haut (logo, titre, champ, chiffres, « Compris »), les
   onglets et la barre d'outils restent fixes ; SEUL le bloc des résultats
   défile, jusqu'en bas de l'écran (aussi en plein écran). En-têtes de
   groupe collés sous la barre, poussés par le suivant. */
html:has(body.n0-epure.ss-has-results:not(.n0-globe-mode)){ overflow:hidden; }
body.n0-epure.ss-has-results:not(.n0-globe-mode){ overflow:hidden; height:100vh; height:100dvh; }
.n0-epure.ss-has-results:not(.n0-globe-mode) .n0-wrap{ height:100vh; height:100dvh; display:flex; flex-direction:column; padding-bottom:16px; }
.n0-epure.ss-has-results:not(.n0-globe-mode) .n0-wrap > *{ flex:none; }
.n0-epure.ss-has-results:not(.n0-globe-mode) .n0-search-wrap{ width:100%; box-sizing:border-box; } /* marges auto d'une colonne flexible : sinon le champ se réduit à son contenu */
.n0-epure.ss-has-results:not(.n0-globe-mode) .n0-wrap > .n0-ss{ flex:1 1 auto; min-height:0; display:flex; flex-direction:column; }
.n0-epure.ss-has-results:not(.n0-globe-mode) .n0-ss > *{ flex:none; }
.n0-epure.ss-has-results:not(.n0-globe-mode) .n0-ss > .ss-results{ flex:1 1 auto; min-height:0; overflow-y:auto; overscroll-behavior:none; }
.n0-epure.ss-has-results:not(.n0-globe-mode) .ss-mosaic, .n0-epure.ss-has-results:not(.n0-globe-mode) .ss-trips{ max-height:none; overflow:visible; }
.n0-epure.ss-has-results:not(.n0-globe-mode) .ss-results > .ss-actions:first-child{ position:sticky; top:0; z-index:20; background:var(--night); border-radius:14px 14px 0 0; }
.n0-epure.ss-has-results:not(.n0-globe-mode) .ss-group-head{ position:sticky; top:var(--ss-actions-h, 0px); z-index:19; background:var(--night); }
/* Taille des vignettes (curseur, AP). */
.ss-mosaic{ grid-template-columns:repeat(auto-fill, minmax(var(--ss-tile, 170px), 1fr)); }
.ss-zoom{ margin-left:auto; padding-right:4px; }
.ss-zoom .zi, .ss-zoom .cell-reset-btn{ color:var(--bone); }
.ss-zoom input[type="range"]{ width:120px; accent-color:var(--c-orange-moyen-8); }
.ss-zoom input[type="range"]::-webkit-slider-runnable-track{ background:color-mix(in srgb, var(--bone) 35%, transparent); }
.ss-zoom input[type="range"]::-moz-range-track{ background:color-mix(in srgb, var(--bone) 35%, transparent); }
/* Haut de page avec résultats : variantes « compact » et « mini » (AP). */
.n0-epure.ss-has-results.ss-head-compact:not(.n0-globe-mode) .n0-wrap,
.n0-epure.ss-has-results.ss-head-mini:not(.n0-globe-mode) .n0-wrap{ padding-top:48px; }
.n0-epure.ss-has-results.ss-head-compact:not(.n0-globe-mode) .n0-logo svg{ height:clamp(40px, 6vh, 64px); }
.n0-epure.ss-has-results.ss-head-compact:not(.n0-globe-mode) .n0-title{ font-size:calc(var(--fs-h1-n0) * 0.5); margin-top:2px; margin-bottom:10px; }
.n0-epure.ss-has-results.ss-head-compact:not(.n0-globe-mode) .n0-statline{ flex-direction:row; justify-content:center; gap:16px; margin-top:8px; }
.n0-epure.ss-has-results.ss-head-compact:not(.n0-globe-mode) .n0-fig{ flex-direction:row; align-items:baseline; gap:6px; }
.n0-epure.ss-has-results.ss-head-compact:not(.n0-globe-mode) .n0-fig b{ font-size:var(--fs-xl); }
.n0-epure.ss-has-results.ss-head-compact:not(.n0-globe-mode) .n0-fig small{ font-size:var(--fs-2xs); letter-spacing:.12em; }
.n0-epure.ss-has-results.ss-head-mini:not(.n0-globe-mode) .n0-logo,
.n0-epure.ss-has-results.ss-head-mini:not(.n0-globe-mode) .n0-title,
.n0-epure.ss-has-results.ss-head-mini:not(.n0-globe-mode) .n0-statline{ display:none; }
.n0-tile-fields{ display:flex; flex-wrap:wrap; gap:8px 12px; align-items:center; }
.n0-tile-field{ display:inline-flex; align-items:center; gap:6px; font-size:var(--fs-sm); }
.n0-tile-field input{ width:72px; }

/* Voyages sur la grille standard (13/10, P2-4 lot 7) : barre pleine largeur
   au-dessus de la grille ET de la fiche ; la grille garde sa place à gauche. */
.voyages-below-header > .std-grid-mount{ flex:0 1 auto; min-width:0; min-height:0; display:flex; flex-direction:column; }
.voyages-below-header > .std-grid-mount > .std-scroll{ flex:1 1 auto; min-height:0; }
/* Champ de recherche de la grille standard et son « ? » : un seul bloc, au
   moins 240 px, qui prend la place restante (13/10, P2-4 lot 7 : écrasé
   par les cinq filtres de Voyages). */
.std-search-box{ display:inline-flex; align-items:center; gap:6px; flex:0 1 380px; min-width:240px; } /* BG 14/10 : en premier, largeur fixe, les filtres suivent à gauche */
.std-search-box > .srw, .std-search-box > .std-search{ flex:1 1 auto; min-width:0; }

/* N0 au repos (13/10, AQ) : suggestions par-dessus (plus de saut au clic) ;
   leur place est réservée sous la colonne centrée, le champ est donc plus
   haut ; chiffres et lien suivent, juste dessous, au-dessus du globe
   (« reste simple », précision de Thierry 18:44 : pas de calcul de
   l'horizon, ni de chiffres réduits ou masqués). */
.n0-epure:not(.ss-has-results):not(.n0-globe-mode) .n0-wrap{ padding-bottom:calc(40px + var(--n0-suggest-h, 160px)); }
.n0-epure .n0-suggest{ position:absolute; left:0; right:0; margin-left:auto; margin-right:auto; width:min(760px, calc(100% - 48px)); z-index:5; pointer-events:auto; background:var(--night); }
/* Plusieurs voyages (13/10, AT.5) : grille standard, hauteur bornée. */
.ig-batch-grid .std-scroll{ max-height:min(52vh, 560px); overflow:auto; overscroll-behavior:none; }
.ig-batch-count{ margin-left:auto; font-size:var(--fs-sm); font-variant-numeric:tabular-nums; opacity:.85; }
.ig-batch-grid td{ white-space:nowrap; }
.trip-summary-combo{ min-width:320px; }
.std-toolbar > .std-lead-bar{ margin:0; padding:0; border:0; background:none; }
/* Système › Base de données (13/10, BC) : la liste défile SEULE (barre et
   en-tête fixes) ; le pavé de l'opération est figé en bas, hauteur bornée,
   son propre défilement, repliable. Journal : même règle. Sauvegardes
   (plusieurs grilles) : le volet défile en entier. */
.settings-tabpanel.active[data-tabpanel="sqlops"]{ overflow:hidden; gap:10px; }
[data-tabpanel="sqlops"] .sqlops-pane:not([hidden]){ flex:1 1 auto; min-height:0; display:flex; flex-direction:column; gap:10px; }
[data-tabpanel="sqlops"] .sqlops-pane[data-sqlops-pane="backups"]:not([hidden]){ display:block; overflow-y:auto; overscroll-behavior:none; }
#sqlopsGrid, #sqlopsJournalGrid{ flex:1 1 auto; min-height:0; display:flex; flex-direction:column; }
#sqlopsGrid > .std-scroll, #sqlopsJournalGrid > .std-scroll{ flex:1 1 auto; min-height:0; overflow:auto; overscroll-behavior:none; }
#sqlopsGrid thead th, #sqlopsJournalGrid thead th{ position:sticky; top:0; z-index:1; background:var(--settings-bg); }
[data-tabpanel="sqlops"] .sqlops-detail:not([hidden]){ flex:0 0 auto; margin-top:0; max-height:min(45%, 420px); overflow-y:auto; overscroll-behavior:none; background:var(--settings-bg); }
.sqlops-detail.is-folded > :not(.sqlops-detail-head){ display:none; }
.sqlops-detail.is-folded .sqlops-detail-head h4{ margin-bottom:0; }
.sqlops-detail-btns{ display:flex; gap:6px; align-items:center; flex:none; }
.sqlops-fold{ border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); background:transparent; color:inherit; border-radius:12px; padding:2px 10px; font-size:var(--fs-xs); cursor:pointer; }
.personne-list{ list-style:none; margin:0; padding:0; max-height:14em; overflow-y:auto; overscroll-behavior:none; } .personne-list li{ padding:3px 0; display:flex; gap:10px; align-items:baseline; flex-wrap:wrap; }
/* Accès discret à l'extranet (13/10, BB) : icône à côté du compte, sur N0. */
#n0AccountMount{ display:flex; align-items:center; gap:8px; }
.n0-extranet-link{ display:inline-flex; align-items:center; justify-content:center; width:28px; height:28px; border-radius:50%; color:color-mix(in srgb, var(--bone) 70%, transparent); border:1px solid color-mix(in srgb, var(--bone) 22%, transparent); }
.n0-extranet-link svg{ fill:none; stroke:currentColor; stroke-width:1.3; }
.n0-extranet-link:hover{ color:var(--bone); border-color:var(--ember); }
/* Palette de commandes et fiche des raccourcis (13/10, AW). */
.cmd-overlay{ position:fixed; inset:0; z-index:100000; display:flex; align-items:flex-start; justify-content:center; padding-top:14vh; background:color-mix(in srgb, var(--night) 55%, transparent); }
.cmd-overlay[hidden]{ display:none; }
.cmd-box{ width:min(560px, calc(100vw - 32px)); max-height:70vh; display:flex; flex-direction:column; background:var(--settings-bg); color:var(--settings-default-text-color); border:1px solid color-mix(in srgb, var(--bone) 18%, transparent); border-radius:12px; box-shadow:0 24px 60px color-mix(in srgb, var(--black-pure) 45%, transparent); font-family:var(--text-font-family); overflow:hidden; }
.cmd-input{ border:0; border-bottom:1px solid color-mix(in srgb, var(--bone) 14%, transparent); background:transparent; color:inherit; font:inherit; font-size:var(--fs-lg); padding:16px 18px; outline:none; }
.cmd-list{ list-style:none; margin:0; padding:6px; overflow-y:auto; overscroll-behavior:none; }
.cmd-item{ display:flex; align-items:center; justify-content:space-between; gap:12px; padding:9px 12px; border-radius:8px; font-size:var(--fs-md); cursor:pointer; }
.cmd-item.is-sel, .cmd-list .cmd-item:hover{ background:color-mix(in srgb, var(--ember) 18%, transparent); }
.cmd-help .cmd-item{ cursor:default; }
.cmd-help .cmd-item:hover{ background:transparent; }
.cmd-item.is-fixed{ opacity:.7; }
.cmd-empty{ padding:12px; opacity:.7; font-size:var(--fs-sm); }
.cmd-kbd{ flex:none; font-family:var(--text-font-family); font-size:var(--fs-sm); padding:2px 8px; border-radius:6px; border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); background:color-mix(in srgb, var(--bone) 6%, transparent); white-space:nowrap; }
.cmd-foot{ padding:8px 16px; font-size:var(--fs-xs); opacity:.65; border-top:1px solid color-mix(in srgb, var(--bone) 10%, transparent); }
.cmd-help-head{ display:flex; align-items:center; justify-content:space-between; padding:14px 16px 8px; font-size:var(--fs-lg); }
.cmd-help-close{ border:0; background:transparent; color:inherit; font-size:var(--fs-md); cursor:pointer; }
.sc-key-cell{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.sc-key-cell .settings-save-btn{ padding:2px 10px; font-size:var(--fs-xs); }
.sc-mod{ font-size:var(--fs-2xs); opacity:.7; }
.cmd-box:focus{ outline:none; }
/* Charte des grilles — DÉFILEMENT (BG, 14/10, règle de Thierry) : dans un
   écran d'administration, SEULE la liste des lignes défile ; titre, barre
   de la grille, en-têtes de colonnes et pavé de détail restent fixes.
   Fenêtre basse = la liste rétrécit (140 px au moins ; en dessous, le volet
   défile en secours). Classes posées par stdFillChain() (app.js), pour
   toute grille de createStdGrid() sauf fill:false. */
.settings-tabpanel.active.std-fill-pane{ overflow-y:auto; overscroll-behavior:none; }
.std-fill-pane > *, .std-fill-chain > *{ flex-shrink:0; }
.std-fill-chain{ flex:1 1 auto; min-height:0; }
.std-fill-chain:not(.std-fill-row){ display:flex; flex-direction:column; }
.std-fill-chain > .users-table-scroll{ flex:1 1 auto; min-height:140px; overflow:auto; overscroll-behavior:none; }
.std-fill-chain ~ :not(.std-fill-chain){ flex:0 1 auto; min-height:0; max-height:50%; overflow-y:auto; overscroll-behavior:none; }
.std-fill-row{ grid-template-rows:minmax(0, 1fr); align-items:stretch; }
.std-fill-row > *{ min-height:0; }
.std-fill-row > :not(.std-fill-chain){ max-height:none; overflow-y:auto; overscroll-behavior:none; }
@media (max-width: 1200px){ .std-fill-row{ grid-template-rows:minmax(140px, 1fr) minmax(0, 45%); } }
/* Pavé SOUS la liste qui défile seul (BG) : son en-tête (titre, ✓ ✕) reste
   visible en haut du pavé. */
.std-fill-chain ~ .em-editor .em-ed-head{ position:sticky; top:-18px; z-index:3; margin:-18px -22px 14px; padding:18px 22px 8px; background:var(--settings-bg); }
.sqlops-copy{ margin-left:10px; border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); background:transparent; color:var(--ember); border-radius:12px; padding:2px 10px; font:inherit; font-size:var(--fs-xs); cursor:pointer; }
.sqlops-copy:hover{ border-color:var(--ember); }
/* Aide › Chartes (14/10, BI) : sommaire, sections ciblées par les « ? ». */
.charter-toc{ display:flex; flex-wrap:wrap; gap:8px; margin:6px 0 18px; }
.charter-toc .charter-toc-link{ border:1px solid color-mix(in srgb, var(--bone) 22%, transparent); border-radius:14px; padding:3px 12px; color:var(--settings-default-text-color); text-decoration:none; font-size:var(--fs-sm); }
.charter-toc .charter-toc-link:hover{ border-color:var(--ember); color:var(--ember); }
.charter-section{ scroll-margin-top:8px; border-radius:8px; transition:background-color .6s ease; }
.charter-section.is-flash, .charter.is-flash{ background:color-mix(in srgb, var(--ember) 14%, transparent); }
.grid-search-help-charter{ margin-top:8px; padding-top:8px; border-top:1px solid color-mix(in srgb, var(--bone) 12%, transparent); line-height:1.7; }
.grid-search-help-charter .charter-help-link{ color:var(--dust-gold); text-decoration:none; }
.grid-search-help-charter .charter-help-link:hover{ text-decoration:underline; }
.charter-img{ display:block; max-width:min(100%, 820px); height:auto; margin:8px 0 4px; border-radius:8px; border:1px solid color-mix(in srgb, var(--bone) 14%, transparent); }
.charter-link{ color:var(--ember); }
/* Atelier « Couleurs » (14/10, AV lot 1 ; maquette maquettes/panneau-couleurs.html) :
   photo en grand et planche à gauche, palette à droite (blocs pliables comme
   Camera Raw). Fond neutre et chaud (on juge les couleurs). */
body.cl-page{ margin:0; height:100vh; overflow:hidden; background:color-mix(in srgb, var(--night) 88%, var(--bone)); color:var(--bone); font-family:var(--text-font-family); font-size:var(--fs-md); }
.cl-empty{ padding:40px; }
.cl-app{ height:100%; display:grid; grid-template-rows:auto minmax(0, 1fr); }
.cl-top{ display:flex; align-items:center; gap:12px; padding:10px 18px; border-bottom:1px solid color-mix(in srgb, var(--bone) 14%, transparent); flex-wrap:wrap; }
.cl-crumb{ font-size:var(--fs-sm); opacity:.8; } .cl-crumb b{ opacity:1; font-weight:600; }
.cl-sp{ flex:1; }
.cl-btn{ border:1px solid color-mix(in srgb, var(--bone) 22%, transparent); background:color-mix(in srgb, var(--bone) 6%, transparent); color:var(--bone); border-radius:14px; padding:5px 12px; font:inherit; font-size:var(--fs-sm); cursor:pointer; white-space:nowrap; text-decoration:none; }
.cl-btn:hover{ border-color:var(--ember); }
.cl-btn[aria-pressed="true"]{ background:var(--bone); color:var(--night); }
.cl-btn.is-primary{ background:var(--ember); border-color:var(--ember); color:var(--night); font-weight:600; }
.cl-btn:disabled{ opacity:.45; cursor:default; }
.cl-main{ display:grid; grid-template-columns:minmax(0, 1fr) 360px; min-height:0; }
.cl-left{ display:grid; grid-template-rows:minmax(0, 1fr) auto; min-height:0; min-width:0; }
.cl-stage{ position:relative; display:grid; place-items:center; padding:18px; min-height:0; overflow:hidden; }
.cl-view{ display:block; border-radius:2px; box-shadow:0 10px 40px color-mix(in srgb, var(--black-pure) 45%, transparent); }
.cl-view.is-pipette{ cursor:crosshair; }
.cl-badge{ position:absolute; top:12px; left:12px; font-size:var(--fs-xs); background:color-mix(in srgb, var(--night) 75%, transparent); padding:3px 8px; border-radius:4px; }
.cl-cap{ position:absolute; bottom:4px; left:0; right:0; text-align:center; font-size:var(--fs-xs); opacity:.7; }
.cl-msg{ position:absolute; inset:auto 20px 50% 20px; text-align:center; font-size:var(--fs-md); opacity:.85; }
.cl-strip{ border-top:1px solid color-mix(in srgb, var(--bone) 14%, transparent); background:color-mix(in srgb, var(--night) 70%, var(--bone) 4%); padding:10px 14px 12px; display:flex; flex-direction:column; gap:8px; min-width:0; }
.cl-selbar{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; font-size:var(--fs-sm); }
.cl-count{ color:var(--ember); margin-right:6px; }
.cl-hint{ opacity:.55; font-size:var(--fs-xs); margin-left:auto; }
.cl-film{ display:flex; gap:18px; overflow-x:auto; padding-bottom:6px; overscroll-behavior:none; }
.cl-grp{ display:flex; flex-direction:column; gap:6px; flex:0 0 auto; }
.cl-gh{ display:flex; align-items:center; gap:8px; font-size:var(--fs-xs); opacity:.85; white-space:nowrap; }
.cl-link{ background:none; border:0; color:var(--ember); cursor:pointer; padding:0; font:inherit; font-size:var(--fs-xs); text-decoration:underline; }
.cl-row{ display:flex; gap:6px; }
.cl-thumb{ position:relative; width:84px; height:60px; border-radius:3px; overflow:hidden; cursor:pointer; border:2px solid transparent; padding:0; background:var(--black-pure); flex:0 0 auto; }
.cl-thumb img, .cl-thumb canvas{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block; }
.cl-thumb canvas[hidden]{ display:none; }
.cl-thumb.is-sel{ border-color:var(--ember); }
.cl-thumb.is-cur{ box-shadow:0 0 0 2px var(--bone); }
.cl-dot{ position:absolute; top:4px; right:4px; width:8px; height:8px; border-radius:50%; background:var(--bone); box-shadow:0 0 0 1.5px var(--black-pure); display:none; } /* crème = retouchée, braise = modifiée non enregistrée */
.cl-thumb.is-edited .cl-dot{ display:block; }
.cl-thumb.is-dirty .cl-dot{ display:block; background:var(--ember); }
.cl-palette{ border-left:1px solid color-mix(in srgb, var(--bone) 14%, transparent); background:color-mix(in srgb, var(--night) 70%, var(--bone) 4%); display:flex; flex-direction:column; min-height:0; }
.cl-phead{ padding:10px 18px 8px; border-bottom:1px solid color-mix(in srgb, var(--bone) 14%, transparent); display:flex; flex-direction:column; gap:10px; }
.cl-target{ font-size:var(--fs-sm); opacity:.85; } .cl-target b{ font-weight:600; }
.cl-histo{ height:64px; width:100%; display:block; background:color-mix(in srgb, var(--black-pure) 35%, transparent); border-radius:4px; }
.cl-pbody{ overflow-y:auto; flex:1; overscroll-behavior:none; }
.cl-blk{ border-bottom:1px solid color-mix(in srgb, var(--bone) 14%, transparent); }
.cl-bh{ display:flex; align-items:center; gap:10px; padding:9px 18px; cursor:pointer; user-select:none; }
.cl-chev{ width:12px; opacity:.6; transition:transform .15s; }
.cl-blk.is-closed .cl-chev{ transform:rotate(-90deg); }
.cl-bh h3{ margin:0; font-size:var(--fs-md); font-weight:500; flex:1; }
.cl-ic{ background:none; border:0; color:inherit; opacity:.6; cursor:pointer; font-size:var(--fs-md); padding:2px 4px; }
.cl-ic:hover{ opacity:1; }
.cl-blk.is-off .cl-bh h3{ opacity:.55; text-decoration:line-through; }
.cl-blk.is-soon .cl-bh h3{ opacity:.6; }
.cl-soon{ font-size:var(--fs-3xs); text-transform:uppercase; letter-spacing:.06em; padding:1px 7px; border-radius:9px; border:1px solid color-mix(in srgb, var(--bone) 30%, transparent); opacity:.75; }
.cl-bb{ padding:0 18px 12px; display:flex; flex-direction:column; gap:7px; }
.cl-blk.is-closed .cl-bb{ display:none; }
.cl-sl{ display:grid; grid-template-columns:1fr 62px; align-items:center; column-gap:12px; }
.cl-sl label{ font-size:var(--fs-sm); }
.cl-sl label.is-chg::after{ content:" •"; color:var(--ember); }
.cl-val{ width:62px; text-align:right; font:inherit; font-size:var(--fs-xs); background:color-mix(in srgb, var(--black-pure) 30%, transparent); border:1px solid color-mix(in srgb, var(--bone) 18%, transparent); color:var(--bone); border-radius:4px; padding:1px 6px; }
.cl-sl input[type=range]{ grid-column:1 / span 2; -webkit-appearance:none; appearance:none; width:100%; height:14px; background:transparent; margin:0; cursor:pointer; }
.cl-sl input[type=range]::-webkit-slider-runnable-track{ height:4px; border-radius:2px; background:var(--track); }
.cl-sl input[type=range]::-moz-range-track{ height:4px; border-radius:2px; background:var(--track); }
.cl-sl input[type=range]::-webkit-slider-thumb{ -webkit-appearance:none; width:13px; height:13px; margin-top:-4.5px; border-radius:50%; background:var(--bone); border:1px solid var(--night); }
.cl-sl input[type=range]::-moz-range-thumb{ width:13px; height:13px; border-radius:50%; background:var(--bone); border:1px solid var(--night); }
.cl-wb{ display:flex; gap:8px; align-items:center; font-size:var(--fs-sm); }
.cl-wb select{ flex:1; background:color-mix(in srgb, var(--black-pure) 30%, transparent); color:var(--bone); border:1px solid color-mix(in srgb, var(--bone) 18%, transparent); border-radius:4px; padding:4px 6px; font:inherit; font-size:var(--fs-sm); }
.cl-note{ font-size:var(--fs-xs); opacity:.65; line-height:1.5; margin:0; }
.cl-presets{ display:flex; flex-direction:column; gap:6px; }
.cl-pr{ display:flex; align-items:center; gap:10px; padding:6px 10px; border:1px solid color-mix(in srgb, var(--bone) 14%, transparent); border-radius:6px; }
.cl-pr span{ flex:1; font-size:var(--fs-sm); }
.cl-newp{ display:flex; gap:6px; margin-top:4px; }
.cl-newp input{ flex:1; min-width:0; background:color-mix(in srgb, var(--black-pure) 30%, transparent); border:1px solid color-mix(in srgb, var(--bone) 18%, transparent); color:var(--bone); border-radius:4px; padding:4px 8px; font:inherit; font-size:var(--fs-sm); }
.cl-pfoot{ border-top:1px solid color-mix(in srgb, var(--bone) 14%, transparent); padding:12px 18px; display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.cl-pending{ font-size:var(--fs-xs); color:var(--ember); }
.cl-toast{ position:fixed; left:50%; bottom:24px; transform:translateX(-50%); background:var(--bone); color:var(--night); padding:8px 14px; border-radius:6px; font-size:var(--fs-sm); opacity:0; transition:opacity .2s; pointer-events:none; max-width:min(680px, 90vw); }
.cl-toast.on{ opacity:1; pointer-events:auto; }
.cl-toast-link{ color:var(--night); font-weight:600; margin-left:8px; }
@media (max-width: 900px){ body.cl-page{ overflow:auto; height:auto; } .cl-app{ height:auto; } .cl-main{ grid-template-columns:minmax(0, 1fr); } .cl-stage{ height:60vh; } }
.lb-retouch-workshop{ display:inline-block; margin:0 0 8px; color:var(--ember); font-size:var(--fs-sm); }
.dia-workshop-link{ margin-left:auto; margin-right:10px; font-size:var(--fs-sm); color:var(--ember); white-space:nowrap; }
.cl-tabs{ display:flex; gap:2px; background:color-mix(in srgb, var(--black-pure) 30%, transparent); border-radius:6px; padding:2px; }
.cl-tabs .cl-tab{ flex:1; border:0; background:none; padding:5px 0; border-radius:4px; cursor:pointer; font:inherit; font-size:var(--fs-xs); color:inherit; opacity:.7; }
.cl-tabs .cl-tab[aria-pressed="true"]{ background:color-mix(in srgb, var(--bone) 12%, transparent); opacity:1; }
.cl-sub{ font-size:var(--fs-3xs); letter-spacing:.12em; text-transform:uppercase; opacity:.6; margin-top:6px; }
/* Grilles en mode virtuel (charte G16, AX du 14/10) : lignes de hauteur
   fixe (--std-row-h posé par createStdGrid), lignes vides de calage. */
.std-virtual > tr[data-std-id]{ height:var(--std-row-h); }
.std-virtual > tr[data-std-id] > td{ height:var(--std-row-h); box-sizing:border-box; padding-top:4px; padding-bottom:4px; }
.std-virtual > tr.std-spacer:hover td{ background:transparent; }
/* Onglet Photos (BE-3, 14/10, variante A) : liste à gauche, fiche à droite
   (dessous sous 1 200 px, règle de .std-split) ; la fiche défile seule,
   son en-tête (nom, ‹ › ✓ ✕) reste visible. */
#pmGridWrap .std-grid .pm-thumb{ width:50px; height:50px; object-fit:cover; display:block; border-radius:3px; }
#pmGridWrap .std-grid .photo-tag-svg, #pmGridWrap .std-grid svg{ width:15px; height:15px; color:var(--ember); }
#pmGridWrap .std-grid tbody tr[data-std-id]{ cursor:pointer; }
#pmGridWrap .std-grid .pm-etape{ width:100%; }
#pmGridWrap .std-grid .pm-pos{ width:56px; }
.pm-split{ align-items:stretch; }
.pm-docked{ background:color-mix(in srgb, var(--bone) 3%, var(--settings-bg)); border:1px solid color-mix(in srgb, var(--bone) 12%, transparent); border-radius:8px; color:var(--settings-value-color);
  font-family:var(--text-font-family); font-size:var(--fs-sm); overflow-y:auto; overscroll-behavior:none; outline:none; min-height:0; }
.pm-docked .pm-docked-head{ position:sticky; top:0; z-index:3; background:color-mix(in srgb, var(--bone) 3%, var(--settings-bg)); min-height:var(--confirm-head-h); gap:8px; }
.pm-docked .pm-title{ display:flex; flex-direction:column; gap:2px; font-weight:normal; }
.pm-docked-name{ font-family:var(--title-font-family, var(--text-font-family)); font-size:var(--fs-lg); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.pm-docked-sub{ font-size:var(--fs-xs); opacity:.75; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.pm-docked .pm-icon{ width:var(--confirm-btn-size-row); height:var(--confirm-btn-size-row); border-radius:50%; }
.pm-docked .pm-sec{ display:block; padding:10px 14px; border-bottom:1px solid color-mix(in srgb, var(--bone) 10%, transparent); }
.pm-docked .pm-sec > h4, .pm-docked .pm-sec > summary{ margin:0 0 8px; font-size:var(--fs-2xs); text-transform:uppercase; letter-spacing:.06em; color:var(--ember); font-weight:600; display:flex; justify-content:space-between; gap:10px; }
.pm-docked .pm-sec > h4 span{ color:var(--settings-label-color); text-transform:none; letter-spacing:0; font-weight:normal; text-align:right; }
.pm-docked .pm-sec > summary{ cursor:pointer; margin:0; padding:0; }
.pm-docked .pm-sec[open] > summary{ margin-bottom:8px; }
.pm-docked .pm-field{ grid-template-columns:130px 1fr; }
.pm-docked .pm-big-box{ padding:10px 14px; }
.pm-docked .pm-big{ width:100%; max-height:240px; object-fit:contain; display:block; border-radius:6px; background:color-mix(in srgb, var(--black-pure) 25%, transparent); }
.pm-chips{ display:flex; flex-wrap:wrap; gap:6px; }
.pm-chip{ border:1px solid color-mix(in srgb, var(--bone) 22%, transparent); border-radius:12px; padding:1px 10px; font-size:var(--fs-xs); opacity:.7; }
.pm-chip.is-on{ border-color:var(--c-vert-moyen-2); color:var(--c-vert-moyen-3); opacity:1; }
.pm-docked-empty{ padding:24px 16px; opacity:.7; }
.pm-docked .pm-msg, .pm-docked .pm-review{ margin:8px 14px; }
/* Remplacement en masse (R66 bis) : une seule ligne tant que rien n'est
   cherché (l'attribut hidden l'emporte sur display:flex des libellés). */
.pm-replace [hidden], .pm-replace-keys:empty{ display:none; }
.pm-toolbar-host{ display:flex; flex-direction:column; gap:6px; }
.pm-toolbar-host .pm-replace{ padding:2px 0 6px; }
/* Interface › Affichage en blocs (BH-1, 14/10, maquette A) : 3 colonnes
   à 1 512 px, 2 ou 1 plus étroit ; libellé au-dessus de son contrôle,
   défaut et ↺ à côté (↺ orange = valeur modifiée). */
.disp-blocks{ columns:3 330px; column-gap:16px; }
.disp-card{ break-inside:avoid; border:1px solid color-mix(in srgb, var(--bone) 14%, transparent); border-radius:10px; padding:12px 14px 6px; margin:0 0 16px; background:color-mix(in srgb, var(--bone) 2%, transparent); }
.disp-card[hidden]{ display:none; }
.disp-card > h4{ margin:0 0 6px; font-size:var(--fs-md); font-weight:normal; display:flex; align-items:baseline; gap:8px; color:var(--settings-default-text-color); }
.disp-card > h4 small{ margin-left:auto; font-size:var(--fs-2xs); color:var(--settings-label-color); }
.disp-card .settings-row.disp-set{ display:flex; flex-direction:column; align-items:flex-start; gap:6px; padding:8px 0; margin:0; border-top:1px solid color-mix(in srgb, var(--bone) 8%, transparent); }
.disp-card .settings-row.disp-set:first-of-type{ border-top:0; }
.disp-card .settings-row.disp-set > .settings-row-text{ width:100%; }
.disp-card .settings-row.disp-set .settings-desc{ font-size:var(--fs-2xs); line-height:1.4; }
.disp-card .settings-row.disp-set > :not(.settings-row-text){ display:inline-flex; vertical-align:middle; }
.disp-card .settings-row.disp-set{ flex-flow:row wrap; align-items:center; column-gap:8px; }
.disp-card .settings-row.disp-set > .settings-row-text{ flex:1 1 100%; }
.disp-card .settings-ticker-textarea{ width:100%; box-sizing:border-box; }
.disp-card > .settings-save-btn{ margin:4px 0 8px; }
.disp-def{ font-family:var(--text-font-family); font-size:calc(var(--fs-sm) * var(--ui-scale, 1)); color:var(--settings-default-text-color); opacity:.65; white-space:nowrap; }
.disp-reset{ opacity:.55; }
.disp-reset.is-mod{ opacity:1; color:var(--ember); border-color:var(--ember); }
/* Aide › Documentation (BF lot D0, 14/10) : sommaire par famille, fiches,
   « ? » discret des écrans (18 px, par-dessus quand l'écran n'a pas de titre). */
.doc-toc{ display:flex; flex-direction:column; gap:8px; margin:6px 0 18px; }
.doc-toc-fam{ display:flex; flex-wrap:wrap; align-items:center; gap:6px 8px; }
.doc-toc-label{ font-size:var(--fs-xs); color:var(--settings-label-color); min-width:220px; }
.doc{ scroll-margin-top:8px; border-radius:8px; transition:background-color .6s ease; margin-bottom:22px; }
.doc.is-flash, .doc-section.is-flash{ background:color-mix(in srgb, var(--ember) 14%, transparent); }
.doc-meta{ font-size:var(--fs-xs); color:var(--settings-label-color); margin:2px 0 8px; }
.doc-inner-toc{ display:flex; flex-wrap:wrap; gap:4px 12px; font-size:var(--fs-xs); margin-bottom:6px; }
.doc-section-title{ margin:12px 0 4px; font-size:var(--fs-sm); text-transform:uppercase; letter-spacing:.05em; color:var(--ember); font-weight:600; }
.doc-steps{ margin:4px 0; padding-left:22px; }
.doc-steps li{ margin-bottom:10px; }
.doc-img{ display:block; max-width:min(100%, 900px); border-radius:6px; border:1px solid color-mix(in srgb, var(--bone) 14%, transparent); margin:6px 0 4px; }
.doc-links a{ color:var(--ember); }
.doc-q{ display:inline-flex; align-items:center; justify-content:center; width:18px; height:18px; border-radius:50%; border:1px solid color-mix(in srgb, var(--bone) 30%, transparent); background:transparent; color:var(--settings-label-color); font:inherit; font-size:var(--fs-2xs); line-height:1; cursor:pointer; vertical-align:middle; padding:0; flex:none; }
.doc-q:hover{ border-color:var(--ember); color:var(--ember); }
.settings-tabpanel.has-doc-q{ position:relative; }
.doc-q.is-corner{ position:absolute; top:2px; right:2px; z-index:4; }
.doc-link{ color:var(--ember); text-decoration:none; }
.doc-link:hover{ text-decoration:underline; }
.doc-inner-toc .doc-link{ color:var(--settings-label-color); }
.doc-toc .charter-toc-link{ border:1px solid color-mix(in srgb, var(--bone) 22%, transparent); border-radius:14px; padding:3px 12px; color:var(--settings-default-text-color); text-decoration:none; font-size:var(--fs-sm); }
.doc-toc .charter-toc-link:hover{ border-color:var(--ember); color:var(--ember); }
.doc-meta, .doc-inner-toc{ font-family:var(--text-font-family); }

/* Interface › Thème en blocs (15/10, BL, comme Affichage BH-1 A) : blocs en
   colonnes dans la zone qui défile ; aperçu de chaque couleur près de son
   libellé (zone pleine pour un fond, « Aa » pour un texte) ; défaut à côté. */
.theme-editor-rows-scroll.theme-blocks{ display:grid; grid-template-columns:repeat(auto-fill, minmax(min(100%, 420px), 1fr)); gap:16px; align-items:start; align-content:start; }
.theme-blocks > .theme-card{ margin:0; min-width:0; }
.theme-card .settings-row.theme-color-row{ padding:6px 0; border-top:1px solid color-mix(in srgb, var(--bone) 8%, transparent); flex-wrap:wrap; row-gap:4px; }
.theme-card > h4 .theme-card-icon{ font-size:var(--fs-lg); }
.theme-prev{ display:inline-flex; align-items:center; justify-content:center; width:22px; height:16px; margin-right:8px; vertical-align:middle; border-radius:4px; background:var(--pv); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); font-size:var(--fs-2xs); font-weight:bold; }
.theme-prev.is-text{ width:30px; background:linear-gradient(90deg, var(--bone) 50%, var(--night) 50%); color:var(--pv); letter-spacing:4px; padding-left:3px; box-sizing:border-box; }
.theme-card .theme-color-row > .settings-row-text{ flex:1 1 100%; }
.theme-def{ display:inline-flex; align-items:center; gap:4px; }
.theme-def-chip{ display:inline-block; width:10px; height:10px; border-radius:2px; background:var(--pv); border:1px solid color-mix(in srgb, var(--bone) 25%, transparent); }
/* Charte des icônes (15/10, BQ) : plus de ↺ ni de ⊘ dans le Thème. Remettre
   le défaut = le texte « défaut : … » lui-même, cliquable (souligné au
   survol), visible seulement quand la valeur diffère du défaut (place
   réservée : visibility) ; ✕ annule la saisie non enregistrée (R44). */
button.theme-def.is-link{ background:none; border:none; padding:0; cursor:pointer; font-size:calc(var(--fs-sm) * var(--ui-scale, 1)); opacity:.65; color:var(--settings-default-text-color); }
button.theme-def.is-link:hover{ text-decoration:underline; opacity:1; color:var(--settings-default-text-color); }
.theme-color-reset, .theme-font-reset{ font-size:var(--fs-md); }
