:root {
  --header-h: 76px;
  --nav-h: 76px;
  --ink: #101828;
  --ink-soft: #667085;
  --ink-faint: #98a2b3;

  --paper: #ffffff;
  --bg: #f5f8fd;

  --blue: #155eef;
  --blue-dark: #0b3fc4;
  --blue-darker: #0a2f94;
  --blue-soft: #eaf1ff;
  --blue-soft-2: #d9e8ff;
  --blue-line: #cddffd;
  /* Pas de halo de focus : uniquement la bordure bleue, propre sur tous
     les thèmes (le halo ressortait toujours comme un contour disgracieux). */
  --focus-ring: transparent;

  --line: #e5e8ef;
  --line-soft: #eef1f6;

  --amber: #d98e2b;
  --amber-dim: #fbeed9;
  --rose: #e14a63;
  --rose-dim: #fbe3e8;
  --slate: #667085;
  --slate-dim: #eceef2;

  --display: 'Sora', sans-serif;
  --body: 'Inter', sans-serif;

  --radius: 12px;
  --radius-sm: 8px;
  --radius-lg: 18px;
  --shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.06);
  --shadow-md: 0 4px 14px rgba(16, 24, 40, 0.06), 0 1px 3px rgba(16, 24, 40, 0.05);
  --shadow-lg: 0 12px 32px rgba(16, 24, 40, 0.10), 0 2px 6px rgba(16, 24, 40, 0.05);
}

/* ---------- Thème Night (mode sombre) ----------
   On flippe simplement les variables de palette : tout le site utilise
   deja ces variables (--ink, --paper, --bg, --line...), donc surcharger
   ici suffit a passer l'interface en sombre sans toucher au reste du CSS.
   Applique via <html data-theme="night"> (voir script inline dans <head>
   + applyTheme() dans main.html). */
html[data-theme="night"] {
  --ink: #e7eaf0;
  --ink-soft: #99a3b3;
  --ink-faint: #6b7484;

  --paper: #1a1d24;
  --bg: #12141a;

  --blue: #4a86ff;
  --blue-dark: #3a72e8;
  --blue-darker: #2e5fce;
  --blue-soft: #1e2b45;
  --blue-soft-2: #24365a;
  --blue-line: #2f4472;
  /* Pas de halo de focus sur les thèmes sombres : juste la bordure bleue
     (le halo translucide ressortait en gris terne autour de la bordure). */
  --focus-ring: transparent;

  --line: #2b303b;
  --line-soft: #23272f;

  --amber: #e0a54a;
  --amber-dim: #3a2f1c;
  --rose: #f0637b;
  --rose-dim: #3a222a;
  --slate: #99a3b3;
  --slate-dim: #262a33;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-md: 0 4px 14px rgba(0, 0, 0, 0.45), 0 1px 3px rgba(0, 0, 0, 0.4);
  --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.55), 0 2px 6px rgba(0, 0, 0, 0.4);
}
/* Fond global de la page en Night : le body/html doit etre sombre aussi
   (au-dela de .page-scroll), sinon les bords hors-contenu restent blancs. */
html[data-theme="night"], html[data-theme="night"] body {
  background: var(--bg);
}

/* ---------- Thème Gris (gris sombre / charcoal) ----------
   Variante sombre mais grise (charcoal), plus claire que Night (qui tire vers
   le noir). Texte clair, accent bleu clair. */
html[data-theme="gris"] {
  --ink: #e8eaee;
  --ink-soft: #a3a8b2;
  --ink-faint: #757b86;

  --paper: #3a3d43;
  --bg: #2f3237;

  --blue: #4a86ff;
  --blue-dark: #3a72e8;
  --blue-darker: #2e5fce;
  --blue-soft: #333b4c;
  --blue-soft-2: #3a4457;
  --blue-line: #45536e;
  --focus-ring: transparent;

  --line: #4a4e56;
  --line-soft: #40444b;

  --amber: #e0a54a;
  --amber-dim: #453a26;
  --rose: #f0637b;
  --rose-dim: #453035;
  --slate: #a3a8b2;
  --slate-dim: #3c4046;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
  --shadow-md: 0 4px 14px rgba(0, 0, 0, 0.38), 0 1px 3px rgba(0, 0, 0, 0.32);
  --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.45), 0 2px 6px rgba(0, 0, 0, 0.32);
}
html[data-theme="gris"], html[data-theme="gris"] body {
  background: var(--bg);
}

* { box-sizing: border-box; }

button, input, select, textarea {
  font-family: var(--body);
  font-size: 15px;
  color: inherit;
}

button { cursor: pointer; }

/* ---------- Optimisations de rendu globales ----------
   CSS containment cible : chaque zone marquee ci-dessous se comporte comme
   une "ile" pour le navigateur — un changement a l'interieur (hover, focus,
   ouverture de popup) n'oblige plus le recalcul de layout/style de tout le
   reste de la page. Sur Chrome et Firefox, ca supprime la majorite des
   micro-lags ressentis a l'ouverture d'une popup ou au survol d'un bouton.

   IMPORTANT : on utilise "contain: layout style" et JAMAIS "paint" ici.
   "paint" decoupe le rendu au bord de l'element, ce qui masquerait les
   box-shadow (ombres portees) des cartes/popups/modales qui debordent
   volontairement de leur boite. layout + style suffisent a isoler les
   recalculs sans rien rogner visuellement.
   .modal-inner est volontairement EXCLU : c'est le conteneur qui recoit
   le translateY du rebond elastique (voir initModalElasticScroll), le
   contenir pourrait perturber le calcul de scroll. */
.access-card,
.feature-card,
.telegram-popup,
.settings-modal-box,
.mobile-menu-inner {
  contain: layout style;
}

/* NB : on n'utilise PAS content-visibility:auto ici. Ce serait un gain de
   premier paint, mais il modifie la hauteur rapportee des sections hors
   ecran, ce qui perturberait le calcul de la barre de scroll personnalisee
   (hauteur/position du curseur recalculees en JS depuis scrollHeight). La
   fluidite du scroll custom prime. */

/* ----- Lancement propre -----
   Le temps que le CSS/police/état (connecté ou non) soit posé, on coupe
   toutes les transitions et on garde la page invisible : ça évite le
   flash de bordure/ombre "hover" sur les boutons et la barre de recherche
   au premier rendu. Une fois prêt, un fondu simple révèle la page. */
body.preload,
body.preload * {
  transition: none !important;
  animation: none !important;
}

/* will-change: opacity : indique explicitement au navigateur que l'opacite
   du body est susceptible de bouger (transitions swapView utilisees pour
   toute navigation SPA, y compris register -> login apres creation de
   compte). Sur mobile en particulier, le compositeur promeut le body en
   layer GPU dedie et evite un repaint plein-ecran a chaque frame du
   fondu -> transition fluide identique sur telephone et desktop. Sans ce
   hint, Chrome/Safari mobiles decidaient au coup par coup s'il valait la
   peine de promouvoir la couche, avec parfois un premier fondu saccade
   avant que le compositeur "chauffe". */
body {
  will-change: opacity;
}

body.preload {
  opacity: 0;
}
body.app-ready {
  opacity: 1;
  /* Courbe "easeOut" plus douce et duree legerement plus courte que
     l'ancienne (.32s -> .28s) : le contenu apparait sans temps mort,
     mais toujours avec un decollage progressif. */
  transition: opacity .28s cubic-bezier(.22, 1, .36, 1);
}

/* ----- Transition douce entre les pages -----
   Quand on clique sur un lien interne (Connexion, Créer un compte, retour
   à l'accueil...), on fait disparaître la page en douceur avant de
   naviguer, plutôt qu'un changement de page brutal. Utilisée par
   swapView() pour TOUTES les navigations, y compris la redirection
   register -> login après création de compte.
   Le translateY ajoute un léger glissement vertical (la page descend en
   sortant, remonte en entrant) pour donner de la spatialité au switch —
   purement composité sur GPU (will-change: opacity déjà sur body).
   Le transform est sur .page-scroll et pas sur body, pour ne pas casser
   le containing block des éléments fixed (nav, modale TOS, menu mobile). */
body.nav-leaving {
  opacity: 0;
  /* Sortie legerement plus rapide que l'entree (.18s vs .28s), courbe
     "easeIn" douce : le contenu s'estompe rapidement au debut puis
     ralentit -> impression de "clean" plus vif, moins de temps mort
     entre le clic et l'apparition de la nouvelle page. */
  transition: opacity .18s cubic-bezier(.4, 0, 1, 1);
}

/* ----- Transition douce interne (carte "Créer un compte") -----
   Même mécanique et mêmes durées/easing que la transition de page
   ci-dessus (nav-leaving -> preload -> app-ready), mais appliquée à un
   conteneur précis à l'intérieur de la carte plutôt qu'à toute la page.
   Utilisée par swapInner() (voir JS) pour le passage du formulaire
   classique au flux "Continuer avec Telegram" et entre les deux étapes de
   ce flux (identifiant -> identifiant Telegram/code).
   Le translateY ajoute un léger glissement vertical cohérent avec la
   transition de page (même direction, même feeling). */
.inner-fade-preload,
.inner-fade-preload * {
  transition: none !important;
}
.inner-fade-preload {
  opacity: 0;
}
.inner-fade-leaving {
  opacity: 0;
  transition: opacity .22s cubic-bezier(.22, .61, .36, 1);
}
.inner-fade-ready {
  opacity: 1;
  transition: opacity .32s cubic-bezier(.22, .61, .36, 1);
}

/* IMPORTANT : ne JAMAIS remettre de "transform" sur <body> (même
   translateY(0)). Un transform sur un ancetre cree un nouveau bloc
   conteneur pour tous ses descendants en position:fixed (#tosModal,
   #customScrollbar, #settingsModal, .mobile-menu...) : ils seraient
   alors ancres a <body> au lieu du vrai viewport. C'est ce qui causait
   la barre de scroll native qui reapparaissait au redimensionnement de
   la fenetre sous Firefox (la modale TOS en etant l'exemple le plus
   visible). Si un effet de glissement est souhaite au reveal, l'ajouter
   sur un wrapper interne dedie, jamais sur body/html. */

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--body);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  /* html/body ne défilent plus jamais : seul .page-scroll (sous le
     header) est scrollable, donc sa propre barre de scroll ne peut
     physiquement pas remonter plus haut que le bas du header. */
  overflow: hidden;
  scrollbar-width: none !important;
  -ms-overflow-style: none !important;
}
html::-webkit-scrollbar,
body::-webkit-scrollbar {
  display: none !important;
  width: 0 !important;
  height: 0 !important;
}

a { color: var(--blue); }
a:hover { color: var(--blue-dark); }
svg { display: block; }

/* ---------- Barre de scroll personnalisée -----------
   Fine, discrète mais visible au repos (pas seulement au survol),
   et surtout toujours la même taille — elle ne grossit jamais au
   survol, seule sa teinte change légèrement. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--ink-faint) transparent;
}

::-webkit-scrollbar,
html::-webkit-scrollbar,
body::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

::-webkit-scrollbar-track,
html::-webkit-scrollbar-track,
body::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb,
html::-webkit-scrollbar-thumb,
body::-webkit-scrollbar-thumb {
  background-color: var(--ink-faint);
  border-radius: 999px;
  border: 1px solid var(--bg);
  background-clip: padding-box;
  min-height: 40px;
}

::-webkit-scrollbar-thumb:hover,
html::-webkit-scrollbar-thumb:hover,
body::-webkit-scrollbar-thumb:hover {
  background-color: var(--ink-soft);
}

::-webkit-scrollbar-thumb:active,
html::-webkit-scrollbar-thumb:active,
body::-webkit-scrollbar-thumb:active {
  background-color: var(--blue);
}

::-webkit-scrollbar-corner { background: transparent; }

/* ---------- En-tête (bandeau promo + nav collante) -----------
   Le bandeau promo reste fixe en haut de l'écran, en dehors de
   .page-scroll : la barre de scroll de ce dernier ne peut jamais remonter
   au-dessus de lui. La nav (liens/icônes), elle, vit dans .page-scroll
   mais reste collée juste sous ce bandeau grace a .top-nav { position:
   sticky } ci-dessous : seul le contenu de page (résultats, cartes...)
   défile réellement sous elle. */
.site-header {
  position: relative;
  z-index: 20;
  background: var(--bg);
  /* contain: layout paint : le bandeau promo et sa nav collee ne peuvent
     plus provoquer de repaint hors de leurs limites. Pendant le scroll
     smooth custom, Chrome/Firefox ne recalculent plus le layout au-dessus
     de la ligne de flottaison a chaque frame. */
  contain: layout paint;
}

/* Conteneur de scroll dédié : seule cette zone défile.
   Sa hauteur est calée sur "tout l'écran moins le bandeau promo", donc
   sa barre de scroll commence pile sous le bandeau et ne peut pas
   remonter plus haut. */
.page-scroll {
  height: calc(100vh - var(--header-h));
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior-y: contain;
  /* overflow-anchor: none : desactive le "scroll anchoring" du navigateur.
     Comme la position de scroll est pilotee a la main (smooth-scroll JS +
     barre custom), on ne veut pas que Chrome/Firefox tentent en plus de
     re-ancrer la position quand un contenu au-dessus change de taille
     (bascule de vue, volet de filtres...) : ca entrait en conflit avec
     notre pilotage et pouvait provoquer un micro-saut. */
  overflow-anchor: none;
  /* La nav est maintenant en position: fixed (hors du flux) : le contenu
     scrollable commencerait sous le bandeau promo et passerait derriere
     la nav. Ce padding-top reserve exactement la hauteur de la nav pour
     que le premier element visible commence juste en-dessous d'elle. */
  padding-top: var(--nav-h);
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.page-scroll::-webkit-scrollbar { display: none; width: 0; height: 0; }

/* Wrapper du contenu scrollable de la page (accueil/login/register/
   telegram, tous dans #pageScroll) : recoit en continu le translateY du
   scroll sub-pixel + du rebond elastique (voir stepSmoothScroll dans
   main.html). */
/* Layer compositor + rendu texte figes EN PERMANENCE (sans will-change,
   qui causait le tremblement precedent au hover). C'est le passage
   automatique de Chrome/Safari entre rendu "subpixel" (aucun transform
   actif) et "grayscale" (transform actif) qui repositionnait le texte
   de fractions de pixel a la fin du rebond -> le "recaler" signale.
   En figeant les deux en permanence, aucune bascule possible, aucun
   replacement de texte. Sans will-change, aucune promotion continue
   du layer, donc aucun tremblement au repos non plus. */
.page-scroll-inner {
  transform: translateZ(0);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* ---------- Barre de scroll personnalisée (remplace celle de .page-scroll) ----------
   Le curseur a une hauteur FIXE, définie ici en dur : elle ne dépend
   jamais de la quantité de contenu ni du ratio contenu/écran. Seule sa
   position verticale bouge quand on défile — sa taille, elle, ne change
   jamais, que la page soit courte ou très longue. */
.custom-scrollbar {
  position: fixed;
  top: var(--header-h);
  right: 4px;
  width: 4px;
  height: calc(100vh - var(--header-h));
  z-index: 30;
}

.custom-scrollbar.hidden { display: none; }

.custom-scrollbar-thumb {
  position: absolute;
  left: 0;
  top: 0;
  width: 4px;
  height: 40px; /* valeur initiale ; la hauteur réelle (proportionnelle au
                  contenu, avec ce minimum) est posée en JS via style.height */
  border-radius: 999px;
  background-color: var(--ink-faint);
  opacity: .55;
  pointer-events: auto;
  cursor: pointer;
  transition: opacity .15s ease, background-color .15s ease;
  /* Le positionnement se fait via transform: translateY() (posé en JS)
     plutôt que via top : le navigateur peut animer translateY sur le
     compositor GPU sans déclencher de reflow, ce qui élimine les micro-
     saccades de scroll. will-change: transform garde le layer pré-alloué
     en permanence (pas de promotion just-in-time a chaque frame). */
  will-change: transform;
}

.custom-scrollbar-thumb:hover { opacity: .85; background-color: var(--ink-soft); }
/* Pendant le glissement : meme apparence que le survol (gris), pas de bleu.
   On garde transition:none pour que le curseur suive le geste sans latence. */
.custom-scrollbar-thumb.dragging { opacity: .85; background-color: var(--ink-soft); transition: none; }

.top-nav {
  background: transparent;
  /* Fixed plutot que sticky : sticky provoquait du sub-pixel jitter
     pendant le smooth-scroll custom (le navigateur recalcule la position
     du sticky a chaque frame avec des arrondis differents → tremblement
     visible des boutons @pseudo / Parametres / Deconnecter, et micro-
     decalage quand le scroll s'arrete). En fixed, la nav est purement
     ancree au viewport — le navigateur ne la repositionne plus du tout
     pendant le scroll, zero tremblement.
     top: var(--header-h) la cale juste sous le bandeau promo. */
  position: fixed;
  top: var(--header-h);
  left: 0;
  right: 0;
  z-index: 15;
  /* .top-nav est transparent et couvre TOUTE la largeur de l'ecran
     (left:0;right:0), meme la ou il n'y a visuellement aucun bouton
     (logo absent, espace vide entre le burger et le bord...). Sans ceci,
     cette bande invisible interceptait quand meme les clics destines a
     ce qu'il y a en dessous (ex: la barre de recherche/bouton
     "Rechercher" qui se retrouve sous cette bande a certaines tailles
     de fenetre) -> zone inutilisable. On desactive les clics sur le nav
     lui-meme et on les rend actifs uniquement sur ses enfants directs
     (logo, liens, boutons, burger...) : seuls les elements visibles et
     cliquables interceptent, tout le reste laisse passer vers le
     contenu en dessous. */
  pointer-events: none;
}
.top-nav > * { pointer-events: auto; }

/* ---------- Bandeau promo (au-dessus du nav) ---------- */

.promo-banner {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 10px 40px;
  background: var(--blue-soft);
  border-bottom: 1px solid var(--blue-line);
}

.promo-banner-msg {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  font-size: 13px;
  color: var(--ink-soft);
}

.promo-msg-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: var(--blue);
}

.promo-msg-icon svg { width: 16px; height: 16px; stroke: currentColor; }

.promo-msg-text {
  white-space: nowrap;
}

.promo-msg-link {
  font-weight: 600;
  color: var(--blue);
  text-decoration: none;
  white-space: nowrap;
  flex-shrink: 0;
}

.promo-msg-link:hover { color: var(--blue-dark); text-decoration: none; }

/* ---------- Shared: top nav ---------- */

.top-nav {
  display: flex;
  align-items: center;
  /* Barre unique centree : tout (marque, liens, actions) vit dans .nav-pill. */
  justify-content: center;
  gap: 14px;
  padding: 20px 40px;
}

/* ---------- Barre "pilule" de navigation ----------
   Conteneur arrondi qui regroupe la marque + les liens principaux
   (Accueil / Recherche / Telegram), facon barre d'app. */
.nav-pill {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 6px 10px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--paper);
  box-shadow: var(--shadow-sm);
  min-width: 0;
}
/* Pas de separateurs : c'est l'espace qui separe les liens du bloc
   d'actions (Se connecter / @pseudo + parametres), lequel vit DANS la
   pilule -> pas de marge auto qui le repousserait a droite. */
.nav-pill .nav-actions { margin-left: 28px; gap: 8px; }

/* ----- Thème + langue dans la barre (visiteur non connecte) ----- */
/* Double classe (.icon-btn.xxx) VOLONTAIRE : la regle .icon-btn generique
   (width/height 34px, svg 17px) est declaree PLUS BAS dans ce fichier avec
   la meme specificite ; sans ce bump elle gagnait le cascade et forcait le
   bouton langue a rester carre -> drapeau, code et chevron se chevauchaient. */
.icon-btn.nav-theme-btn { width: 34px; height: 34px; }
/* Une seule icone a la fois : soleil en thème clair, lune en thème sombre. */
.nav-theme-btn .icon-moon { display: none; }
html[data-theme="night"] .nav-theme-btn .icon-sun,
html[data-theme="gris"]  .nav-theme-btn .icon-sun { display: none; }
html[data-theme="night"] .nav-theme-btn .icon-moon,
html[data-theme="gris"]  .nav-theme-btn .icon-moon { display: block; }

.nav-lang-wrap { position: relative; }
.icon-btn.nav-lang-btn {
  width: auto;
  min-width: 0;
  height: 34px;
  gap: 6px;
  padding: 0 10px;
  flex-shrink: 0;
  white-space: nowrap;
}
.nav-lang-flag { font-size: 15px; line-height: 1; flex-shrink: 0; }
.nav-lang-code {
  font-family: var(--body); font-size: 12.5px; font-weight: 700;
  line-height: 1; flex-shrink: 0;
}
/* Meme raison que ci-dessus : bat ".icon-btn svg { width:17px }". */
.icon-btn.nav-lang-btn .nav-lang-chevron {
  width: 13px; height: 13px; opacity: .7; flex-shrink: 0;
}
/* Le menu reprend .user-pill-menu (animation + fond), on ajuste la largeur. */
.nav-lang-menu { min-width: 234px; padding: 8px; }
.nav-lang-search {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; margin-bottom: 6px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--bg);
}
.nav-lang-search svg { width: 14px; height: 14px; color: var(--ink-soft); flex-shrink: 0; }
.nav-lang-search input {
  flex: 1 1 auto; min-width: 0;
  border: none; background: none; outline: none;
  font-family: var(--body); font-size: 13px; color: var(--ink);
}
.nav-lang-list { display: flex; flex-direction: column; gap: 2px; }
.nav-lang-option {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 9px 10px;
  border: none; border-radius: var(--radius-sm);
  background: none; cursor: pointer; text-align: left;
  transition: background .14s ease;
}
.nav-lang-option:hover { background: var(--bg); }
.nav-lang-name {
  flex: 1 1 auto;
  font-family: var(--display); font-weight: 600; font-size: 13.5px; color: var(--ink);
}
.nav-lang-tag { font-size: 11.5px; font-weight: 600; color: var(--ink-soft); }
/* Langue active : coche a droite. */
.nav-lang-option.selected .nav-lang-tag::after {
  content: '✓'; margin-left: 6px; color: var(--blue); font-weight: 700;
}

/* Marque compacte : uniquement en taille telephone (voir media query 560px),
   ou la barre se reduit a "logo + burger". */
.nav-logo {
  display: none;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  color: var(--blue);
}
.nav-logo svg { width: 19px; height: 19px; }
.nav-pill-item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 14px;
  border-radius: 999px;
  font-family: var(--body);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-soft);
  text-decoration: none;
  white-space: nowrap;
  transition: background .15s ease, color .15s ease;
}
.nav-pill-item svg { width: 15px; height: 15px; flex-shrink: 0; }
.nav-pill-item:hover { background: var(--bg); color: var(--ink); text-decoration: none; }
/* Lien de la page courante : pastille pleine. */
.nav-pill-item.active {
  background: var(--bg);
  color: var(--ink);
  border: 1px solid var(--line);
  padding: 7px 13px;
}

/* Bouton d'action principal a droite (Se connecter). */
.nav-cta {
  display: inline-flex;
  align-items: center;
  padding: 8px 18px;
  border-radius: 999px;
  background: var(--blue);
  color: #fff;
  font-family: var(--body);
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  box-shadow: var(--shadow-sm);
  transition: background .15s ease;
}
.nav-cta:hover, .nav-cta:visited { color: #fff; text-decoration: none; }
.nav-cta:hover { background: var(--blue-dark); }


.top-nav .nav-link {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  padding: 9px 18px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--paper);
  transition: border-color .15s ease, color .15s ease;
}

.top-nav .nav-link:hover { border-color: var(--blue); color: var(--blue); }

.nav-actions { display: flex; align-items: center; gap: 10px; margin-left: auto; }

/* ---------- Popup Telegram (accueil uniquement) ---------- */
.telegram-popup {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 55;
  width: 340px;
  max-width: calc(100vw - 40px);
  padding: 20px;
  background: var(--paper);
  border: 1.5px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  visibility: hidden;
  transform: translateY(16px);
  transition: opacity .28s cubic-bezier(.22, .61, .36, 1),
              transform .28s cubic-bezier(.22, .61, .36, 1),
              visibility .28s;
}

.telegram-popup.open { opacity: 1; visibility: visible; transform: translateY(0); }

.telegram-popup-close {
  position: absolute; top: 14px; right: 14px; width: 30px; height: 30px; display: flex;
  align-items: center; justify-content: center; background: var(--bg); border: none;
  border-radius: 50%; font-size: 20px; line-height: 1; color: var(--ink-soft); cursor: pointer;
  transition: color .15s ease, background .15s ease;
}
.telegram-popup-close:hover { color: var(--rose); }

.telegram-popup-head { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; padding-right: 24px; }

.telegram-popup-icon {
  flex-shrink: 0;
  width: 38px; height: 38px;
  display: flex; align-items: center; justify-content: center;
  background: #29a9eb;
  color: #fff;
  border-radius: 50%;
}
.telegram-popup-icon svg { width: 19px; height: 19px; }

.telegram-popup-head h4 {
  margin: 0;
  font-family: var(--display);
  font-size: 15.5px;
  font-weight: 700;
  color: var(--ink);
}

.telegram-popup-text {
  margin: 0 0 16px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--ink-soft);
}

.telegram-popup-btn { background: #29a9eb; }
.telegram-popup-btn:hover { background: #1c8fcb; }

/* .btn n'est utilisé nulle part ailleurs sur un <a> (partout des <button>) :
   les règles globales a{color}/a:hover{color} et le soulignement par
   defaut du navigateur ne s'appliquaient donc jamais avant ce bouton. Ici
   elles sont plus specifiques que .btn (color:#fff) et gagnaient au survol
   -> texte de la meme couleur que le fond, donc invisible. */
.telegram-popup-btn,
.telegram-popup-btn:hover,
.telegram-popup-btn:visited,
.telegram-popup-btn:active {
  color: #fff;
  text-decoration: none;
}

/* Meme souci que .telegram-popup-btn ci-dessus : #telegramBotLinkBtn
   (flux register Telegram) et #linkTelegramBotLinkBtn (modale Security
   > Relier Telegram) sont aussi des <a class="btn btn-telegram">, donc
   les memes overrides sont necessaires pour eviter un texte invisible
   (couleur identique au fond au survol) et le soulignement par defaut
   des liens. */
#telegramBotLinkBtn,
#telegramBotLinkBtn:hover,
#telegramBotLinkBtn:visited,
#telegramBotLinkBtn:active,
#linkTelegramBotLinkBtn,
#linkTelegramBotLinkBtn:hover,
#linkTelegramBotLinkBtn:visited,
#linkTelegramBotLinkBtn:active {
  color: #fff;
  text-decoration: none;
}
#telegramBotLinkBtn { margin: 10px 0 20px; }
/* Bouton "Ouvrir Telegram" (dans le flux register/link Telegram) et
   bouton "Rejoindre" de la popup Telegram : on GARDE l'animation de
   hover (changement de fond), mais on NEUTRALISE toute animation au
   clic (:active + .btn-pressed pose en JS) — le bouton reste fixe a
   l'appui, il ne se decale plus vers le bas. */
#telegramBotLinkBtn:active,
#telegramBotLinkBtn.btn-pressed,
#linkTelegramBotLinkBtn:active,
#linkTelegramBotLinkBtn.btn-pressed,
.telegram-popup-btn:active,
.telegram-popup-btn.btn-pressed {
  transform: none !important;
}

/* En dessous de cette largeur, le contenu centré (cartes stats/features,
   max-width 940/720px) se rapproche trop du bord droit de la fenêtre et
   finit par passer sous la popup fixe (bottom-right) -> on la masque
   complètement, comme .site-header/.promo-banner plus bas qui disparaît
   sous 560px, mais avec un seuil plus grand car ce chevauchement apparaît
   bien avant le format mobile. Pure CSS (comme le bandeau) : pas besoin
   d'écouter le resize en JS, la popup réapparaît d'elle-même dès qu'on
   repasse au-dessus, avec son état (ouverte/fermée) déjà en place. */
@media (max-width: 1550px) {
  .telegram-popup { display: none !important; }
}
@media (max-height: 750px) {
  .telegram-popup { display: none !important; }
}

/* Ancienne règle de repositionnement mobile (left/right/bottom:16px)
   supprimée : elle ne s'appliquait qu'en dessous de 560px, largeur à
   laquelle la popup est de toute façon masquée par la règle ci-dessus
   (max-width: 1550px) — donc plus jamais atteinte. */

/* ---------- Pastille "@pseudo" (remplace le lien "Tableau de bord") ---------- */

.user-pill-wrap { position: relative; }

.user-pill {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 16px 5px 5px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-weight: 600;
  font-size: 14px;
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}

.user-pill:hover { border-color: var(--blue); background: var(--blue-soft); }

.user-pill-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  flex-shrink: 0;
  background: var(--bg);
}

.user-pill-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 120px;
}

/* Menu déroulant : positionné, mais masqué (opacité + léger décalage vers
   le haut + scale) tant que .user-pill-wrap n'a pas la classe "open". La
   transition ci-dessous joue à l'ouverture ET à la fermeture. */
.user-pill-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 190px;
  padding: 6px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  transform: translateY(-6px) scale(.96);
  transform-origin: top right;
  pointer-events: none;
  transition: opacity .16s ease, transform .16s ease;
  z-index: 40;
}

.user-pill-wrap.open .user-pill-menu {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}

/* Menus deroulants de la barre de navigation : le décalage est calcule
   depuis le BOUTON, or celui-ci est encastre dans .nav-pill (6px de padding).
   Avec le 8px par defaut, le menu n'etait donc qu'a ~2px sous la barre et
   semblait collé. On ajoute la hauteur du padding de la pilule pour obtenir
   un vrai espace visible entre la barre et le menu. */
.nav-pill .user-pill-menu { top: calc(100% + 16px); }

.user-pill-wrap.open .user-pill {
  border-color: var(--blue);
  background: var(--blue-soft);
}

.user-pill-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-family: var(--body);
  font-weight: 600;
  font-size: 14px;
  text-decoration: none;
  transition: background .12s ease, color .12s ease;
}

.user-pill-menu-item:hover { background: var(--blue-soft); color: var(--blue); }
.user-pill-menu-item svg { width: 17px; height: 17px; flex-shrink: 0; }

/* Bouton hamburger : masqué par défaut, révélé uniquement en très petite
   largeur (voir media query en bas de fichier) pour remplacer les boutons
   Connexion / Créer un compte. */
.nav-burger {
  display: none;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--paper);
  color: var(--ink);
  cursor: pointer;
  transition: border-color .15s ease, color .15s ease;
}

.nav-burger:hover { border-color: var(--blue); color: var(--blue); }
.nav-burger svg { width: 22px; height: 22px; }
/* Bascule burger <-> croix selon l'etat du menu (aria-expanded pose par le
   JS). Une seule des deux icones est affichee a la fois. */
.nav-burger .burger-close { display: none; }
.nav-burger[aria-expanded="true"] .burger-open { display: none; }
.nav-burger[aria-expanded="true"] .burger-close { display: block; }

/* ---------- Menu mobile plein écran (fond flouté translucide) ---------- */
.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  /* Effet "verre" : translucide + flou du contenu derrière.
     Blur reduit de 16px a 10px + saturation 160% -> 145% + fond legerement
     plus opaque (0.72 -> 0.82) pour compenser visuellement. Rendu quasi
     identique a l'oeil mais chaque frame de recomposition coute beaucoup
     moins cher sur Chrome (le blur est en O(r^2) : 10^2 vs 16^2 = ~61%
     de calcul en MOINS par frame). C'est ce qui rendait l'ouverture/
     fermeture du menu saccadee alors que Firefox etait deja parfait :
     Chrome ne fusionne pas le blur avec l'animation opacity, il doit
     re-convoluer le fond a chaque valeur intermediaire. Moins de rayon =
     moins de saccades, sur Chrome comme sur les Chrome-based (Edge,
     Android Chrome, WebView...). */
  background: rgba(245, 248, 253, 0.82);
  -webkit-backdrop-filter: blur(10px) saturate(145%);
  backdrop-filter: blur(10px) saturate(145%);
  /* will-change permanent : Chrome garde le compositor layer du menu
     (avec son backdrop-filter) pre-alloue en permanence, meme quand le
     menu est visibility:hidden. Consequence : la PREMIERE ouverture ne
     paie plus le cout d'allocation du layer + du premier calcul de blur
     (qui rendait la premiere anim visiblement plus laggy que les suivantes
     sur Chrome). Cout memoire GPU : negligeable (un layer transparent tant
     que visibility:hidden), gagne systematiquement une premiere ouverture
     100% fluide. Firefox l'ignore poliment (aucun cout la-bas). */
  will-change: opacity;
  opacity: 0;
  visibility: hidden;
  /* pointer-events: none tant qu'il n'est pas ouvert. Necessaire en plus
     de visibility:hidden : le navigateur ne bascule visibility sur
     "hidden" qu'a la FIN de sa transition (pas au debut), donc pendant
     tout le fondu de fermeture l'overlay restait visibility:visible et
     donc cliquable/tapable, alors qu'il etait deja quasi invisible a
     l'ecran. Un tap sur le bouton burger juste apres avoir ferme le menu
     (ex: lien "Connexion" clique depuis le menu, qui ferme + navigue en
     meme temps) atterrissait donc sur cet overlay fantome au lieu du
     bouton en dessous : rien ne s'ouvrait, et on pouvait apercevoir la
     croix de fermeture qui finissait son fondu, donnant l'impression d'un
     menu qui s'ouvre puis se referme instantanement. */
  pointer-events: none;
  /* Fermeture : visibility ne bascule qu'a la FIN du fondu (duree 0s +
     delai .28s aligne sur l'opacity). Avant : "visibility .22s ease" qui,
     visibility etant boolean, sautait a mi-parcours (a 50% de la courbe
     ease) -> le menu disparaissait visuellement en plein milieu de son
     propre fade-out. Nouveau easing : cubic-bezier(.22, .61, .36, 1)
     (meme que le body reveal / nav-leaving du site) pour rester coherent
     avec les autres transitions de la page. */
  /* Fermeture : easeIn doux, duree calee sur les enfants (.28s) pour
     que le fond disparaisse en meme temps que les boutons se retirent. */
  transition:
    opacity .28s cubic-bezier(.4, 0, .2, 1),
    visibility 0s linear .28s;
}

.mobile-menu.open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  /* Ouverture : easeOut doux, un peu plus long que la fermeture pour
     un effet "vivant" a l'entree. Visibility bascule immediatement. */
  transition:
    opacity .34s cubic-bezier(.22, 1, .36, 1),
    visibility 0s;
}

/* Animation transform independante sur le contenu (croix + boutons), en
   supplement du fondu opacity du parent. Les enfants heritent deja de
   l'opacity 0 -> 1 pendant .28s, on superpose ici un leger glissement
   vertical sur le bloc de boutons et un mini pop scale sur la croix.
   Purement compositeur (transform), aucun cout perf supplementaire meme
   sur Chrome -> c'est ce qui donne cette impression "vivante et lisse" a
   l'ouverture (le contenu arrive avec du peps, pas juste un aplat qui
   apparait). En fermeture : lecture inversee, tout retombe doucement.

   translateZ(0) systematique dans le transform : force le compositeur GPU
   a rester actif pour ces elements meme apres la fin de l'animation (quand
   la transformation devient "translateY(0)" ou "scale(1)", donc l'identite,
   Chrome relachait le layer et re-rendait sur le main thread -> snap
   subpixel de 1-2px = le fameux "saut" de la croix/des boutons en fin
   d'anim). En gardant translateZ(0), le layer ne quitte plus le GPU :
   le rendu final est pixel-parfait, identique au rendu anime. Firefox
   accepte translateZ(0) comme no-op, aucun cout la-bas. */
.mobile-menu-inner {
  transform: translateY(22px) translateZ(0);
  backface-visibility: hidden;
  will-change: transform;
  /* Courbe "spring-out" doux : les boutons montent avec un leger
     depassement puis se posent. Plus vivant qu'un simple easeOut. */
  transition: transform .42s cubic-bezier(.34, 1.15, .5, 1);
}
.mobile-menu.open .mobile-menu-inner {
  transform: translateY(0) translateZ(0);
}


.mobile-menu-inner {
  margin: auto;
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
  max-width: 320px;
  padding: 24px;
}

.mobile-menu-link {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 15px 20px;
  border-radius: var(--radius);
  font-family: var(--display);
  font-size: 16px;
  font-weight: 600;
  text-decoration: none;
  text-align: center;
  /* Etat de base (menu ferme) : decalage vers le bas + invisible.
     - A L'OUVERTURE : easeOut long (.34s) + spring subtil sur transform
       -> les boutons "montent" avec du peps.
     - A LA FERMETURE : easeIn plus court (.22s) -> les boutons
       s'estompent rapidement en glissant vers le bas.
     Les timings sont overrides plus bas par le stagger. */
  opacity: 0;
  transform: translateY(14px);
  transition:
    opacity .22s cubic-bezier(.4, 0, 1, 1),
    transform .22s cubic-bezier(.4, 0, 1, 1),
    background-color .14s ease-out,
    border-color .14s ease-out,
    color .14s ease-out,
    box-shadow .14s ease-out;
}
.mobile-menu.open .mobile-menu-link {
  opacity: 1;
  transform: translateY(0);
  transition:
    opacity .34s cubic-bezier(.22, 1, .36, 1),
    transform .42s cubic-bezier(.34, 1.15, .5, 1),
    background-color .14s ease-out,
    border-color .14s ease-out,
    color .14s ease-out,
    box-shadow .14s ease-out;
}
/* Stagger a l'ouverture : le 1er bouton demarre a 60ms, le 2e a 120ms. */
.mobile-menu.open .mobile-menu-link:nth-child(1) {
  transition-delay: .06s, .06s, 0s, 0s, 0s, 0s;
}
.mobile-menu.open .mobile-menu-link:nth-child(2) {
  transition-delay: .12s, .12s, 0s, 0s, 0s, 0s;
}
/* Stagger INVERSE a la fermeture : le 2e bouton part en premier (0ms),
   le 1er suit legerement plus tard (30ms) -> effet "cascade descendante"
   qui donne une vraie sensation de fermeture soignee au lieu d'un plat
   fade generique. */
.mobile-menu:not(.open) .mobile-menu-link:nth-child(1) {
  transition-delay: .03s, .03s, 0s, 0s, 0s, 0s;
}
.mobile-menu:not(.open) .mobile-menu-link:nth-child(2) {
  transition-delay: 0s;
}

/* Premier lien = action principale (Connexion), en plein bleu ; le second
   (Créer un compte) en contour, pour hiérarchiser comme sur le reste du site. */
.mobile-menu-link:first-child {
  background: var(--blue);
  color: #fff;
  box-shadow: var(--shadow-md);
}
.mobile-menu-link:first-child:hover { background: var(--blue-dark); }

.mobile-menu-link:last-child {
  background: var(--paper);
  color: var(--ink);
  border: 1.5px solid var(--line);
}
.mobile-menu-link:last-child:hover { border-color: var(--blue); color: var(--blue); }

/* Combine translateY(0) (etat "menu ouvert") avec le petit scale du
   :active, sinon l'un ecraserait l'autre et le bouton "sauterait" a
   l'appui. */
.mobile-menu.open .mobile-menu-link:active { transform: translateY(0) scale(0.98); }

/* Voir le commentaire JS sur le listener "resize" et le paramètre "instant"
   de closeMobileMenu() (main.html) : quand la fenêtre est agrandie au-delà
   de 560px pendant que le menu est ouvert, la classe "no-transition" est
   posée le temps d'un frame pour que la fermeture soit immédiate (aucun
   fondu ni glissement animé), plutôt que de jouer une transition par-dessus
   un redimensionnement encore actif. */
.mobile-menu.no-transition,
.mobile-menu.no-transition .mobile-menu-inner {
  transition: none !important;
}

/* Quand on clique sur un lien du menu, le panneau est cache INSTANTANEMENT
   (opacity 0 sans transition) pendant que la nouvelle page se revele avec
   son fondu habituel. Sans ca, on voyait les liens "pop" en train de
   s'estomper alors que la nouvelle page apparaissait dessous. */
.mobile-menu.closing-to-page .mobile-menu-inner {
  transition: none !important;
  opacity: 0 !important;
}

/* ---------- Hero (page d'accueil : recherche directe) ---------- */

.hero-wrap { position: relative; overflow: hidden; }

.hero {
  text-align: center;
  max-width: 660px;
  margin: 0 auto;
  padding: 56px 24px 56px;
  position: relative;
  z-index: 2;
}

.hero h1 {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(15px, 4.4vw, 32px);
  line-height: 1.12;
  letter-spacing: -0.02em;
  margin: 0 0 16px;
  color: var(--ink);
  white-space: nowrap;
}

.hero h1 em { font-style: normal; color: var(--blue); }

.hero p.tag {
  color: var(--ink-soft);
  font-size: 16px;
  line-height: 1.6;
  margin: 0 0 32px;
}

.hero-search {
  display: flex;
  gap: 10px;
  max-width: 620px;
  margin: 0 auto;
}

.search-box-lg {
  position: relative;
  text-align: left;
  flex: 1 1 auto;
  width: 100%;
  height: 54px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  box-shadow: var(--shadow-md);
  transition: border-color .2s ease, box-shadow .2s ease;
}

.search-box-lg:focus-within {
  border-color: var(--blue);
  box-shadow: 0 0 0 4px var(--focus-ring);
}

.search-toggle {
  position: absolute;
  left: 0;
  top: 0;
  width: 54px;
  height: 54px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 0;
  cursor: default;
  color: var(--ink-faint);
  pointer-events: none;
}

.search-toggle svg { width: 19px; height: 19px; stroke: currentColor; }

.search-box-lg:focus-within .search-toggle { color: var(--blue); }

.search-box-lg input {
  width: 100%;
  height: 100%;
  padding: 0 18px 0 54px;
  border: none;
  background: transparent;
  font-family: var(--body);
  font-size: 15.5px;
  color: var(--ink);
}

.search-box-lg input:focus { outline: none; }

.hero-search .btn-hero {
  width: auto;
  padding: 0 26px;
  border-radius: var(--radius);
  font-size: 15px;
  flex-shrink: 0;
}
/* Bouton "Rechercher" : on garde le survol (fond plus fonce) mais PAS
   l'animation d'enfoncement au clic (translateY). Double classe
   (.btn.btn-hero) pour battre ".btn:active" declaree plus bas. */
.btn.btn-hero:active,
.btn.btn-hero:active:hover { transform: none !important; }

/* ---------- Auth shell (login / register) ---------- */

.auth-shell {
  min-height: calc(100vh - var(--header-h) - var(--nav-h));
  display: flex;
  justify-content: center;
  /* Padding BAS = 0 : quand la carte depasse l'ecran, on scrollait jusqu'au
     bas de la carte puis il restait cette marge vide de 24px en dessous
     (le "petit ecart" qu'on pouvait encore descendre). Sans padding-bottom,
     le bas de la carte = le bas du scroll : arret net, plus aucun ecart.
     On garde le padding haut (respiration en haut) et lateral. */
  padding: 24px 24px 0;
}

.access-card {
  /* Centrage vertical via margin: auto plutôt que align-items: center.
     Quand la carte est plus haute que l'espace disponible (petit écran,
     zoom...), align-items: center coupe le haut du débordement de façon
     incohérente avec le point de scroll réel : c'est ce qui provoque le
     petit "recentrage" de quelques pixels une fois arrivé en haut de la
     barre de scroll. margin: auto n'a pas ce défaut : il se réduit à 0
     dès que le contenu déborde, donc le haut réel de la carte reste
     toujours atteignable en scrollant, sans à-coup. */
  margin-top: auto;
  margin-bottom: auto;
  width: 100%;
  max-width: 400px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 40px 36px 34px;
  position: relative;
}

.card-brand-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 18px;
}

.card-icon {
  width: 48px;
  height: 48px;
  border-radius: 13px;
  background: var(--blue-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 0;
  flex-shrink: 0;
}

.card-icon svg { width: 24px; height: 24px; stroke: var(--blue); }

.brand-mark {
  font-family: var(--body);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--blue);
  margin: 0;
}

.access-card h1 {
  font-family: var(--display);
  font-weight: 700;
  font-size: 27px;
  letter-spacing: -0.01em;
  margin: 0 0 6px;
}

.access-card .sub {
  color: var(--ink-soft);
  font-size: 14px;
  margin: 0 0 26px;
}

.field { margin-bottom: 16px; }

.field label {
  display: block;
  font-size: 12.5px;
  font-family: var(--body);
  font-weight: 600;
  color: var(--ink-soft);
  margin-bottom: 6px;
}

.field .input-wrap { position: relative; }

.field .input-wrap svg {
  position: absolute;
  left: 13px;
  top: 50%;
  transform: translateY(-50%);
  width: 17px;
  height: 17px;
  stroke: var(--ink-faint);
  pointer-events: none;
}

.field input {
  width: 100%;
  padding: 11px 14px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper);
  font-family: var(--body);
  font-size: 14.5px;
  color: var(--ink);
  transition: border-color .15s ease, box-shadow .15s ease;
}

.field .input-wrap svg ~ input {
  padding-left: 40px;
}

.field input:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 4px var(--focus-ring);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 12px 18px;
  background: var(--blue);
  color: #fff;
  border: none;
  border-radius: var(--radius-sm);
  font-family: var(--body);
  font-size: 14.5px;
  font-weight: 600;
  cursor: pointer;
  text-align: center;
  box-shadow: var(--shadow-sm);
  transition: background .15s ease, transform .1s ease;
}

.btn:hover { background: var(--blue-dark); }
.btn:active { transform: translateY(1px); }
.btn.btn-cooldown:active { transform: none; }
.btn:focus-visible { outline: 2px solid var(--blue-dark); outline-offset: 2px; }

/* ---------- Bouton "Continuer avec Telegram" ---------- */

.btn-telegram {
  /* Bleu Telegram plutôt que le bleu de marque du site (var(--blue)) :
     reconnaissable au premier coup d'œil, comme un vrai bouton OAuth. */
  background: #29a9eb;
}
.btn-telegram:hover { background: #1e8fca; }
.btn-telegram:focus-visible { outline-color: #1e8fca; }
.btn-telegram svg { width: 19px; height: 19px; flex-shrink: 0; }

/* Pas de séparateur "ou" : juste un peu d'espace sous le bouton, pour que
   le formulaire classique en dessous reste sa propre section sans ajouter
   d'élément visuel supplémentaire. */
#registerSocialRow,
#loginSocialRow { margin-bottom: 28px; }

/* ---------- Flux "Continuer avec Telegram" (étapes) ---------- */

#telegramRegisterFlow .btn { margin-top: 4px; }
#telegramRegisterFlow #telegramBackBtn,
#telegramRegisterFlow #telegramCancelBtn {
  display: block;
  margin: 12px auto 0;
  text-align: center;
}

/* Bloc "aide Telegram" en tete de l'etape 2 du register Telegram :
   regroupe le texte d'instructions et le bouton "Ouvrir le bot Telegram"
   dans une petite carte visuellement distincte, pour separer nettement
   l'action "sur Telegram" (ouvrir le bot, taper /start puis /register)
   des champs a remplir "ici" (identifiant + code). */
.tg-help {
  border: 1px solid var(--line);
  background: var(--bg);
  border-radius: var(--radius);
  padding: 12px 12px 14px;
  margin-bottom: 10px;
}
.tg-help-text {
  margin: 0 0 10px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-soft);
}
.tg-help-text strong { color: var(--ink); font-weight: 600; }
/* Le bouton n'a aucune marge dans le bloc d'aide : l'espace au-dessus est
   deja donne par la marge basse du .tg-help-text, et celui en dessous par
   le padding du .tg-help lui-meme (12px). Vise l'ancre a la fois cote
   .btn (bouton register) et cote .btn-telegram (lien Ouvrir Telegram, qui
   ne matchait pas la regle #telegramRegisterFlow .btn plus haut car c'est
   un <a>). */
.tg-help .btn,
.tg-help .btn-telegram { margin: 0; }
/* Le bouton "Ouvrir Telegram" du flux register heritait de
   "#telegramRegisterFlow .btn { margin-top: 4px }" (specificite ID plus
   forte que ".tg-help .btn"), ce qui rendait ce cadre plus grand que celui
   du flux "Relier Telegram" (Security). On force le meme margin-top: 0 pour
   que les deux cadres aient EXACTEMENT la meme taille. */
#telegramRegisterFlow .tg-help .btn,
#telegramRegisterFlow .tg-help .btn-telegram { margin-top: 0; }
/* Cadre "Ouvrir Telegram" du flux register : meme taille EXACTE que celui
   de "Relier Telegram" (screen 2). Haut normal (12px), bas compact (10px). */
#telegramRegisterFlow .tg-help {
  padding: 12px 12px 0px;
}

/* ---------- Bulles de validation (login / register) ----------
   Case qui apparaît/disparaît sous un champ via une transition de
   hauteur + opacité + décalage vertical (même animation que la source de
   référence). Trois états : 'error' (jaune, champ requis), 'invalid'
   (rouge, contenu incorrect) et 'success' (vert, contenu valide). */
.message {
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  padding: 0 12px;
  margin-top: 0;
  border-radius: var(--radius-sm);
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1.4;
  border: 1px solid transparent;
  border-width: 0;
  /* translate3d au lieu de translateY : force la promotion en layer GPU
     et fige le mode de rendu texte (subpixel / grayscale) EN PERMANENCE.
     Sans ca, Chrome commence l'anim en subpixel puis bascule en grayscale
     des qu'un transform actif est detecte, puis rebascule a la fin —
     resultat : le texte apparait, se decale d'1-2 px, puis se recale au
     bon endroit. Meme correctif que celui deja utilise sur .modal-inner
     pour le rebond elastique (voir plus haut dans ce fichier). */
  transform: translate3d(0, -4px, 0);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  transition: max-height 0.22s cubic-bezier(0.16, 1, 0.3, 1),
              opacity 0.18s ease,
              padding 0.2s ease,
              margin-top 0.2s ease,
              border-width 0.2s ease,
              transform 0.18s ease;
}

.message.show {
  max-height: 90px;
  opacity: 1;
  padding: 9px 12px;
  margin-top: 8px;
  border-width: 1px;
  /* translate3d(0,0,0) au lieu de translateY(0)/none : garde le layer
     compositor promu au repos aussi, sinon Chrome le detruit a la fin
     de l'anim et re-render le texte en subpixel -> replacement d'1-2 px
     visible juste apres l'apparition. */
  transform: translate3d(0, 0, 0);
}

.message.error {
  background: rgba(217, 142, 43, 0.08);
  border-color: rgba(217, 142, 43, 0.4);
  color: #b06f18;
}

.message.invalid {
  background: rgba(217, 45, 32, 0.07);
  border-color: rgba(217, 45, 32, 0.35);
  color: #d92d20;
}

.message.success {
  background: rgba(18, 183, 106, 0.08);
  border-color: rgba(18, 183, 106, 0.38);
  color: #067647;
}

/* Theme Night : les bulles de validation gardent EXACTEMENT le meme
   rendu qu'en theme clair. Leurs fonds sont translucides (rgba faible) :
   poses sur un fond sombre ils viraient au fonce. On les repasse donc en
   couleurs solides equivalentes a leur aspect sur fond blanc, pour que la
   bulle soit identique quel que soit le theme. */
html[data-theme="night"] .message.error {
  background: #fbf3e7;
  border-color: #eecfa1;
  color: #b06f18;
}
html[data-theme="night"] .message.invalid {
  background: #fbece9;
  border-color: #f0b3aa;
  color: #d92d20;
}
html[data-theme="night"] .message.success {
  background: #e7f7ef;
  border-color: #a3e2c3;
  color: #067647;
}

/* ---------- Case à cocher TOS (inscription) ---------- */

.tos-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  margin: 4px 0 16px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  /* Fond volontairement transparent (avant : var(--paper), qui doublait
     inutilement le fond papier de la .access-card parente). Rendu visuel
     rigoureusement identique - la carte est deja papier - mais un layer
     de paint en moins. C'est ce qui evitait que le cadre TOS apparaisse
     avec un tres leger retard sur Chrome lors du premier /register depuis
     l'accueil : la carte peignait son fond, puis ses enfants texte (les
     champs), et le fond opaque + la bordure de la .tos-row arrivaient
     APRES tout ca, sur une frame supplementaire -> effet "flash" du
     cadre qui se pose une fraction de seconde apres le reste. Sans le
     background, il n'y a plus de "couche pleine" a peindre pour la
     tos-row : seulement la bordure + le texte, qui sont peints dans
     la meme passe que les autres enfants de la carte. */
  background: transparent;
}

.tos-check {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  user-select: none;
  padding-bottom: 10px;
  margin-bottom: 10px;
  border-bottom: 1px solid var(--line-soft);
}

.tos-check input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.tos-box {
  flex: none;
  width: 18px;
  height: 18px;
  border-radius: 5px;
  border: 1.5px solid var(--ink-faint);
  background: var(--paper);
  display: flex;
  align-items: center;
  justify-content: center;
  align-self: center;
  transition: background .15s ease, border-color .15s ease;
}

.tos-box svg {
  width: 12px;
  height: 12px;
  stroke: #fff;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity .15s ease, transform .15s ease;
}

.tos-check input:checked ~ .tos-box {
  background: var(--blue);
  border-color: var(--blue);
}

.tos-check input:checked ~ .tos-box svg {
  opacity: 1;
  transform: scale(1);
}

.tos-check input:focus-visible ~ .tos-box {
  outline: 2px solid var(--blue-dark);
  outline-offset: 2px;
}

.tos-label {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink);
}

.tos-link {
  background: none;
  border: none;
  padding: 0;
  margin: 0 0 0 28px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--blue);
  cursor: pointer;
  text-align: left;
}

.tos-link:hover { color: var(--blue-dark); text-decoration: none; }

/* Petit espace entre la bulle "champs manquants" et la bulle TOS quand
   les deux sont affichées l'une sous l'autre. */
#registerTosMsg.show { margin-top: 6px; }

/* ---------- Modal TOS : petite animation d'apparition ---------- */

/* On n'utilise plus display:none <-> flex ici (c'etait le vrai coupable
   de la latence/saccade a l'ouverture comme a la fermeture) :
   - display force le navigateur a rebatir tout le style/layout de la
     modale au moment du clic, en meme temps qu'il doit peindre le
     backdrop-filter plein ecran -> gros pic de travail synchrone sur
     le thread principal.
   - ca obligeait aussi le JS a faire un reflow force (offsetWidth) puis
     un rAF pour que la transition demarre correctement ; des que le
     thread principal etait un peu charge, Chrome fusionnait les deux
     changements de classe et sautait carrement la transition (d'ou
     l'impression que ca "bug").
   A la place : opacity + visibility, exactement comme .mobile-menu plus
   haut sur cette page, qui n'a jamais ce probleme. L'element reste dans
   l'arbre de rendu en permanence (juste invisible et non cliquable), donc
   l'ouverture/fermeture ne fait plus que de la composition GPU, sans
   recalcul de layout ni de style. Plus besoin de JS complique : un seul
   classList.toggle('open') suffit desormais. */
.tos-overlay {
  display: flex;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  background: rgba(16, 24, 40, 0.58);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  /* Volontairement PAS de will-change permanent : ca gardait un layer
     GPU plein-ecran alloue en continu meme quand la modale etait fermee
     -> perceptible en lag une fois ouvert (rendu plus mou, hover moins
     reactifs). Le navigateur promeut automatiquement le layer pendant
     la transition d'ouverture, c'est suffisant. */
  transition: opacity .32s ease, visibility 0s linear .32s;
}

/* Retrait du backdrop-filter de .modal-overlay UNIQUEMENT pour la modale TOS.
   .modal-overlay applique "background: rgba(16,24,40,0.5); backdrop-filter:
   blur(2px)" a toutes les modales. Sur Firefox, ce blur est peint une fois
   et cache par le compositeur pendant le scroll interne de la modale ->
   parfaitement fluide. Sur Chrome, backdrop-filter force un repaint synchrone
   du blur plein ecran a CHAQUE frame de scroll de n'importe quel enfant :
   c'est un bug de perf connu (crbug.com/1256614 et derives), qui donne
   exactement le lag decrit + qui peut carrement bloquer le scroll quand la
   plage scrollable est petite (fenetre plein ecran / juste en dessous). La
   modale TOS est la seule concernee dans l'app (les autres modales ne
   scrollent pas), donc on ne perd rien ailleurs.

   La compensation visuelle : on remonte legerement l'opacite du fond
   (0.5 -> 0.58) pour compenser le flou perdu -> a l'oeil, la profondeur
   d'occultation reste identique.

   Specificite : ".modal-overlay.tos-overlay" (0,2,0) bat ".modal-overlay"
   (0,1,0), donc on gagne le cascade quel que soit l'ordre des regles dans
   le fichier. Pas besoin de !important. */
/* Double-classe pour battre .modal-overlay (display:none) declare plus bas. */
.modal-overlay.tos-overlay {
  display: flex;
}

.tos-overlay.open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity .32s ease;
}

/* Wrapper interne des modales scrollables (TOS, Password, Security...) :
   c'est CE bloc qui recoit le translateY du scroll sub-pixel + du rebond
   elastique JS (voir initModalElasticScroll dans main.html), jamais la box
   elle-meme (sinon toute la fenetre bouge) ni les enfants un par un (sinon
   chacun a son propre arrondi sub-pixel -> desynchronisation = "replacement"). */
/* Meme raisonnement que .page-scroll-inner ci-dessus : layer + rendu
   texte figes en permanence pour eviter le micro-replacement de texte
   en fin de rebond elastique, sans will-change pour eviter le
   tremblement au hover. */
.modal-inner {
  transform: translateZ(0);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Lien "Mot de passe oublié ?" : place sous le champ mot de passe, juste
   au-dessus du bouton de connexion, aligne a droite. */
.forgot-link {
  display: block;
  margin: -2px auto 14px 0;
  padding: 0;
  border: none;
  background: none;
  /* font: inherit AVANT les reglages : neutralise la police que le
     navigateur applique d'office aux <button> (graisse/rendu legerement
     differents d'un <a>, d'ou l'impression de texte plus gras/plus gros
     que "Créer un compte"). On repart ensuite exactement sur les valeurs
     du lien de .switch-line. */
  font: inherit;
  font-family: var(--body);
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.5;
  letter-spacing: normal;
  color: var(--blue);
  text-align: left;
  cursor: pointer;
  transition: color .15s ease;
}
.forgot-link:hover { color: var(--blue-dark); text-decoration: none; }

.switch-line {
  margin-top: 22px;
  font-size: 13.5px;
  color: var(--ink-soft);
  border-top: 1px solid var(--line-soft);
  padding-top: 18px;
  text-align: center;
}

.switch-line a { font-weight: 600; text-decoration: none; }
.switch-line a:hover { text-decoration: none; }

/* ---------- Statistiques publiques (accueil) ---------- */
.public-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
  max-width: 940px;
  margin: 8px auto 0;
  padding: 0 24px;
  position: relative;
  z-index: 2;
}

.pstat-card {
  display: flex;
  align-items: center;
  gap: 14px;
  /* min-width:0 : sans ça, un item de grille garde par défaut la largeur
     minimale de son contenu (ici le mot "ENREGISTREMENTS", qui ne peut
     pas se couper) comme largeur plancher absolue. La grille ne peut
     alors ni rétrécir ni se réorganiser en dessous de cette largeur ->
     elle déborde et la dernière carte se fait couper par le bord de la
     fenêtre, avant même d'atteindre le point de rupture censé passer en
     2 colonnes. */
  min-width: 0;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px 18px;
  box-shadow: var(--shadow-sm);
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}

.pstat-card:hover {
  border-color: var(--blue-line);
  box-shadow: var(--shadow-md);
}

.pstat-card .ps-icon {
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  border-radius: 10px;
  background: var(--blue-soft);
  display: flex;
  align-items: center;
  justify-content: center;
}

.pstat-card .ps-icon svg { width: 19px; height: 19px; stroke: var(--blue); color: var(--blue); }

.pstat-card .ps-body { min-width: 0; }

.pstat-card .ps-lbl {
  font-family: var(--body);
  font-weight: 600;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  margin-bottom: 4px;
  /* Filet de sécurité : si jamais la carte se retrouve plus étroite que
     ce mot (zoom navigateur, fenêtre partagée, etc.), il se coupe au lieu
     de forcer toute la grille à déborder. */
  overflow-wrap: anywhere;
}

.pstat-card .ps-num {
  font-family: var(--body);
  font-size: 17px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.01em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 1000px) {
  /* Ancien seuil : 720px. Trop tard : la largeur minimale des 4 cartes
       (contrainte par le mot "ENREGISTREMENTS", incassable) fait déborder
       la ligne dès qu'on descend sous ~980px (max-width:940px + paddings),
       bien avant 720px -> la dernière carte se faisait couper par le bord
       de la fenêtre au lieu de se réorganiser. 1000px laisse une petite
       marge de sécurité au-dessus de ce seuil réel. */
    .public-stats { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 480px) {
  /* Sur les très petits téléphones, même 2 colonnes de ~205px minimum
       chacune (toujours à cause du même mot) ne rentrent plus -> on passe
       à 1 seule colonne. */
    .public-stats { grid-template-columns: 1fr; }
}

.feature-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  max-width: 720px;
  margin: 48px auto 0;
  padding: 0 24px 64px;
  position: relative;
  z-index: 2;
}

.feature-card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px 18px;
  text-align: left;
  box-shadow: var(--shadow-sm);
}

.feature-card .f-icon {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: var(--blue-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 12px;
}

.feature-card .f-icon svg { width: 19px; height: 19px; stroke: var(--blue); }

.feature-card h4 {
  font-family: var(--display);
  font-size: 14.5px;
  font-weight: 700;
  margin: 0 0 4px;
}

.feature-card p {
  font-size: 12.5px;
  color: var(--ink-soft);
  margin: 0;
  line-height: 1.5;
}
.sidebar .brand-mark { margin: 0; font-size: 10.5px; }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px;
  flex-shrink: 0; border: 1.5px solid var(--line); background: var(--paper); color: var(--ink-soft);
  cursor: pointer; border-radius: var(--radius-sm); transition: border-color .15s ease, color .15s ease;
}

.icon-btn:hover { color: var(--blue); border-color: var(--blue); background: var(--blue-soft); }

.icon-btn svg { width: 17px; height: 17px; display: block; }

/* Bouton "Se deconnecter" : rouge au survol (au lieu du bleu par defaut
   de .icon-btn) — meme couleur d'accent que .modal-close ou .btn-ghost,
   pour signaler visuellement l'action destructive. Meme transition que
   .icon-btn, background dedie var(--rose-dim) pour rester lisible. */


/* Icône engrenage du bouton "Paramètres" : légère rotation au survol
   (transform sur le svg, indépendant de la transition couleur/bordure
   du bouton lui-même). */
/* Le translateZ(0) est present AUSSI au repos (pas seulement au survol) :
   sans lui, le navigateur cree un calque GPU au debut de la rotation puis le
   DETRUIT a la fin, ce qui re-rasterise le SVG avec un arrondi sub-pixel
   different -> micro-decalage + artefact (petit carre) en fin d'animation.
   Avec un calque stable en permanence, aucune bascule de rendu possible.
   transform-origin explicite pour que la rotation reste parfaitement
   centree quelle que soit la taille calculee de l'icone. */
#settingsBtn svg {
  transition: transform .4s ease;
  transform: rotate(0deg) translateZ(0);
  transform-origin: 50% 50%;
  backface-visibility: hidden;
}
#settingsBtn:hover svg { transform: rotate(90deg) translateZ(0); }

/* ---------- Modal (paramètres) ---------- */

.modal-overlay {
  position: fixed; inset: 0; background: rgba(16, 24, 40, 0.5); backdrop-filter: blur(2px);
  display: none; align-items: center; justify-content: center; padding: 24px; z-index: 100;
}
/* z-index 100 : au-dessus de la popup Telegram (z-index 55) et du menu
   mobile (z-index 60). Sans ca, la popup Telegram "Rejoins notre Telegram"
   du coin bas-droit restait affichee en pleine couleur au-dessus du fond
   flouté de la modale, brisant l'effet de mise a l'ecart du reste de
   l'ecran. */

.modal-overlay.open { display: flex; }

/* ---------- Sous-modales Paramètres (Password / Security) : animation ---
   Meme mecanique que la modale TOS (voir .tos-overlay plus haut) : la
   sous-modale reste en permanence dans l'arbre de rendu (opacity:0 /
   visibility:hidden), on active .open pour la faire apparaitre. Fondu du
   fond + slide vertical de la carte.

   Note importante sur le texte qui "sautait" de quelques pixels a la fin
   de l'anim d'ouverture / debut de fermeture :
   - Pas de scale() dans le keyframe : scale interpole la geometrie
     typographique (0.96 -> 1 = kerning et hinting differents), ce qui fait
     que le texte glisse pendant l'anim puis "snap" a sa position finale.
     On garde uniquement un translateY (le glissement n'affecte pas la
     taille du texte, donc pas de kerning qui bouge).
   - transform: translate3d(0,0,0) en position "ouverte" plutot que
     transform: none : garde le layer compositor promu au repos, sinon
     Chrome le detruit a la fin de l'anim et re-render le texte au pixel
     entier -> texte visible qui "recadre" de quelques px.
   - will-change: transform, opacity : indique explicitement au navigateur
     que ces proprietes vont changer et qu'il doit preparer un layer
     compositor (evite la promotion tardive qui donne le meme saut).
   - font-smoothing fixe a antialiased : sans cette regle, Chrome/Safari
     bascule automatiquement de "subpixel" (au repos) a "grayscale" (des
     qu'un transform est actif), donc le texte est litteralement rendu
     differemment entre le debut et la fin de l'anim -> saut visible. En
     forçant grayscale en permanence, le rendu est identique. */
.settings-overlay {
  display: flex;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  /* Retrait de backdrop-filter (herite par defaut de .modal-overlay) pour
     cette sous-modale : sur Chrome, l'application/retrait du blur en meme
     temps que le fondu d'opacite oblige une recomposition GPU pleine
     ecran a chaque frame -> hitch tres visible sur close puis re-open
     rapide. Sans blur, le fondu ne fait plus que de la composition
     d'opacite pure (chemin GPU rapide). Compensation visuelle : on
     assombrit legerement le fond (0.5 -> 0.58) pour garder la meme
     impression d'occultation. Meme technique que la modale TOS (voir
     .modal-overlay.tos-overlay plus haut). */
  background: rgba(16, 24, 40, 0.58);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  /* Promotion permanente en layer GPU : le compositeur garde le layer
     pret entre les ouvertures, donc pas de "cold start" au 2e clic. */
  will-change: opacity;
  transition: opacity .32s ease, visibility 0s linear .32s;
}
.settings-overlay.open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity .32s ease;
}
.settings-overlay .settings-modal-box {
  opacity: 0;
  transform: translate3d(0, 20px, 0);
  will-change: transform, opacity;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  transition:
    opacity .34s cubic-bezier(0.16, 1, 0.3, 1),
    transform .42s cubic-bezier(0.16, 1, 0.3, 1);
}
.settings-overlay.open .settings-modal-box {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* Scroll de la sous-modale calque sur celui de la modale TOS (voir
   .tos-overlay .tos-modal-box plus haut) : scrollbar native completement
   masquee (Firefox + Chrome/Safari), overscroll-behavior:contain pour ne
   pas propager au parent une fois arrive en haut ou en bas, et
   scroll-behavior:smooth pour un defilement doux. Meme specificite double-
   classe (.settings-overlay .settings-modal-box) que pour TOS, pour battre
   les regles .modal declarees plus bas. */
.settings-overlay .settings-modal-box {
  scrollbar-width: none !important;
  -ms-overflow-style: none !important;
  overscroll-behavior: contain;
  scroll-behavior: smooth;
}
.settings-overlay .settings-modal-box::-webkit-scrollbar,
.settings-overlay .settings-modal-box::-webkit-scrollbar-track,
.settings-overlay .settings-modal-box::-webkit-scrollbar-thumb {
  display: none !important;
  width: 0 !important;
  height: 0 !important;
  background: transparent !important;
}

/* ---------- Modal Security : layout deux colonnes ----------
   La modale Security accueille un menu deroulant a gauche (sections +
   items) et un panneau de contenu a droite. Elle est plus large que les
   autres modales pour donner de l'espace aux deux colonnes. Le padding
   habituel de .modal (30px partout) est retire pour que la sidebar puisse
   toucher les bords : le padding est reapplique sur security-content. */
/* Selecteur double-classe (.modal.security-modal-box) pour battre la
   regle .modal (padding: 30px, max-width: 460px) declaree PLUS BAS dans
   ce fichier avec la meme specificite : sans le bump, le max-width 460px
   remportait le cascade et la modale Security restait etroite sur
   desktop, comme un modal classique, alors qu'elle doit s'etaler jusqu'a
   900px pour laisser la place a la sidebar + la ligne Telegram. */
.modal.security-modal-box {
  max-width: 900px;
  padding: 0;
  /* Le rebond elastique deplace .modal-inner (donc la sidebar + le contenu)
     et laisse voir le fond de .modal.security-modal-box en dessous. Sans
     ce gradient, on verrait du blanc uniforme (fond paper) alors que la
     sidebar est grise -> effet moche a la limite. Le gradient reproduit
     la meme repartition 180px gris + reste paper, donc pendant le rebond
     la continuite visuelle est parfaite. */
  /* Deux couches de fond :
     1. le trait vertical (divider sidebar/contenu) a 180px, dessine DANS le
        fond de la modale (cadre fixe) -> il est donc revele avec le fond dans
        toute zone etiree par le rebond, du tout en haut au tout en bas, sans
        jamais scroller ni creer de debordement.
     2. le degrade gris (sidebar) / papier (contenu). */
  /* Fond du cadre reproduisant EXACTEMENT la sidebar : gris [0,179],
     trait du divider [179,180] (meme couleur/position que le border-right
     de la sidebar), puis papier [180,∞]. Ainsi, pendant le rebond, la zone
     revelee sous le contenu prolonge le divider sans le moindre decalage :
     le trait du fond (fixe) est pile dans l'axe du border-right (qui defile).
     Avant, le fond passait du gris au papier a 180px sans trait a 179-180,
     donc le bord gris/papier revele etait decale d'1px par rapport au
     border -> "barre pas alignee" pendant le rebond. */
  background:
    linear-gradient(to right, var(--bg) 0, var(--bg) 179px, var(--line) 179px, var(--line) 180px, var(--paper) 180px);
}
/* Sur mobile, la sidebar bascule au-dessus du contenu (media query 720px
   plus bas) et perd sa largeur fixe : on retire le gradient pour
   retrouver le fond papier uniforme classique. */
@media (max-width: 720px) {
  .modal.security-modal-box { background: var(--paper); }
}
/* Divider vertical (entre la sidebar "Compte/Security" et le contenu) porte
   par le CADRE FIXE de la modale, pas par la sidebar : il court donc de tout
   en haut a tout en bas et reste continu meme pendant le rebond elastique
   (le rebond translate .modal-inner, pas le cadre). Positionne pile a la
   largeur de la sidebar (180px), sur la separation du degrade de fond. */

/* Croix de la modale Security : Security a "padding: 0" (voir ci-dessus)
   parce que la sidebar doit toucher les bords. Du coup la croix sticky
   par defaut se collait au bord (0,0) au lieu du meme decalage visuel
   que TOS/Password (18px du bord). On la repositionne explicitement
   avec les memes offsets que les autres modales. */
.security-modal-box .modal-close {
  position: sticky;
  top: 18px;
  right: 18px;
  float: right;
  margin: 0 0 -30px 0;
  z-index: 2;
}
.security-shell {
  display: flex;
  min-height: 380px;
}
.security-sidebar {
  flex: 0 0 180px;
  /* Fond TRANSPARENT + aucune bordure : la colonne grise ET le trait du
     divider viennent UNIQUEMENT du fond (fixe) du cadre .security-modal-box
     (gris [0,179] + trait [179,180] + papier [180,∞]). Seul le TEXTE des
     onglets defile ; le fond, lui, ne bouge jamais. Avant, la sidebar avait
     son propre fond gris opaque + un border-right : ces couches defilaient/
     etaient transformees (translateY du scroll + rebond) et ne se calaient
     pas parfaitement sur le fond fixe derriere -> "marche"/encoche visible
     en bas pendant le rebond. En rendant la sidebar transparente, il n'y a
     plus qu'une seule couche de fond (fixe) : rien a desaligner. */
  background: transparent;
  padding: 22px 12px;
}
.security-sidebar-head {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 10px 12px;
  margin-bottom: 10px;
  color: var(--ink); font-family: var(--display); font-size: 14.5px; font-weight: 700;
  white-space: nowrap;
}
/* "Terms of service" est plus long que les autres titres de sidebar :
   on le garde sur une seule ligne dans la colonne de 180px en reduisant
   un peu sa taille. */
.tos-head-label { font-size: 13px; }
/* En-tete "Terms of service" centre (icone + libelle), uniquement dans la
   modale des conditions. */
#tosModal .security-sidebar-head {
  display: flex !important;
  flex-direction: row !important;
  justify-content: center !important;
  align-items: center !important;
  width: 100% !important;
  text-align: center !important;
}
.security-sidebar-icon {
  width: 22px; height: 22px; display: inline-flex; align-items: center;
  justify-content: center; color: var(--ink); flex-shrink: 0;
}
.security-sidebar-icon svg { width: 17px; height: 17px; }

/* Item de sidebar : pilule pleine, sans bordure gauche batarde.
   - Repos : fond transparent, texte "ink-soft" (gris moyen).
   - Survol : fond papier tres legerement souleve, texte "ink" (contraste
     doux) — feedback net mais discret.
   - Actif : pastille bleu-clair (var(--blue-soft)) + texte bleu, sans
     bordure gauche, la surface elle-meme signale la selection.
   - Transitions un chouia plus longues (.2s cubic-bezier) pour un rendu
     plus "moelleux" qu'un simple ease court : plus jamais le clic sec
     precedent. */
/* Conteneur des items avec un indicateur (barre verticale) qui glisse
   entre eux quand on change d'onglet actif. La barre est positionnee
   absolument et son top/height sont mis a jour en JS pour animer le
   deplacement. */
.security-sidebar-items {
  position: relative;
  /* Promotion GPU + lissage fige : pendant le rebond elastique, la
     modale-inner recoit un translateY. Sans layer dedie, le texte des
     items et la barre indicateur se re-rendaient en subpixel a chaque
     frame du rebond -> tremblement. Meme correctif que .message /
     .modal-inner ailleurs dans ce fichier. */
  transform: translateZ(0);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
.security-sidebar-item {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
/* Rail vertical gris continu le long de tous les items (piste sur
   laquelle glisse l'indicateur fonce de l'item actif). */
.security-sidebar-items::before {
  content: '';
  position: absolute;
  left: 6px; top: 8px; bottom: 8px;
  width: 2px; border-radius: 2px;
  background: var(--line);
  pointer-events: none;
}
.security-sidebar-indicator {
  position: absolute;
  left: 6px;
  width: 2px;
  border-radius: 2px;
  background: var(--ink);
  top: 0; height: 0;
  z-index: 1;
  transition:
    top .28s cubic-bezier(.4, 0, .2, 1),
    height .28s cubic-bezier(.4, 0, .2, 1),
    opacity .2s ease;
  opacity: 0;
  pointer-events: none;
}
.security-sidebar-indicator.ready { opacity: 1; }
.security-sidebar-item {
  position: relative;
  display: block; width: 100%; text-align: left;
  padding: 10px 12px 10px 18px;
  border: none; background: transparent; cursor: pointer;
  color: var(--ink-soft); font-family: var(--body);
  font-size: 13.5px; font-weight: 500;
  border-radius: 0;
  margin-top: 2px;
  transition: color .2s cubic-bezier(.22, .61, .36, 1);
}
.security-sidebar-item:hover,
.security-sidebar-item.active {
  color: var(--ink);
  font-weight: 600;
}
/* Pas de cadre de focus (contour blanc/bleu) sur ces boutons : la
   selection est deja signalee par la barre verticale + le texte fonce. */
.security-sidebar-item:focus,
.security-sidebar-item:focus-visible {
  outline: none;
  box-shadow: none;
}

.security-content {
  flex: 1;
  padding: 32px 34px;
  min-width: 0;
}
/* ---------- Modale TOS : taille fixe + sidebar figee ----------
   La fenetre TOS a une hauteur FIXE (elle ne retrecit plus selon le panneau).
   La box ne defile plus (overflow hidden) ; c'est la colonne de droite
   (.tos-scroll) qui defile, avec le meme moteur de scroll elastique. La
   sidebar (Terms of service + onglets) reste donc TOUJOURS visible : seul le
   texte de droite bouge. */
.tos-overlay .modal.security-modal-box {
  height: min(88vh, 660px);
  overflow: hidden;
}
.tos-overlay .security-shell {
  height: 100%;
  min-height: 0;
}
.tos-overlay .tos-scroll {
  flex: 1;
  min-width: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.tos-overlay .tos-scroll::-webkit-scrollbar { display: none; width: 0; height: 0; }
.security-panel { display: none; }
.security-panel.active { display: block; }

/* Transition entre la vue "liste des comptes" et la vue "formulaire
   Relier Telegram" dans le panneau Authentificator.
   - La hauteur du panneau s'anime (elargissement/retrecissement).
   - Chaque vue apparait/disparait EN PLACE via opacity uniquement
     (aucun mouvement de translation : ni horizontal, ni vertical).
     Le texte ne "bouge" jamais — il apparait/disparait, c'est tout. */
/* (Anciennes regles de swap de vue Authentificator supprimees : plus
   utilisees depuis le passage a l'accordeon in-place. La transition
   opacity qui ciblait #securityAuthLink ecrasait la nouvelle transition
   grid-template-rows -> Telegram s'ouvrait instant sans anim.) */

/* Une ligne "compte externe" (Telegram, plus tard d'autres) dans le
   panneau Authentificator : icone couleur du service a gauche, bloc
   "nom + sous-texte (id)" ensuite, chip d'etat (Relié / Non relié)
   pousse a droite. flex-wrap: wrap : le chip retombe automatiquement
   sous le nom quand la carte est trop etroite pour tout tenir sur une
   ligne (mobile), plutot que d'ecraser le texte a l'ellipse. */
.security-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px 14px;
  padding: 14px 16px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  margin-top: 12px;
}
.security-row-left {
  display: flex; align-items: center; gap: 12px;
  min-width: 0;
  /* Pousse le chip de statut en bout de ligne (equivalent d'un
     justify-content: space-between sur .security-row, mais compatible
     avec flex-wrap pour l'ecran etroit). */
  flex: 1 1 auto;
}
.security-row-icon {
  width: 36px; height: 36px; border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center; flex-shrink: 0; color: #fff;
}
.security-row-icon-telegram { background: #29a9eb; }
/* La ligne "Code" n'a pas d'icone : le label commence directement au
   bord de la ligne, comme les entrees "Sessions actives". */
.security-row-left-code { padding-left: 4px; }
/* Puces sous le sous-titre "Code" quand un code est actif : reprend le
   style .session-chip (voir "Sessions actives"). */
.security-row-chips {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-top: 6px;
}
.security-row-icon svg { width: 18px; height: 18px; display: block; }
.security-row-icon-telegram svg { transform: translateX(-0.5px); }
.security-row-label { min-width: 0; }
.security-row-name {
  font-family: var(--display); font-weight: 700; font-size: 14.5px; color: var(--ink);
}
.security-row-sub {
  font-size: 12.5px; color: var(--ink-soft); margin-top: 2px;
  /* On autorise la casse pour un id numerique long : mieux vaut deux
     lignes visibles qu'une ellipse qui cache l'info. */
  word-break: break-all;
}
/* Bouton icone "suivant" (chevron) sur une security-row : meme taille
   compacte que le bouton de deconnexion d'une session, mais avec l'accent
   bleu (action non destructive) au survol. */
.security-row-next-btn {
  width: 28px; height: 28px;
  padding: 0;
  line-height: 0;
  transition: color .15s ease, border-color .15s ease, background .15s ease;
}
/* Swap d'icone entre etat replie (chevron >) et etat deplie (chevron v).
   Aucune animation sur le bouton lui-meme : c'est le panneau qui s'anime. */
.security-row-next-btn .icon-open { display: none; }
.security-row-group.expanded .security-row-next-btn .icon-closed { display: none; }
.security-row-group.expanded .security-row-next-btn .icon-open { display: block; }

/* Accordeon : conteneur qui contient la ligne + le bloc a deplier. */
.security-row-group {
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  margin-top: 12px;
  overflow: hidden;
  transition: border-color .2s ease;
}
.security-row-group.expanded { border-color: var(--blue); }
/* Une ligne enfant d'un groupe n'a plus son propre cadre, la ligne
   partage celui du groupe. */
.security-row-group > .security-row {
  border: none;
  border-radius: 0;
  margin: 0;
}
/* Bloc replie par defaut (height: 0, overflow hidden). Le JS anime la
   hauteur en fixant la valeur pixel. Meme courbe que les autres
   transitions Security. */
/* Technique CSS Grid pour animer une hauteur "auto" -> 0 : le conteneur
   utilise grid-template-rows qui transitionne fiablement de 0fr a 1fr.
   Aucun JS de mesure necessaire, animation identique dans les deux sens. */
.security-row-expand {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .4s cubic-bezier(.22, 1, .36, 1);
}
.security-row-group.expanded .security-row-expand {
  grid-template-rows: 1fr;
}
.security-row-expand-inner {
  min-height: 0;
  overflow: hidden;
  padding: 0 16px;
  border-top: 1px solid var(--line);
  /* Fade du contenu synchronise avec la fermeture : disparait un peu
     avant que la hauteur atteigne 0, plus jamais de texte visible en
     fin d'anim. Padding vertical anime aussi pour un rendu plus doux. */
  opacity: 0;
  padding-top: 0;
  padding-bottom: 0;
  transition:
    opacity .18s ease,
    padding-top .4s cubic-bezier(.22, 1, .36, 1),
    padding-bottom .4s cubic-bezier(.22, 1, .36, 1);
}
.security-row-group.expanded .security-row-expand-inner {
  opacity: 1;
  padding-top: 18px;
  padding-bottom: 16px;
}
.security-row-next-btn svg {
  width: 17px; height: 17px;
  display: block;
}
.security-row-next-btn:hover {
  color: var(--blue);
  border-color: var(--blue);
  background: var(--blue-soft);
}
.security-code-disable-btn {
  background: none; color: var(--rose); border: 1.5px solid var(--rose);
  margin-top: 8px;
}
.security-code-disable-btn:hover { background: var(--rose); color: #fff; }

/* Sessions actives : liste d'appareils connectes au compte. Meme cadre
   que .security-row mais layout simplifie (device+IP a gauche, bouton
   Deconnexion a droite). La ligne "current" est mise en evidence par
   un liseret bleu discret et un badge. */
.sessions-list { margin-top: 12px; }
.sessions-empty {
  padding: 14px 16px; color: var(--ink-soft); font-size: 13px;
  border: 1.5px dashed var(--line); border-radius: var(--radius);
}
.session-row {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 16px; border: 1.5px solid var(--line);
  border-radius: var(--radius); margin-top: 10px;
}
.session-row.current { border-color: #29a9eb; background: rgba(41,169,235,0.05); }
.session-info { flex: 1 1 auto; min-width: 0; }
.session-device {
  font-family: var(--display); font-weight: 700; font-size: 14.5px; color: var(--ink);
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.session-meta {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin-top: 8px;
}
.session-chip {
  display: inline-flex; align-items: center;
  font-size: 11.5px; font-weight: 500;
  padding: 3px 7px; border-radius: 5px;
  background: var(--bg); border: 1px solid var(--line);
  color: var(--ink-soft); white-space: nowrap;
}
.session-chip + .session-chip {
  background: rgba(41,169,235,0.10);
  border-color: rgba(41,169,235,0.35);
  color: #1e6f9c;
}
/* Sur les thèmes sombres (Gris / Night), le bleu foncé #1e6f9c de la puce
   date/heure devient illisible sur le fond sombre. On repasse à un bleu
   clair très lisible, aussi net que sur le thème Classique. */
html[data-theme="night"] .session-chip + .session-chip,
html[data-theme="gris"] .session-chip + .session-chip {
  color: #8fd0f7;
  border-color: rgba(41,169,235,0.45);
}
/* Bouton "Deconnexion" d'une session : simple croix (X) sans cadre ni
   fond, couleur neutre. Devient rouge au survol. */
.session-close-btn {
  flex-shrink: 0;
  width: 26px; height: 26px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; border: none; background: none; cursor: pointer;
  color: var(--ink-soft);
  transition: color .15s ease;
}
.session-close-btn svg { width: 16px; height: 16px; display: block; }
.session-close-btn:hover { color: var(--rose); }
.session-close-btn:disabled,
.session-close-btn.cooldown { opacity: 0.6; cursor: default; }

/* Ligne "Se déconnecter de tous les appareils" en bas du panneau :
   meme layout qu'une session-row, mais sans cadre — juste le texte a
   gauche et le bouton icone deconnexion a droite. */
.sessions-logout-all-row {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 16px; margin-top: 14px;
}
.sessions-logout-all-label {
  flex: 1 1 auto;
  font-family: var(--display); font-weight: 700; font-size: 14.5px;
  color: var(--ink);
}
/* Bouton "Se deconnecter de tous les appareils" : contour neutre (pas de
   liseret rouge), seule l'icone SVG passe au rouge au survol. */
.session-logout-all-icon-btn { width: 28px; height: 28px; }
.session-logout-all-icon-btn svg { width: 14px; height: 14px; }

/* ---------- Double authentification (2FA) ---------- */
/* Pastille d'etat a cote du titre de la ligne. */
.twofa-badge {
  display: inline-flex; align-items: center;
  margin-left: 8px; padding: 2px 8px;
  border-radius: 999px;
  font-family: var(--body); font-size: 10.5px; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase;
  vertical-align: middle;
}
.twofa-badge-off { background: var(--rose-dim, rgba(220,38,38,.12)); color: var(--rose); }
.twofa-badge-on  { background: rgba(22,163,74,.12); color: #16a34a; }

/* Bloc d'activation : QR a gauche, etapes a droite. */
.twofa-setup { display: flex; gap: 20px; align-items: flex-start; }
.twofa-qr {
  flex: 0 0 auto;
  width: 168px; height: 168px;
  display: flex; align-items: center; justify-content: center;
  padding: 10px;
  background: #fff;                      /* le QR doit rester lisible en thème sombre */
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: opacity .2s ease;
}
/* max-width/height plutot que 100% impose : combine au viewBox injecte cote
   serveur, le QR se met a l'echelle proprement sans jamais etre rogne. */
.twofa-qr svg {
  display: block;
  width: 100%; height: auto;
  max-width: 100%; max-height: 100%;
}
/* QR expire : grise, on ne peut plus le scanner utilement. */
.twofa-qr.expired { opacity: .25; }
.twofa-qr-loading { font-size: 12.5px; color: #6b7280; }
.twofa-steps { flex: 1 1 auto; min-width: 0; }
.twofa-step-title {
  margin: 0 0 4px;
  font-family: var(--display); font-weight: 700; font-size: 13.5px; color: var(--ink);
}
.twofa-step-text { margin: 0 0 16px; font-size: 12.5px; color: var(--ink-soft); }
.twofa-validate-row { display: flex; gap: 8px; align-items: center; margin-top: 8px; }
.twofa-validate-row input {
  flex: 1 1 auto; min-width: 0;
  padding: 10px 14px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper); color: var(--ink);
  font-family: var(--body); font-size: 15px; font-weight: 600;
  letter-spacing: .18em; text-align: center;
}
.twofa-validate-row input:focus { outline: none; border-color: var(--blue); }
.twofa-validate-row .btn { flex: 0 0 auto; width: auto; margin: 0; padding: 10px 18px; }
.twofa-expiry { margin: 10px 0 0; font-size: 12px; color: var(--ink-soft); }
/* ----- Codes de secours ----- */
.twofa-codes {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
  margin: 12px 0 14px;
}
.twofa-code {
  padding: 11px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 14px; font-weight: 600; letter-spacing: .06em;
  color: var(--ink); text-align: center;
  user-select: all;                 /* un clic selectionne le code entier */
}
.twofa-copy-btn { width: auto; margin: 0 0 14px; padding: 9px 16px; }
.twofa-confirm {
  display: flex; align-items: flex-start; gap: 9px;
  margin: 0 0 14px;
  font-size: 13px; color: var(--ink-soft); cursor: pointer;
}
.twofa-confirm input { margin-top: 1px; accent-color: var(--blue); cursor: pointer; }
/* Bouton "Terminer" grise tant que la case n'est pas cochee. */
#twofaDoneBtn:disabled { opacity: .5; cursor: not-allowed; }
@media (max-width: 620px) {
  .twofa-codes { grid-template-columns: 1fr; }
}

/* Lien "Générer un nouveau" affiche a l'expiration du QR. */
.link-like {
  background: none; border: none; padding: 0;
  font-family: var(--body); font-size: 12px; font-weight: 600;
  color: var(--blue); cursor: pointer; text-decoration: underline;
}
@media (max-width: 620px) {
  .twofa-setup { flex-direction: column; }
  .twofa-qr { width: 100%; max-width: 200px; height: auto; aspect-ratio: 1; margin: 0 auto; }
}

/* ---------- Appareil de confiance ---------- */
/* Bouton bouclier propose sur chaque session tant qu'aucun appareil de
   confiance n'est defini. */
.icon-btn.trust-btn { width: 28px; height: 28px; margin-right: 6px; }
.icon-btn.trust-btn svg { width: 15px; height: 15px; }
.icon-btn.trust-btn:hover { color: #16a34a; border-color: #16a34a; background: rgba(22,163,74,.10); }
/* Ligne de l'appareil de confiance : liseret vert discret. */
.trusted-row { border: 1px solid rgba(22,163,74,.35) !important; background: rgba(22,163,74,.06); }
.trusted-tag {
  display: inline-flex; align-items: center;
  margin-left: 8px; padding: 2px 8px;
  border-radius: 999px;
  background: rgba(22,163,74,.14); color: #16a34a;
  font-family: var(--body); font-size: 10.5px; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase;
}
.trusted-hint { display: block; margin-top: 6px; font-size: 12.5px; opacity: .8; }

/* ---------- Activites recentes (journal du compte) ---------- */
.activity-table {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
  overflow: hidden;
}
/* En-tete de colonnes (ACTION / APPAREIL / DATE). */
.activity-thead,
.activity-row {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) auto;
  gap: 14px;
  align-items: center;
  padding: 12px 16px;
}
.activity-thead {
  font-size: 10.5px; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-faint, var(--ink-soft));
  border-bottom: 1px solid var(--line);
}
.activity-thead span:last-child { text-align: right; }
.activity-row + .activity-row { border-top: 1px solid var(--line); }
.activity-action { display: flex; align-items: center; gap: 12px; min-width: 0; }
.activity-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex-shrink: 0;
  border-radius: 50%;
  background: var(--blue-soft); color: var(--blue);
}
.activity-icon svg { width: 15px; height: 15px; }
/* Teintes par type d'evenement. */
.activity-icon-green { background: rgba(22,163,74,.12); color: #16a34a; }
.activity-icon-red   { background: var(--rose-dim, rgba(220,38,38,.12)); color: var(--rose); }
.activity-icon-grey  { background: var(--paper); color: var(--ink-soft); }
.activity-labels { display: flex; flex-direction: column; min-width: 0; }
.activity-title {
  font-family: var(--display); font-weight: 700; font-size: 13.5px; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.activity-sub {
  font-size: 12px; color: var(--ink-soft); margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.activity-loc { min-width: 0; }
.activity-ip {
  font-size: 12.5px; color: var(--ink-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block;
}
.activity-date {
  display: flex; flex-direction: column; align-items: flex-end;
  font-size: 12px; color: var(--ink-soft); white-space: nowrap;
}
@media (max-width: 620px) {
  /* Colonne "Appareil" masquee : l'info reste sous le titre de l'action. */
  .activity-thead span:nth-child(2), .activity-loc { display: none; }
  .activity-thead, .activity-row { grid-template-columns: minmax(0, 1fr) auto; }
}
.session-logout-all-icon-btn:hover {
  color: var(--rose);
  border-color: var(--line);
  background: var(--paper);
}
.session-logout-all-icon-btn:disabled { opacity: 0.6; cursor: default; }

/* Animation de fermeture d'une session : reduction douce de la hauteur,
   opacite, marges et bordures — meme courbe que les transitions du
   panneau Authentificator (cubic-bezier(.22,1,.36,1)). */
.session-row.collapsing {
  overflow: hidden;
  transition:
    height .4s cubic-bezier(.22, 1, .36, 1),
    opacity .3s cubic-bezier(.22, 1, .36, 1),
    margin-top .4s cubic-bezier(.22, 1, .36, 1),
    padding-top .4s cubic-bezier(.22, 1, .36, 1),
    padding-bottom .4s cubic-bezier(.22, 1, .36, 1),
    border-width .4s cubic-bezier(.22, 1, .36, 1);
}

/* Modale "Trop de sessions" affichee sur la page login quand le compte
   est deja a MAX_SESSIONS. Reprend le layout des .session-row mais
   chaque ligne est cliquable pour selectionner la session a fermer. */
.tms-list { margin-bottom: 18px; }
.session-choice-row {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px; border: 1.5px solid var(--line);
  border-radius: var(--radius); margin-top: 10px;
  cursor: pointer; transition: border-color .15s ease, background .15s ease;
}
.session-choice-row:hover { border-color: #29a9eb; }
.session-choice-row.selected {
  border-color: #29a9eb; background: rgba(41,169,235,0.08);
}
.session-choice-radio {
  width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid var(--line); flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
}
.session-choice-row.selected .session-choice-radio {
  border-color: #29a9eb;
}
.session-choice-row.selected .session-choice-radio::after {
  content: ''; width: 8px; height: 8px; border-radius: 50%; background: #29a9eb;
}

/* Bascule vers l'empilement (sidebar sur la ligne du haut, contenu
   dessous) plus tot qu'a l'ancien 620px : a 720px viewport, la modale
   est encore trop etroite pour un layout horizontal sans que le
   sous-titre "Comptes externes reliés à votre compte Scandex." ne se
   casse en deux lignes. Mieux vaut la version empilee sur la plage
   620-720 : le contenu recupere toute la largeur de la modale et le
   texte tient sur une ligne. Fond transparent sur la sidebar en mode
   empile : le fond papier de la modale suffit, sans creer une bande
   grise-bleutee qui divise visuellement la carte en deux. */
@media (max-width: 720px) {
  .security-shell { flex-direction: column; min-height: 0; }
    .security-sidebar {
      flex: none; width: auto;
      background: transparent;
      border-right: none; border-bottom: 1px solid var(--line);
      border-radius: var(--radius-lg) var(--radius-lg) 0 0;
      padding: 18px 16px;
    }
    .security-sidebar-head { padding: 4px 10px 10px; }
    .security-sidebar-item { margin-top: 4px; }
    .security-content { padding: 22px; }
}

/* ---------- Menu deroulant Parametres -----
   Reutilise strictement les styles du menu @pseudo (.user-pill-wrap /
   .user-pill-menu / .user-pill-menu-item) ; on ne pose ici que le rappel
   visuel du bouton engrenage quand le menu est ouvert : la regle
   .user-pill-wrap.open .user-pill ne matche que pour le pseudo, donc
   l'equivalent pour l'icone parametres est declare explicitement. */
.user-pill-wrap.open .icon-btn {
  border-color: var(--blue);
  background: var(--blue-soft);
  color: var(--blue);
}

/* Le menu Parametres reutilise .user-pill-menu-item pour l'affichage,
   mais chaque item ici est un <button>, pas un <a> : par defaut le
   navigateur ajoute un fond gris et une bordure. On repart de zero. */
button.user-pill-menu-item {
  width: 100%;
  border: none;
  background: none;
  cursor: pointer;
  text-align: left;
}

.modal {
  width: 100%; max-width: 460px; max-height: 88vh; overflow-y: auto; background: var(--paper);
  border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  padding: 30px; position: relative;
}

.modal h2 { font-family: var(--display); font-size: 21px; font-weight: 700; margin: 0 0 4px; }
.modal .modal-sub { color: var(--ink-soft); font-size: 13px; margin: 0 0 22px; }
/* Texte des sections TOS (multi-lignes) : interligne plus aere. */
.tos-text { line-height: 1.7; font-size: 13.5px; margin: 0; }
/* Paragraphes de contenu TOS : couleur douce + espace vertical entre eux
   (sinon .tos-text force margin:0 et les paragraphes se collent). */
.tos-para { color: var(--ink-soft); margin: 0 0 14px; }
.tos-para:last-child { margin-bottom: 0; }
/* Ligne "Derniere mise a jour : ..." en haut du panneau Conditions. */
.tos-updated {
  color: var(--ink-faint, var(--ink-soft));
  font-size: 12.5px;
  margin: 0 0 16px;
}
.tos-updated span { font-weight: 600; color: var(--ink-soft); }
/* Sous-titres de section a l'interieur d'un panneau TOS (Objet du service,
   Utilisation du service...). */
.tos-subtitle {
  font-family: var(--display);
  font-weight: 700;
  font-size: 15px;
  color: var(--ink);
  margin: 22px 0 8px;
}
/* Cadre "Donnees de compte" : un seul cadre propre, lignes separees par de
   fins traits, avec une petite icone devant chaque ligne (style epure). */
.tos-databox {
  margin: 12px 0 16px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--bg);
  overflow: hidden;
}
.tos-databox-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  font-size: 14px;
  line-height: 1.4;
  color: var(--ink);
}
.tos-databox-item + .tos-databox-item {
  border-top: 1px solid var(--line);
}
.tos-databox-item svg {
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  stroke: var(--ink-soft);
}

.modal-close {
  position: sticky; top: 0; float: right; margin: 0 0 -30px 0;
  width: 30px; height: 30px; display: flex; z-index: 2;
  align-items: center; justify-content: center; background: var(--bg); border: none;
  border-radius: 50%; font-size: 20px; line-height: 1; color: var(--ink-soft); cursor: pointer;
}

.modal-close:hover { color: var(--rose); }

.modal .field { margin-bottom: 14px; }

/* ---------- Modale Compte : selecteur d'avatar (cadre carre + fleches) ---------- */
.compte-avatar-picker {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  margin: 22px 0 4px;
}
.compte-avatar-frame {
  width: 148px;
  height: 148px;
  border-radius: var(--radius);
  border: 1.5px solid var(--line);
  background: var(--bg);
  overflow: hidden;
  flex-shrink: 0;
  box-shadow: var(--shadow-sm);
}
.compte-avatar-frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.compte-arrow {
  width: 38px; height: 38px;
  flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1.5px solid var(--line);
  background: var(--paper);
  border-radius: 50%;
  color: var(--ink-soft);
  cursor: pointer;
  transition: color .15s ease, border-color .15s ease, background .15s ease;
}
.compte-arrow svg { width: 18px; height: 18px; }
.compte-arrow:hover {
  color: var(--blue);
  border-color: var(--blue);
  background: var(--blue-soft);
}
.compte-arrow:disabled { opacity: 0.5; cursor: default; }
.compte-dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 16px;
}
.compte-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--line);
  transition: background .15s ease, transform .15s ease;
}
.compte-dot.active {
  background: var(--blue);
  transform: scale(1.25);
}

/* ---------- Modale Compte : panneau Préférences (choix du thème) ----------
   Petits carres de couleur (facon selecteur d'accent macOS) : chaque carre
   montre la couleur dominante du thème, le carre selectionne a un liseret
   bleu + une pastille "coche" en coin. */
.theme-options {
  display: flex;
  gap: 14px;
  margin-top: 2px;
  flex-wrap: wrap;
  /* Composite le bloc des carres en un seul layer GPU stable : evite le
     micro-tremblement (jitter subpixel) des carres en fin d'agrandissement
     de l'accordeon (quand la hauteur en grid se fige). */
  transform: translateZ(0);
  backface-visibility: hidden;
}
.theme-option {
  position: relative;
  display: inline-flex;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  border-radius: 14px;
}
.theme-swatch {
  width: 54px; height: 54px;
  border-radius: 12px;
  display: block;
  border: 2px solid var(--line);
  box-shadow: var(--shadow-sm);
  /* translateZ(0) + backface-visibility : promeut le carre en layer GPU
     stable pour qu'il ne "tremble" pas (jitter subpixel) pendant
     l'animation d'ouverture de l'accordeon (hauteur qui change). */
  transform: translateZ(0);
  backface-visibility: hidden;
  transition: border-color .15s ease, transform .18s cubic-bezier(.34,1.56,.64,1);
}
.theme-option:hover .theme-swatch { border-color: var(--ink-faint); }
.theme-option.selected .theme-swatch {
  border-color: var(--blue);
  transform: scale(1.04) translateZ(0);
}
/* Couleur de chaque carre (couleur de fond dominante du thème). */
.theme-swatch-classique { background: #ffffff; }
.theme-swatch-gris { background: #3a3d43; border-color: #4a4e56; }
.theme-swatch-night { background: #16181d; border-color: #2b303b; }

/* Pastille "coche" en haut a droite, visible seulement sur le selectionne. */
.theme-badge {
  position: absolute;
  top: -5px; right: -5px;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--blue);
  border: 2px solid var(--paper);
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0;
  transform: scale(.5) translateZ(0);
  backface-visibility: hidden;
  transition: opacity .15s ease, transform .15s cubic-bezier(.34,1.56,.64,1);
}
.theme-badge svg { width: 12px; height: 12px; display: block; }
.theme-option.selected .theme-badge { opacity: 1; transform: scale(1) translateZ(0); }

/* Sous-titre de section dans Préférences (ex: "Langue"). */
.pref-subhead {
  font-family: var(--display);
  font-weight: 700;
  font-size: 14.5px;
  color: var(--ink);
  margin: 28px 0 0;
}

/* Champ de recherche de langue. */
.lang-search {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
  padding: 9px 12px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
}
.lang-search svg { width: 16px; height: 16px; color: var(--ink-soft); flex-shrink: 0; }
.lang-search input {
  flex: 1 1 auto;
  border: none; background: none; outline: none;
  font-family: var(--body); font-size: 13.5px; color: var(--ink);
}
.lang-search input::placeholder { color: var(--ink-soft); }

/* Liste des langues. */
.lang-list { margin-top: 10px; }
.lang-option {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 11px 14px;
  margin-top: 8px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  cursor: pointer;
  text-align: left;
  transition: border-color .15s ease, background .15s ease;
}
.lang-option:hover { border-color: var(--blue); }
.lang-option.selected { border-color: var(--blue); background: var(--blue-soft); }
.lang-flag { font-size: 20px; line-height: 1; flex-shrink: 0; }
.lang-name {
  flex: 1 1 auto;
  font-family: var(--display); font-weight: 600; font-size: 14px; color: var(--ink);
}

@media (max-width: 720px) {
  .feature-row { grid-template-columns: 1fr; }
    .top-nav { padding: 16px 20px; }
    
    
    
    .hero { padding: 40px 18px 28px; }
    .btn-hero { padding: 0 16px; font-size: 13.5px; }
    
    
    .promo-banner { padding: 10px 20px; }
}

/* ---------- Taille téléphone : bandeau promo masqué + menu hamburger ----------
   Sous ce seuil, on ne montre plus le bandeau promo ni les boutons
   Connexion / Créer un compte : ces derniers sont remplacés par un bouton
   hamburger qui ouvre le menu plein écran flouté. S'applique à l'accueil,
   /login et /register (même en-tête partout). */
@media (max-width: 560px) {
  .site-header, .promo-banner { display: none; }
    #navLoggedOut .nav-link { display: none; }
    #navLoggedOut .nav-burger { display: inline-flex; }

    /* Barre reduite : plus que "logo + burger". Les liens texte et le bouton
       d'action passent dans le panneau deroulant (#mobileMenu). */
    .nav-pill { gap: 6px; padding: 5px 6px; }
    .nav-logo { display: inline-flex; }
    .nav-pill .nav-pill-item { display: none; }
    .nav-pill .nav-cta { display: none; }
    /* Thème + langue masques aussi : la barre se limite a "logo + burger". */
    .nav-pill .nav-theme-btn,
    .nav-pill .nav-lang-wrap { display: none; }
    .nav-pill .nav-actions { margin-left: 0; }
    /* Burger sans cadre : il vit deja dans la pilule. */
    .nav-burger { width: 34px; height: 34px; border-color: transparent; background: none; }

    /* ----- Menu mobile : panneau deroulant sous la barre -----
       (au lieu de l'ancien plein ecran floute) */
    .mobile-menu {
      inset: auto 0 auto 0;
      top: calc(var(--nav-h) + 8px);
      padding: 0 16px;
      background: none;
      -webkit-backdrop-filter: none;
      backdrop-filter: none;
    }
    .mobile-menu-inner {
      margin: 0;
      width: 100%;
      max-width: none;
      gap: 4px;
      padding: 8px;
      background: var(--paper);
      border: 1px solid var(--line);
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-lg);
    }
    /* Items en liste alignee a gauche, avec icone. */
    .mobile-menu-link,
    .mobile-menu-link:first-child,
    .mobile-menu-link:last-child {
      justify-content: flex-start;
      gap: 12px;
      padding: 13px 14px;
      border: none;
      border-radius: var(--radius);
      background: none;
      box-shadow: none;
      color: var(--ink-soft);
      font-family: var(--body);
      font-size: 15px;
      text-align: left;
    }
    .mobile-menu-link svg { width: 18px; height: 18px; flex-shrink: 0; }
    .mobile-menu-link:first-child:hover,
    .mobile-menu-link:last-child:hover,
    .mobile-menu-link:hover {
      background: var(--bg);
      color: var(--ink);
      border-color: transparent;
    }
    /* Page courante mise en avant, comme sur la barre desktop. */
    .mobile-menu-link.active { background: var(--bg); color: var(--ink); }
}
