/**
 * Module Abonnements — parcours de souscription.
 *
 * Dimensionné pour la tablette de l'accueil autant que pour le mobile :
 * cibles tactiles d'au moins 48 px, texte d'au moins 16 px dans les champs
 * (en dessous, iOS zoome à chaque focus et déforme la mise en page).
 */

.ge-abo {
  --ge-abo-vert: #244022;
  --ge-abo-creme: #f3e6da;
  --ge-abo-orange: #ff874d;
  --ge-abo-encre: #090909;
  --ge-abo-gris: #6f6a63;
  --ge-abo-bord: #e3dbd2;
  --ge-abo-danger: #9b1c1c;
  --ge-abo-succes: #1a6b3c;
  --ge-abo-sauge: #6f9d6a;
  --ge-abo-rayon: 8px;

  /* Une seule courbe pour tout le module. Elle démarre franc et finit
     amorti : le mouvement se lit comme une chose qu'on pose, pas comme une
     chose qui glisse. Nommée ici plutôt que recopiée à chaque transition —
     elle était déjà écrite en clair une quarantaine de fois. */
  --ge-abo-ease: cubic-bezier(.22, 1, .36, 1);

  /* Plus petite cible que le doigt trouve sans viser. La borne de l'accueil
     se pilote debout, souvent d'une main : rien de cliquable en dessous. */
  --ge-abo-tap: 44px;

  /* 900px pour que trois cartes tiennent à l'aise en grand écran. Les blocs
     de prose ont leur propre plafond plus bas : au-delà d'environ 68
     caractères, l'œil perd la ligne en revenant à la marge. */
  max-width: 900px;
  margin: 0 auto;

  /* Le bas dégage la barre de total, qui est fixée à l'écran sur mobile et
     recouvrirait sinon les boutons d'action de l'étape 1. 140 px : la barre
     fait 76 px, plus l'encoche du bas et une marge de lecture. */
  padding: 8px 16px 140px;
  color: var(--ge-abo-encre);
  font-size: 16px;
  line-height: 1.6;
}

/* La racine est incluse : sans elle, ses 900 px de plafond s'entendaient
   hors padding, et le bloc occupait 948 px à l'écran. */
.ge-abo,
.ge-abo *,
.ge-abo *::before,
.ge-abo *::after { box-sizing: border-box; }

/* Filet de sécurité contre le débordement horizontal. Ciblé, et non posé sur
   `*` : un min-width:0 universel laisserait les boutons se comprimer sous la
   largeur de leur propre libellé. */
.ge-abo img,
.ge-abo svg,
.ge-abo canvas,
.ge-abo table,
.ge-abo pre { max-width: 100%; }

/* Les enfants de grille, eux, doivent pouvoir passer sous leur taille
   min-content : c'est ce qui fait revenir le texte à la ligne au lieu
   d'élargir la piste. */
.ge-abo-activites > *,
.ge-abo-formules > *,
.ge-abo-variantes > * { min-width: 0; }

/* Longueur de ligne des textes courants — indépendante de la largeur des
   grilles, qui, elles, profitent des 900 px. */
.ge-abo-note,
.ge-abo-mentions,
.ge-abo-aide,
.ge-abo-titre { max-width: 68ch; }

/* Lu par les technologies d'assistance, invisible à l'écran. Ni display:none
   ni visibility:hidden — les deux retirent aussi le texte de la vocalisation. */
.ge-abo-lecture-seule {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* L'attribut hidden doit l'emporter sur nos propres `display`.
   Le navigateur ne le pose que par `[hidden] { display: none }` dans sa
   feuille par défaut : la moindre règle d'auteur qui fixe un display la
   bat, et le bloc « caché » reste affiché — vide. C'est ce qui faisait
   apparaître un rectangle vert sur vert sous « Bienvenue dans le crew »,
   et une barre de total vide dès l'étape 1. */
.ge-abo [hidden] { display: none !important; }

/* ── Barre de progression ─────────────────────────── */

.ge-abo-progression {
  position: sticky;
  top: 0;
  z-index: 5;
  height: 5px;
  margin-bottom: 20px;
  border-radius: 999px;
  background: var(--ge-abo-bord);
  overflow: hidden;
}

/* Unie, pas dégradée : un dégradé multicolore jure avec la charte.
   La largeur vient d'une propriété personnalisée plutôt que d'un style en
   ligne : le script pose un pourcentage, la feuille garde la main sur la
   façon de le rendre. Repli à 0 si le script n'a pas encore parlé. */
.ge-abo-progression-barre {
  height: 100%;
  width: var(--ge-abo-p, 0%);
  border-radius: 999px;
  background: var(--ge-abo-orange);
  transition: width .8s var(--ge-abo-ease);
}

/* ── Fil d'étapes ─────────────────────────────────── */

.ge-abo-fil {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  list-style: none;
  margin: 0 0 28px;
  padding: 0;
}

.ge-abo-fil-item {
  display: flex;
  align-items: center;
  gap: 7px;
  min-height: var(--ge-abo-tap);
  padding: 5px 12px;
  border-radius: 999px;
  background: #fff;
  border: 1.5px solid var(--ge-abo-bord);
  font-size: 13px;
  font-weight: 600;
  color: var(--ge-abo-gris);
  transition: background .35s var(--ge-abo-ease), border-color .35s var(--ge-abo-ease);
}

.ge-abo-fil-item.est-actif {
  border-color: var(--ge-abo-orange);
  background: rgba(255, 135, 77, .12);
  color: var(--ge-abo-vert);
}

/* L'étape courante se lit aussi à la graisse de son libellé : la couleur
   ne porte jamais seule une information. */
.ge-abo-fil-item.est-actif .ge-abo-fil-label { font-weight: 700; }

.ge-abo-fil-item.est-fait {
  border-color: var(--ge-abo-vert);
  background: var(--ge-abo-vert);
  color: var(--ge-abo-creme);
}

/* Revenir en arrière doit être possible, et se voir. */
.ge-abo-fil-item.est-fait { cursor: pointer; }
.ge-abo-fil-item.est-fait:hover { opacity: .85; }

.ge-abo-fil-item.est-fait:focus-visible {
  outline: 3px solid var(--ge-abo-orange);
  outline-offset: 2px;
}

.ge-abo-fil-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--ge-abo-creme);
  font-size: 12px;
  font-weight: 700;
  transition: background .35s var(--ge-abo-ease);
}

/* Le numéro cède la place à une coche : l'étape n'est plus à faire. */
.ge-abo-fil-item.est-fait .ge-abo-fil-num {
  background: var(--ge-abo-creme);
  color: transparent;
  position: relative;
}

/* Centrée par pourcentages plutôt qu'au pixel : la coche suit désormais la
   taille de la pastille, sans quatre valeurs à recalculer à chaque fois.
   -55 % en hauteur et non -50 : le coude de la coche n'est pas son centre
   géométrique, et une coche centrée mathématiquement paraît trop basse. */
.ge-abo-fil-item.est-fait .ge-abo-fil-num::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 6px;
  height: 10px;
  border: solid var(--ge-abo-vert);
  border-width: 0 2px 2px 0;
  transform: translate(-50%, -55%) rotate(45deg);
}

/* ── Titres et notes ──────────────────────────────── */

.ge-abo-titre {
  margin: 0 0 6px;
  font-size: 24px;
  line-height: 1.25;
  color: var(--ge-abo-vert);
}

.ge-abo-etape h3 {
  margin: 28px 0 6px;
  font-size: 17px;
  color: var(--ge-abo-vert);
}

.ge-abo-note {
  margin: 0 0 18px;
  font-size: 14px;
  color: var(--ge-abo-gris);
}

.ge-abo-alerte {
  margin: 0 0 18px;
  padding: 12px 16px;
  border-left: 4px solid var(--ge-abo-danger);
  background: rgba(155, 28, 28, .07);
  border-radius: 0 var(--ge-abo-rayon) var(--ge-abo-rayon) 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--ge-abo-danger);
}

.ge-abo-etat {
  margin: 0 0 16px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ge-abo-succes);
}

/* ── Choix de l'activité ──────────────────────────── */

.ge-abo-activites {
  display: grid;
  /* 220px et non 240 : à 760px de large moins deux gouttières, trois pistes
     de 240 ne laissaient que 190px de texte utile par carte. */
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
  margin-bottom: 24px;
}

/* Carte cliquable en entier : l'étiquette d'un bouton radio natif, masqué
   mais bien là — c'est lui qui porte l'état coché pour le clavier et les
   lecteurs d'écran, sans un seul attribut ARIA.

   Toutes les règles de la carte restent préfixées par .ge-abo : le reset du
   thème pose des règles de survol à la spécificité (0,1,1), qui battent une
   simple classe (0,1,0). Sans le préfixe, la carte survolée passait au vert
   plein du thème en gardant son texte vert foncé, et devenait illisible. */
.ge-abo .ge-abo-activite {
  display: block;
  /* Repère du bouton radio, posé en absolu sur toute la carte. */
  position: relative;
  /* Un élément de grille vaut au moins sa taille min-content : sans ce 0, la
     piste s'élargit pour contenir la plus longue ligne insécable au lieu de
     la faire revenir à la ligne. */
  min-width: 0;
  width: 100%;
  max-width: 100%;
  min-height: 190px;
  padding: 26px 24px;
  border: 2px solid var(--ge-abo-bord);
  border-radius: 12px;
  background: #fff;
  font-family: inherit;
  font-size: inherit;
  text-align: left;
  /* Le bouton hérite du gabarit d'origine des contrôles de formulaire dans
     plusieurs moteurs : on le ramène explicitement au texte courant. */
  white-space: normal;
  overflow: hidden;
  cursor: pointer;
  transition: border-color .16s ease, transform .16s ease, box-shadow .16s ease;
}

/* Conteneur flex réel — voir le commentaire du gabarit. */
.ge-abo-activite-corps {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  /* Le contenu se cale en haut : trois cartes de hauteur identique dont les
     titres ne sont pas sur la même ligne se lisent mal. */
  justify-content: flex-start;
  gap: 10px;
  width: 100%;
  min-width: 0;
}

/* Les trois états actifs partagent la même inversion : fond vert forêt,
   texte et pictogramme en crème. Écrire la couleur du texte en même temps
   que le fond, et jamais l'un sans l'autre, est ce qui garantit que le
   contraste tient — c'est l'oubli inverse qui rendait la carte invisible. */
.ge-abo .ge-abo-activite:hover,
.ge-abo .ge-abo-activite:has(input:focus-visible),
.ge-abo .ge-abo-activite.est-choisie {
  background: var(--ge-abo-vert);
  border-color: var(--ge-abo-vert);
  /* Contraste crème sur vert forêt : 9,36:1 — au-delà du AAA (7:1). */
  color: var(--ge-abo-creme);
}

.ge-abo .ge-abo-activite:hover .ge-abo-activite-nom,
.ge-abo .ge-abo-activite:has(input:focus-visible) .ge-abo-activite-nom,
.ge-abo .ge-abo-activite.est-choisie .ge-abo-activite-nom,
.ge-abo .ge-abo-activite:hover .ge-abo-activite-desc,
.ge-abo .ge-abo-activite:has(input:focus-visible) .ge-abo-activite-desc,
.ge-abo .ge-abo-activite.est-choisie .ge-abo-activite-desc {
  color: var(--ge-abo-creme);
}

/* La pastille s'inverse aussi : crème sur vert, le pictogramme reprend la
   couleur du fond de la carte. */
.ge-abo .ge-abo-activite:hover .ge-abo-activite-icone,
.ge-abo .ge-abo-activite:has(input:focus-visible) .ge-abo-activite-icone,
.ge-abo .ge-abo-activite.est-choisie .ge-abo-activite-icone {
  background: var(--ge-abo-creme);
  color: var(--ge-abo-vert);
}

.ge-abo .ge-abo-activite:hover {
  transform: translateY(-3px);
  box-shadow: 0 10px 26px rgba(36, 64, 34, .14);
}

/* Le radio reste dans le flux d'accessibilité, hors de vue. Ni display:none
   ni visibility:hidden : les deux le retireraient de la navigation clavier,
   et le groupe ne se parcourrait plus aux flèches. */
.ge-abo .ge-abo-activite input {
  position: absolute;
  inset: 0;
  width: 0;
  height: 0;
  opacity: 0;
  margin: 0;
  pointer-events: none;
}

/* Une étiquette ne reçoit jamais le focus : c'est son radio qui le prend,
   et :has le reporte sur la carte, seule chose visible à l'écran. */
.ge-abo .ge-abo-activite:has(input:focus-visible) {
  outline: 3px solid var(--ge-abo-orange);
  outline-offset: 3px;
}

/* La sélection reste lisible sans survol — sur tablette il n'y a pas de
   pointeur, et l'état choisi doit se voir au retour en arrière.
   Le liseré intérieur la distingue du simple survol, qui, lui, ne pose que
   le fond : deux cartes vertes à l'écran, une survolée et une choisie, ne
   diraient sinon pas la même chose. */
.ge-abo .ge-abo-activite.est-choisie {
  box-shadow: 0 0 0 2px var(--ge-abo-vert) inset, 0 6px 18px rgba(36, 64, 34, .18);
  transform: translateY(-2px);
}


.ge-abo-activite-icone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: flex-start;
  flex: 0 0 auto;
  width: 54px;
  height: 54px;
  border-radius: 50%;
  background: var(--ge-abo-creme);
  color: var(--ge-abo-vert);
}

/* Icône fournie par les réglages : normalisée à la taille de la pastille,
   quelle que soit la dimension du fichier téléversé. */
.ge-abo .ge-abo-activite-icone img,
.ge-abo .ge-abo-activite-icone svg {
  display: block;
  /* Hauteur imposée, largeur libre : forcer les deux écrasait toute image
     qui n'était pas carrée. La pastille borne la largeur, object-fit
     garantit qu'on n'y rogne rien. */
  height: 30px;
  width: auto;
  max-width: 34px;
  object-fit: contain;
}

/* Les deux blocs de texte occupent toute la largeur de la carte et coupent
   les mots trop longs plutôt que de déborder sur la carte voisine. */
.ge-abo .ge-abo-activite-nom,
.ge-abo .ge-abo-activite-desc {
  display: block;
  align-self: stretch;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  /* overflow-wrap seul : il ne coupe un mot que si celui-ci ne tient pas à
     lui seul sur une ligne. hyphens:auto coupait « ESCALADE » en plein
     milieu alors que le mot entier tenait à la ligne suivante. */
  overflow-wrap: break-word;
  word-break: normal;
  hyphens: none;
  -webkit-hyphens: none;
}

.ge-abo .ge-abo-activite-nom {
  /* La taille cède avant le mot : plutôt un titre légèrement plus petit
     qu'un mot coupé. Le plancher de 16px reste confortable. */
  font-size: clamp(16px, 1.35vw + 12px, 19px);
  font-weight: 700;
  color: var(--ge-abo-vert);
  line-height: 1.25;
}

.ge-abo .ge-abo-activite-desc { font-size: 13px; line-height: 1.55; color: var(--ge-abo-gris); }

/* ── Bloc crew : deux questions d'usage, une phrase, les adhésions ── */

.ge-abo-crew { margin-bottom: 22px; }

.ge-abo-crew-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 14px;
}

.ge-abo-crew-tete h3 { margin: 0; font-size: 19px; color: var(--ge-abo-vert); }

/* Hauteur imposée, largeur libre : le fichier téléversé n'est pas forcément
   au ratio qu'on croit, et forcer les deux dimensions l'écraserait. */
.ge-abo-crew-logo {
  flex: 0 0 auto;
  height: 26px;
  width: auto;
  max-width: 110px;
  object-fit: contain;
}

.ge-abo-crew-questions {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
  margin-bottom: 16px;
}

@media (min-width: 620px) {
  .ge-abo-crew-questions { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Le fieldset porte le groupement natif des boutons radio : rien à
   déclarer en ARIA, et la légende reste lisible à l'écran. */
.ge-abo-crew-question {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

.ge-abo-crew-question legend {
  padding: 0;
  margin-bottom: 7px;
  font-size: 13px;
  font-weight: 700;
  color: var(--ge-abo-vert);
}

/* ── Boutons segmentés ── */

.ge-abo-segment {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.ge-abo-segment-choix {
  position: relative;
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--ge-abo-tap);
  padding: 0 14px;
  border: 1.5px solid var(--ge-abo-bord);
  border-radius: var(--ge-abo-rayon);
  background: #fff;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.3;
  text-align: center;
  color: #4a453f;
  cursor: pointer;
  transition: background .2s var(--ge-abo-ease), border-color .2s var(--ge-abo-ease),
              color .2s var(--ge-abo-ease);
}

.ge-abo-segment-choix input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

/* Coché : vert plein et texte crème — écrits ensemble, jamais l'un sans
   l'autre. Aucun état ne dépend du survol : la borne est tactile. */
.ge-abo-segment-choix:has(input:checked) {
  border-color: var(--ge-abo-vert);
  background: var(--ge-abo-vert);
  color: var(--ge-abo-creme);
}

.ge-abo-segment-choix:has(input:focus-visible) {
  outline: 3px solid var(--ge-abo-orange);
  outline-offset: 2px;
}

/* La phrase de réponse, dans son encart translucide : elle répond à la
   question qu'on vient de poser, elle ne s'annonce pas comme un argument. */
.ge-abo-crew-chiffre {
  margin: 0;
  padding: 14px 16px;
  border-radius: var(--ge-abo-rayon);
  background: rgba(36, 64, 34, .07);
  font-size: 14.5px;
  font-weight: 500;
  line-height: 1.6;
  color: #3c3831;
}

.ge-abo-crew-chiffre:empty { display: none; }

/* ── « Non merci, pas cette fois » ── */

.ge-abo-crew-case {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: var(--ge-abo-tap);
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--ge-abo-bord);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--ge-abo-gris);
  cursor: pointer;
}

.ge-abo-crew-case input {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  accent-color: var(--ge-abo-vert);
}

.ge-abo-crew-case:has(input:checked) { color: var(--ge-abo-vert); font-weight: 700; }

/* ── Validation au fil de la saisie ───────────────── */

.ge-abo-champ input.est-en-erreur {
  border-color: var(--ge-abo-danger);
}

.ge-abo-champ input.est-en-erreur:focus {
  box-shadow: 0 0 0 4px rgba(155, 28, 28, .15);
}

/* Le message se déplie plutôt que d'apparaître : la ligne qui pousse le
   reste de la page d'un coup fait perdre sa place. max-height et non height,
   qui ne s'anime pas depuis auto. */
.ge-abo-erreur-champ {
  display: block;
  overflow: hidden;
  margin-top: 5px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--ge-abo-danger);
  /* Sans « forwards » : passée l'animation, le message retrouve sa hauteur
     naturelle. Figé sur l'image de fin, un message de trois lignes aurait
     été rogné par le plafond de 44 px qui n'existe que le temps du dépli. */
  animation: ge-abo-deplie .3s var(--ge-abo-ease);
}

@keyframes ge-abo-deplie {
  from { max-height: 0; margin-top: 0; opacity: 0; }
  to   { max-height: 44px; margin-top: 5px; opacity: 1; }
}

/* ── Justificatif : zone de dépôt ─────────────────── */

.ge-abo-depot {
  position: relative;
  overflow: hidden;
  min-height: 180px;
  margin-bottom: 20px;
  border: 2px dashed var(--ge-abo-bord);
  border-radius: var(--ge-abo-rayon);
  background: #fff;
  transition: border-color .3s var(--ge-abo-ease), background .3s var(--ge-abo-ease);
}

/* Le champ natif reste focusable et relié à son étiquette : c'est lui qui
   ouvre l'appareil photo. Ni display:none ni visibility:hidden, qui le
   sortiraient du parcours clavier. */
.ge-abo-depot input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.ge-abo-depot-appel {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 180px;
  padding: 24px;
  text-align: center;
  cursor: pointer;
}

.ge-abo-depot-titre { font-size: 15px; font-weight: 700; color: var(--ge-abo-vert); }
.ge-abo-depot-aide { font-size: 12.5px; color: var(--ge-abo-gris); }

.ge-abo-depot:has(input:focus-visible) {
  outline: 3px solid var(--ge-abo-orange);
  outline-offset: 3px;
}

/* Fichier choisi : le pointillé se referme et la jauge court d'un bord à
   l'autre. Rien n'est encore parti vers le serveur — c'est « Continuer »
   qui téléverse — mais quelque chose se passe, et cela se voit. */
.ge-abo-depot.est-actif {
  border-style: solid;
  border-color: var(--ge-abo-orange);
  background: rgba(255, 135, 77, .06);
}

.ge-abo-depot-jauge {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 0;
  height: 4px;
  background: var(--ge-abo-orange);
}

.ge-abo-depot.est-actif .ge-abo-depot-jauge {
  animation: ge-abo-jauge 1.2s var(--ge-abo-ease) forwards;
}

@keyframes ge-abo-jauge {
  from { width: 0; }
  to   { width: 100%; }
}

/* ── Justificatif : accusé de réception ───────────── */

.ge-abo-recu {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 20px;
  padding: 16px 18px;
  border: 2px solid var(--ge-abo-succes);
  border-radius: var(--ge-abo-rayon);
  background: #fff;
  animation: ge-abo-recu-entre .45s var(--ge-abo-ease) both;
}

@keyframes ge-abo-recu-entre {
  from { opacity: 0; transform: translateX(-10px); }
  to   { opacity: 1; transform: none; }
}

.ge-abo-recu-vignette {
  flex: 0 0 auto;
  width: 64px;
  height: 64px;
  border-radius: 6px;
  background: var(--ge-abo-creme);
  object-fit: cover;
}

.ge-abo-recu-corps {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.ge-abo-recu-nom {
  font-size: 14px;
  font-weight: 700;
  color: var(--ge-abo-vert);
  overflow-wrap: anywhere;
}

.ge-abo-recu-note { font-size: 12.5px; line-height: 1.5; color: var(--ge-abo-gris); }

/* Sous 480 px, le bouton « Changer » passe sous le texte plutôt que de le
   comprimer à quelques mots par ligne. */
@media (max-width: 480px) {
  .ge-abo-recu { flex-wrap: wrap; }
  .ge-abo-recu .ge-abo-btn { width: 100%; }
}

/* ── Formules et variantes ────────────────────────── */

.ge-abo-formules {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 16px;
  margin-bottom: 24px;
}

.ge-abo-formule {
  display: flex;
  flex-direction: column;
  padding: 22px;
  border: 2px solid var(--ge-abo-bord);
  border-radius: var(--ge-abo-rayon);
  background: #fff;
}

/* Une seule formule proposée pour l'activité choisie : la carte n'a aucune
   raison de se serrer dans une colonne. Le script pose « est-seule » quand il
   n'en reste qu'une de visible — l'attribut hidden ne se lit pas en CSS depuis
   le parent, et :only-child compterait aussi les cartes masquées. */
.ge-abo-formules.est-seule {
  grid-template-columns: minmax(0, 1fr);
}

.ge-abo-formules.est-seule .ge-abo-formule { padding: 28px; }

/* Les variantes profitent de la largeur : côte à côte plutôt qu'empilées.
   Le seuil fait le travail à la place d'un compte de colonnes : deux
   variantes se rangent de front, trois aussi dès que la carte le permet,
   et elles se réempilent d'elles-mêmes quand la place manque. */
.ge-abo-formules.est-seule .ge-abo-variantes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  align-items: stretch;
  gap: 12px;
}

/* En grille, chaque variante doit remplir la hauteur de sa rangée pour que
   les cadres s'alignent, quel que soit le nombre de lignes de libellé. */
.ge-abo-formules.est-seule .ge-abo-variante,
.ge-abo-formules.est-seule .ge-abo-variante-corps { height: 100%; }

/* Les blocs d'une carte gardent leur hauteur propre.
   `height: 100%` (hauteur uniforme des cartes, plus bas) fait de la carte un
   conteneur flex à hauteur contrainte : sans ce flex-shrink:0, l'en-tête se
   faisait comprimer sous sa hauteur de contenu et le logo CREW venait se
   poser par-dessus le paragraphe de description. */
.ge-abo-formule > * { flex: 0 0 auto; }

/* Le bloc des variantes, lui, absorbe l'espace restant et reste en bas :
   c'est ce qui aligne les tarifs d'une carte à l'autre. */
.ge-abo-formule .ge-abo-variantes { margin-top: auto; }

.ge-abo-formule-tete { margin-bottom: 14px; }

/* Titre et logo sur une même ligne, bases alignées. Le logo cède le pas :
   c'est le nom qui doit rester lisible si la place manque. */
.ge-abo-formule-titre {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 6px;
  /* Le titre et le logo forment une ligne à part entière : rien de ce qui
     suit ne peut remonter dessus. */
  flex: 0 0 auto;
  min-height: 24px;
}

.ge-abo-formule-nom { margin: 0; font-size: 18px; line-height: 1.25; color: var(--ge-abo-vert); min-width: 0; }

/* Hauteur calée sur celle du titre (18px × 1.25), largeur libre : le logo
   garde son ratio quelle que soit la dimension du fichier fourni. */
.ge-abo-formule-logo {
  flex: 0 0 auto;
  align-self: center;
  /* Hauteur bornée plutôt qu'imposée : une image qui n'a pas encore été
     chargée, ou qui n'a pas de dimensions intrinsèques, ne peut pas prendre
     plus de place que la ligne du titre. */
  max-height: 22px;
  height: auto;
  width: auto;
  max-width: 96px;
  object-fit: contain;
}
.ge-abo-formule-desc { margin: 0; font-size: 13px; line-height: 1.55; color: var(--ge-abo-gris); }

.ge-abo-formule-avantages {
  margin: 0 0 16px;
  padding: 0;
  list-style: none;
  font-size: 13px;
  line-height: 1.7;
  color: #4a453f;
}

.ge-abo-formule-avantages li { padding-left: 18px; position: relative; }
.ge-abo-formule-avantages li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: .62em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--ge-abo-orange);
}

.ge-abo-formule-note {
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--ge-abo-bord);
  font-size: 12px;
  line-height: 1.55;
  color: var(--ge-abo-gris);
}

/* ── Une variante = un tarif pour un engagement donné ── */

/* 12px de haut : le tag « Tarif membre » déborde de 8px au-dessus de sa
   variante, et n'a rien à recouvrir. */
.ge-abo-variantes { display: flex; flex-direction: column; gap: 14px; padding-top: 10px; }

/* Repère du tag « Tarif membre », posé à cheval sur le bord haut. */
.ge-abo-variante { position: relative; display: block; cursor: pointer; }

.ge-abo-variante input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

/* Trois colonnes : le rond, le texte, le prix. Le prix calé à droite est ce
   qui rend deux variantes comparables sans les lire — c'est la seule chose
   qu'on cherche quand on hésite entre deux lignes. */
.ge-abo-variante-corps {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 12px;
  row-gap: 8px;
  padding: 14px 16px;
  border: 2px solid var(--ge-abo-bord);
  border-radius: var(--ge-abo-rayon);
  background: #fff;
  transition: background .2s var(--ge-abo-ease), border-color .2s var(--ge-abo-ease),
              box-shadow .15s ease, transform .15s ease;
}

.ge-abo-variante:hover .ge-abo-variante-corps {
  border-color: var(--ge-abo-vert);
  transform: translateY(-2px);
}

/* Coché : le fond vert forêt, et le texte en crème avec lui. Écrire les deux
   ensemble, jamais l'un sans l'autre — c'est l'oubli inverse qui rendait la
   carte survolée illisible en production. */
.ge-abo-variante input:checked + .ge-abo-variante-corps {
  border-color: var(--ge-abo-vert);
  background: var(--ge-abo-vert);
  color: var(--ge-abo-creme);
}

.ge-abo-variante input:checked + .ge-abo-variante-corps .ge-abo-variante-statut,
.ge-abo-variante input:checked + .ge-abo-variante-corps .ge-abo-prix-n {
  color: var(--ge-abo-creme);
}

/* Mentions et prix barré s'effacent sans disparaître : ils restent au-dessus
   du seuil de contraste, mais cessent de concurrencer le prix retenu. */
.ge-abo-variante input:checked + .ge-abo-variante-corps .ge-abo-variante-engagement,
.ge-abo-variante input:checked + .ge-abo-variante-corps .ge-abo-variante-note {
  color: rgba(243, 230, 218, .78);
}

.ge-abo-variante input:checked + .ge-abo-variante-corps .ge-abo-prix-old {
  color: rgba(243, 230, 218, .62);
}

/* Le focus clavier doit rester visible : la case native est masquée. */
.ge-abo-variante input:focus-visible + .ge-abo-variante-corps {
  outline: 3px solid var(--ge-abo-orange);
  outline-offset: 2px;
}

/* Le bloc central : libellé, mention d'engagement, note de statut. */
.ge-abo-variante-texte { min-width: 0; }

.ge-abo-variante-tete {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* Pastille radio dessinée : la native ne se met pas à la charte. */
.ge-abo-pastille {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  align-self: center;
  width: 18px;
  height: 18px;
  border: 2px solid var(--ge-abo-bord);
  border-radius: 50%;
  background: #fff;
}

/* Le point intérieur grandit depuis le centre : le geste dit « c'est celle-ci »
   mieux qu'une apparition sèche. Un pseudo-élément plutôt qu'un dégradé
   radial, parce qu'un dégradé ne s'anime pas. */
.ge-abo-pastille::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ge-abo-vert);
  transform: scale(0);
  transition: transform .3s var(--ge-abo-ease);
}

.ge-abo-variante input:checked + .ge-abo-variante-corps .ge-abo-pastille {
  border-color: var(--ge-abo-creme);
  background: transparent;
}

.ge-abo-variante input:checked + .ge-abo-variante-corps .ge-abo-pastille::before {
  background: var(--ge-abo-creme);
  transform: scale(1);
}

/* ── Tag « Tarif membre » ──
   À cheval sur le bord haut, et seulement sur les variantes qui exigent le
   statut. La mention en toutes lettres reste dans la carte : le tag se voit
   pendant qu'on compare les prix, la phrase s'y lit quand on s'arrête. */
.ge-abo-variante-tag {
  position: absolute;
  top: -8px;
  right: 12px;
  z-index: 1;
  padding: 2px 8px;
  border-radius: 4px;
  background: var(--ge-abo-orange);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .02em;
  line-height: 1.5;
  color: var(--ge-abo-vert);
  white-space: nowrap;
  pointer-events: none;
}

.ge-abo-variante-statut { font-size: 13px; font-weight: 700; color: #4a453f; }

.ge-abo-badge {
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--ge-abo-orange);
  font-size: 11px;
  font-weight: 700;
  color: var(--ge-abo-vert);
  white-space: nowrap;
}

/* colonne inversée : le prix barré est écrit après le prix retenu dans le
   document — pour qu'un lecteur d'écran annonce d'abord ce qu'on paie — mais
   il s'affiche au-dessus, là où l'œil attend le « avant ». */
.ge-abo-prix {
  display: flex;
  flex-direction: column-reverse;
  align-items: flex-end;
  justify-self: end;
  gap: 2px;
  text-align: right;
}

/* « 39 €/mois » se coupait en « 39 €/ » puis « mois » : la barre oblique est
   une occasion de césure pour le navigateur. Le montant et son unité forment
   un tout — c'est le couple de prix qui passe à la ligne, jamais un prix. */
.ge-abo-prix-n,
.ge-abo-prix-old,
.ge-abo-ajout-prix-n,
.ge-abo-ajout-prix-old,
.ge-abo-total-valeur,
.ge-abo-total-detail-prix { white-space: nowrap; }

.ge-abo-prix-n { font-size: 26px; font-weight: 700; line-height: 1.1; color: var(--ge-abo-vert); }
.ge-abo-prix-old { font-size: 15px; color: var(--ge-abo-gris); text-decoration: line-through; }
/* Accroche à la place d'un montant (« 1er mois offert ») : plus longue
   qu'un prix, elle passe à la ligne au lieu de déborder sur le texte. */
.ge-abo-prix .ge-abo-prix-n.ge-abo-prix-n--texte { font-size: 18px; white-space: normal; text-align: inherit; max-width: 9em; }

.ge-abo-variante-engagement { display: block; margin-top: 5px; font-size: 12px; color: var(--ge-abo-gris); }

.ge-abo-variante-note {
  display: block;
  margin-top: 8px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.45;
  color: var(--ge-abo-vert);
}

/* ── Ligne Membre, révélée par un tarif membre ── */

.ge-abo-membre {
  margin-bottom: 24px;
  padding: 22px;
  border: 2px solid var(--ge-abo-vert);
  border-radius: var(--ge-abo-rayon);
  background: var(--ge-abo-creme);
}

.ge-abo-membre h3 { margin: 0 0 6px; font-size: 16px; color: var(--ge-abo-vert); }

/* ── Total mensuel ── */

/* ── Ajouter un abonnement au tarif membre ──────────
   Panneau à part entière de l'étape 1, et non un encart : pleine largeur,
   titre de niveau étape, et deux issues explicites en bas. */

.ge-abo-ajouts-liste {
  display: grid;
  gap: 12px;
  margin-bottom: 24px;
}

/* Rangée d'actions secondaire — le retour en arrière, sous les deux issues
   principales, sans concurrencer leur poids visuel. */
.ge-abo-actions-secondaires {
  margin-top: 10px;
  justify-content: center;
}

.ge-abo-actions-secondaires .ge-abo-btn { flex: 0 1 auto; }

/* Rappel de ce que le statut donne déjà : sans lui, la page n'aligne que
   des lignes payantes de plus. */
.ge-abo-rappel {
  margin-bottom: 20px;
  padding: 18px 22px;
  border-radius: var(--ge-abo-rayon);
  background: var(--ge-abo-vert);
  color: var(--ge-abo-creme);
}

.ge-abo-rappel-titre {
  margin: 0 0 8px;
  font-size: 14px;
  font-weight: 700;
}

.ge-abo-rappel-liste {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 13px;
  line-height: 1.5;
  color: rgba(243, 230, 218, .85);
}

.ge-abo-rappel-liste li { position: relative; padding-left: 14px; }

.ge-abo-rappel-liste li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .55em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--ge-abo-orange);
}

/* La ligne entière est la cible : case dessinée, contenu, prix. */
.ge-abo-ajout {
  position: relative;
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) auto;
  align-items: start;
  column-gap: 16px;
  row-gap: 10px;
  padding: 20px 22px;
  border: 2px solid var(--ge-abo-bord);
  border-radius: var(--ge-abo-rayon);
  background: #fff;
  cursor: pointer;
  transition: border-color .16s ease, box-shadow .16s ease;
}

.ge-abo-ajout.est-choisie {
  border-color: var(--ge-abo-vert);
  box-shadow: 0 0 0 3px rgba(36, 64, 34, .12);
}

/* La case native reste dans le flux d'accessibilité — celle qu'on voit est
   dessinée à côté, parce que la native ne se met pas à la charte. */
.ge-abo-ajout input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.ge-abo-ajout-case {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  margin-top: 2px;
  border: 2px solid var(--ge-abo-bord);
  border-radius: 6px;
  background: #fff;
  transition: background .2s var(--ge-abo-ease), border-color .2s var(--ge-abo-ease);
}

/* Coche en deux bordures tournées : rien à charger, nette à tout zoom. */
.ge-abo-ajout-case::before {
  content: "";
  width: 11px;
  height: 6px;
  margin-top: -3px;
  border-left: 2.5px solid var(--ge-abo-creme);
  border-bottom: 2.5px solid var(--ge-abo-creme);
  transform: rotate(-45deg) scale(0);
  transition: transform .3s var(--ge-abo-ease);
}

.ge-abo-ajout input:checked ~ .ge-abo-ajout-case {
  border-color: var(--ge-abo-vert);
  background: var(--ge-abo-vert);
}

.ge-abo-ajout input:checked ~ .ge-abo-ajout-case::before { transform: rotate(-45deg) scale(1); }

.ge-abo-ajout-corps {
  display: block;
  min-width: 0;
}

.ge-abo-ajout input:focus-visible ~ .ge-abo-ajout-corps {
  outline: 3px solid var(--ge-abo-orange);
  outline-offset: 6px;
}

.ge-abo-ajout-nom {
  display: block;
  font-size: 16px;
  font-weight: 700;
  color: var(--ge-abo-vert);
  line-height: 1.3;
}

.ge-abo-ajout-desc {
  display: block;
  margin-top: 4px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ge-abo-gris);
}

.ge-abo-ajout-prix {
  display: flex;
  flex-direction: column-reverse;
  align-items: flex-end;
  justify-self: end;
  gap: 2px;
  text-align: right;
}

.ge-abo-ajout-prix-n { font-size: 30px; font-weight: 700; line-height: 1.05; color: var(--ge-abo-vert); }
.ge-abo-ajout-prix-old { font-size: 14px; color: var(--ge-abo-gris); text-decoration: line-through; }

/* ── Puces d'économie ──
   Orange quand il y a vraiment quelque chose à gagner ce mois-ci, gris
   sinon. La couleur ne porte jamais l'information à elle seule : la puce
   grise dit en toutes lettres pourquoi elle n'est pas orange. */
.ge-abo-ajout-puces {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}

.ge-abo-puce {
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--ge-abo-bord);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.5;
  color: #4a453f;
  white-space: nowrap;
}

.ge-abo-puce.est-gain {
  background: var(--ge-abo-orange);
  font-weight: 700;
  color: var(--ge-abo-vert);
}

/* Somme des économies constatées, sous le total du panneau. */
.ge-abo-ajouts-gain {
  margin: -12px 0 24px;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.5;
  color: var(--ge-abo-succes);
}

/* Sous 480 px, le prix passe sous le texte : deux colonnes plutôt que
   trois, et un prix de 30 px ne comprime plus le nom de la formule. */
@media (max-width: 480px) {
  .ge-abo-ajout { grid-template-columns: 24px minmax(0, 1fr); }

  .ge-abo-ajout-prix {
    grid-column: 2;
    align-items: flex-start;
    justify-self: start;
    text-align: left;
  }
}

/* ── Détail du total ──────────────────────────────── */

.ge-abo-total-detail {
  margin: 0 0 2px;
  padding: 14px 22px 6px;
  list-style: none;
  border-radius: var(--ge-abo-rayon) var(--ge-abo-rayon) 0 0;
  background: var(--ge-abo-creme);
  font-size: 14px;
}

.ge-abo-total-detail li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  padding: 7px 0;
  /* Pointillé plutôt que trait plein : il sépare sans découper, et l'œil
     suit la ligne du libellé jusqu'à son montant sans se perdre. */
  border-bottom: 1px dotted rgba(111, 106, 99, .45);
}

.ge-abo-total-detail li:last-child { border-bottom: 0; }

.ge-abo-total-detail-nom { color: var(--ge-abo-gris); min-width: 0; }
.ge-abo-total-detail-prix { font-weight: 700; white-space: nowrap; }

/* La ligne qui vient d'apparaître, et elle seule. Le halo s'efface de
   lui-même : passée la seconde et demie, plus rien ne la distingue des
   autres — elle n'est plus nouvelle. */
.ge-abo-total-detail li.est-nouvelle {
  animation: ge-abo-ligne-neuve 1.4s var(--ge-abo-ease);
}

@keyframes ge-abo-ligne-neuve {
  0%   { background: rgba(255, 135, 77, .25); transform: translateX(-6px); }
  30%  { transform: translateX(0); }
  100% { background: rgba(255, 135, 77, 0); transform: translateX(0); }
}

/* Le total vide, avant tout choix : gris, discret, mais présent — la place
   du montant est déjà réservée à l'écran, il n'y aura pas de saut. */
.ge-abo-total-vide {
  margin: 0 0 24px;
  padding: 16px 22px;
  border: 1.5px dashed var(--ge-abo-bord);
  border-radius: var(--ge-abo-rayon);
  font-size: 14px;
  color: var(--ge-abo-gris);
}

.ge-abo-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 24px;
  padding: 18px 22px;
  border-radius: var(--ge-abo-rayon);
  background: var(--ge-abo-vert);
  color: var(--ge-abo-creme);
}

.ge-abo-total-label { font-size: 14px; font-weight: 600; }

.ge-abo-total-valeur {
  display: inline-block;
  font-size: 26px;
  font-weight: 700;
  /* Le point d'ancrage du sursaut : à droite, le chiffre grossit vers
     l'intérieur du bloc au lieu de déborder de sa marge. */
  transform-origin: right center;
}

/* Le sursaut du compteur, posé au tiers du trajet : le montant se fait
   remarquer pendant qu'il roule, pas une fois arrêté. */
.ge-abo-total-valeur.est-bousculee {
  animation: ge-abo-sursaut .5s var(--ge-abo-ease);
}

@keyframes ge-abo-sursaut {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.08); color: var(--ge-abo-orange); }
  100% { transform: scale(1); }
}

/* ── Barre de total, fixée en bas d'écran ──
   Mobile seulement : au-delà de 768 px, le total du flux est déjà sous les
   yeux, et une barre fixe ne ferait que manger de la hauteur utile. */
.ge-abo-barre {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
  background: var(--ge-abo-encre);
  color: var(--ge-abo-creme);
  box-shadow: 0 -6px 20px rgba(9, 9, 9, .28);
}

.ge-abo-barre-montant {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.15;
}

.ge-abo-barre-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: rgba(243, 230, 218, .72);
}

.ge-abo-barre-valeur { font-size: 26px; font-weight: 700; white-space: nowrap; }

.ge-abo-barre .ge-abo-btn {
  flex: 0 0 auto;
  width: auto;
  min-height: var(--ge-abo-tap);
  margin-left: 0;
  padding: 0 20px;
}

@media (min-width: 768px) {
  /* !important : l'attribut hidden du script pilote déjà l'affichage, mais
     au-delà de 768 px la barre ne doit jamais réapparaître, même visible. */
  .ge-abo-barre { display: none !important; }
}

/* En paysage, la hauteur utile est rare : la barre coûte plus qu'elle ne
   rapporte, et le total du flux reprend son rôle. */
@media (max-height: 520px) {
  .ge-abo-barre { display: none !important; }
}

.ge-abo-aide {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  color: var(--ge-abo-gris);
}

/* ── Champs ───────────────────────────────────────── */

/* Une colonne au départ, deux dès la tablette — et jamais trois : à
   900 px, un auto-fit à 230 px en posait trois, et le formulaire devenait
   une grille de cases où l'œil ne savait plus quel libellé allait avec quel
   champ. Deux colonnes gardent la lecture en Z. */
.ge-abo-grille {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 4px 16px;
}

@media (min-width: 768px) {
  .ge-abo-grille { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.ge-abo-champ { margin: 0 0 16px; }
.ge-abo-champ-large { grid-column: 1 / -1; }

.ge-abo-champ label {
  display: block;
  margin-bottom: 5px;
  font-size: 13px;
  font-weight: 700;
  color: #4a453f;
}

.ge-abo-champ input {
  width: 100%;
  min-height: 48px;
  padding: 11px 14px;
  border: 1.5px solid var(--ge-abo-bord);
  border-radius: var(--ge-abo-rayon);
  background: #fff;
  /* 16 px minimum : en dessous, Safari iOS zoome au focus. */
  font-size: 16px;
  font-family: inherit;
  color: inherit;
}

.ge-abo-champ input {
  transition: border-color .18s ease, box-shadow .18s ease;
}

/* Bordure encre et halo orange : le halo signale, la bordure ancre. Une
   bordure orange seule se confondait avec l'anneau de focus du reste du
   module, qui, lui, dit « clavier » et non « champ actif ». */
.ge-abo-champ input:focus {
  outline: none;
  border-color: var(--ge-abo-encre);
  box-shadow: 0 0 0 4px rgba(255, 135, 77, .35);
}

/* ── Champ valide ──
   Coche posée en fond plutôt qu'en pseudo-élément : un <input> n'en accepte
   pas, et l'accrocher au paragraphe parent l'aurait fait flotter quelque
   part entre le libellé et le message d'erreur, à une distance qui dépend
   de la hauteur du libellé. En fond, elle est calée sur le champ lui-même,
   quelle qu'en soit la taille. */
.ge-abo-champ input.est-valide {
  border-color: var(--ge-abo-succes);
  /* La place de la coche, pour que le texte saisi ne passe pas dessous. */
  padding-right: 38px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 8.5l4 4 8-9' fill='none' stroke='%231a6b3c' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 13px center;
  background-size: 16px 16px;
}

/* Le champ en erreur n'a jamais de coche : les deux états s'excluent, et la
   règle d'erreur passe après pour reprendre la main quoi qu'il arrive. */
.ge-abo-champ input.est-en-erreur {
  background-image: none;
  padding-right: 14px;
}

/* ── Les six cases du code ────────────────────────── */

.ge-abo-otp-champ {
  min-width: 0;
  margin: 0 0 14px;
  padding: 0;
  border: 0;
}

.ge-abo-otp-champ legend {
  padding: 0;
  margin-bottom: 7px;
  font-size: 13px;
  font-weight: 700;
  color: #4a453f;
}

.ge-abo-otp-cases {
  display: flex;
  gap: 8px;
}

.ge-abo-otp-case {
  flex: 1 1 0;
  min-width: 0;
  /* 56px de haut, et 16px de police au minimum : en dessous, Safari iOS
     zoome à chaque focus et le clavier recouvre les cases suivantes. */
  height: 56px;
  padding: 0;
  border: 2px solid var(--ge-abo-bord);
  border-radius: var(--ge-abo-rayon);
  background: #fff;
  font-family: inherit;
  font-size: 26px;
  font-weight: 700;
  line-height: 1;
  text-align: center;
  color: var(--ge-abo-vert);
}

.ge-abo-otp-case:focus {
  outline: none;
  border-color: var(--ge-abo-encre);
  box-shadow: 0 0 0 4px rgba(255, 135, 77, .35);
}

/* Une case remplie se voit sans être relue : la bordure suffit, le chiffre
   qu'elle contient dit le reste. */
.ge-abo-otp-case:not(:placeholder-shown) { border-color: var(--ge-abo-vert); }

@media (max-width: 380px) {
  .ge-abo-otp-case { height: 50px; font-size: 22px; }
  .ge-abo-otp-cases { gap: 5px; }
}

.ge-abo-mineur {
  margin: 8px 0 20px;
  padding: 18px;
  border: 1.5px solid var(--ge-abo-orange);
  border-radius: var(--ge-abo-rayon);
  background: rgba(255, 135, 77, .06);
}

.ge-abo-mineur legend {
  padding: 0 8px;
  font-size: 14px;
  font-weight: 700;
  color: var(--ge-abo-vert);
}

/* Le pot de miel doit être invisible pour un humain sans être « display:none »,
   que certains robots détectent et contournent. */
.ge-abo-pot-de-miel {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ── Mentions RGPD ────────────────────────────────── */

.ge-abo-rgpd {
  margin: 24px 0;
  padding: 18px;
  border-radius: var(--ge-abo-rayon);
  background: var(--ge-abo-creme);
  font-size: 13px;
  line-height: 1.65;
  color: #4a453f;
}

.ge-abo-rgpd h3 { margin: 0 0 8px; font-size: 14px; }
.ge-abo-rgpd p { margin: 0 0 10px; }
.ge-abo-rgpd p:last-child { margin-bottom: 0; }

/* ── Documents ────────────────────────────────────── */

.ge-abo-doc {
  margin-bottom: 22px;
  border: 1.5px solid var(--ge-abo-bord);
  border-radius: var(--ge-abo-rayon);
  background: #fff;
  overflow: hidden;
}

/* align-items: center et non baseline : l'anneau de 32 px ajouté à droite
   se calait sinon sur la ligne de base du titre et débordait du padding. */
.ge-abo-doc-entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 18px;
  background: var(--ge-abo-vert);
  transition: background .35s var(--ge-abo-ease), box-shadow .35s var(--ge-abo-ease);
}

.ge-abo-doc-titre { min-width: 0; }

.ge-abo-doc-entete h3 { margin: 0; font-size: 15px; color: var(--ge-abo-creme); }
.ge-abo-doc-version { display: block; font-size: 12px; color: var(--ge-abo-creme); opacity: .75; }

/* Lu jusqu'au bout : l'en-tête s'assombrit. Un voile d'encre plutôt qu'une
   seconde couleur de vert — le module n'en a qu'un, et en ajouter une le
   ferait diverger de la charte au premier changement de teinte. */
.ge-abo-doc[data-lu="1"] .ge-abo-doc-entete {
  box-shadow: inset 0 0 0 999px rgba(9, 9, 9, .25);
}

/* ── Anneau de progression de lecture ──
   Il suit la lecture, il ne la commande pas : le déverrouillage de la case
   reste entièrement piloté par l'arithmétique du défilement et la position
   de la sentinelle. Ici, on ne fait que dessiner. */
.ge-abo-anneau {
  position: relative;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
}

/* Le tracé démarre à midi et tourne dans le sens des aiguilles. */
.ge-abo-anneau svg { transform: rotate(-90deg); }

.ge-abo-anneau circle {
  fill: none;
  stroke-width: 3;
}

.ge-abo-anneau-fond { stroke: rgba(243, 230, 218, .28); }

.ge-abo-anneau-trait {
  stroke: var(--ge-abo-creme);
  stroke-linecap: round;
  /* 100.5 ≈ 2π × 16 : la circonférence exacte du cercle tracé. Le viewBox
     fait 36 pour un rendu à 32 — le rayon de 16 tient ainsi dans la boîte
     sans que la moitié externe du trait soit rognée. */
  stroke-dasharray: 100.5;
  stroke-dashoffset: calc(100.5 - 100.5 * var(--ge-abo-k, 0));
  transition: stroke-dashoffset .3s var(--ge-abo-ease), stroke .3s var(--ge-abo-ease);
}

/* Lu : l'anneau se ferme et passe au sauge — quel que soit le dernier k
   mesuré. C'est la mécanique de lecture qui fait foi, pas l'arithmétique du
   défilement, qui s'arrête parfois quelques pixels avant le bas. Et si le
   journal refuse la lecture, l'attribut est retiré : la règle cesse de
   s'appliquer, et l'anneau retombe de lui-même sur la mesure réelle. */
.ge-abo-doc[data-lu="1"] .ge-abo-anneau-trait {
  stroke: var(--ge-abo-sauge);
  stroke-dashoffset: 0;
}

.ge-abo-anneau-coche {
  position: absolute;
  width: 9px;
  height: 5px;
  margin-top: -2px;
  border-left: 2px solid var(--ge-abo-creme);
  border-bottom: 2px solid var(--ge-abo-creme);
  transform: rotate(-45deg) scale(0);
  transition: transform .3s var(--ge-abo-ease);
}

.ge-abo-doc[data-lu="1"] .ge-abo-anneau-coche { transform: rotate(-45deg) scale(1); }

/* Case acceptée : le cadre entier le dit. Aucune classe à poser en script —
   :has lit la case native, qui est la source de vérité, et se resynchronise
   toute seule quand le serveur refuse ou qu'on remet le parcours à zéro. */
.ge-abo-doc:has([data-role="doc-case"]:checked) {
  border-color: var(--ge-abo-vert);
}

.ge-abo-doc-texte {
  /* 220px : assez pour une dizaine de lignes, assez peu pour qu'on voie le
     cadre suivant et qu'on comprenne qu'il y en a quatre. Les surcharges en
     vh plus bas reprennent la main sur écran tactile et en paysage, où la
     hauteur utile ne se mesure pas en pixels. */
  max-height: 220px;
  overflow-y: auto;
  padding: 18px;
  font-size: 14px;
  line-height: 1.7;
  /* Le défilement doit rester dans le cadre : sans cela, atteindre le bas du
     texte ferait défiler la page entière et la sentinelle ne serait jamais vue. */
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

/* Le texte est posé par le script en textContent : les retours à la ligne du
   document doivent donc être rendus par la mise en forme, pas par des <br>. */
.ge-abo-doc-corps {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.ge-abo-doc-texte:focus-visible {
  outline: 3px solid var(--ge-abo-orange);
  outline-offset: -3px;
}

.ge-abo-doc-fin { display: block; height: 1px; }

.ge-abo-doc-progression {
  margin: 0;
  padding: 10px 18px;
  background: var(--ge-abo-creme);
  font-size: 13px;
  font-weight: 600;
  color: var(--ge-abo-gris);
}

.ge-abo-doc-progression.est-fait { color: var(--ge-abo-succes); }

.ge-abo-doc-case {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 16px 18px;
  border-top: 1px solid var(--ge-abo-bord);
  font-size: 14px;
  line-height: 1.55;
  cursor: pointer;
}

.ge-abo-doc-case input {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  margin-top: 1px;
  accent-color: var(--ge-abo-vert);
}

.ge-abo-doc-case input:disabled { cursor: not-allowed; }
.ge-abo-doc-case:has(input:disabled) { color: var(--ge-abo-gris); cursor: not-allowed; }

/* La bulle du chatbot (autre module, fixée en bas à droite) recouvrait le
   bouton « Continuer » de la barre. Tant que la barre est affichée, elle
   monte au-dessus — et sa fenêtre avec elle. Mêmes bornes que la barre. */
@media (max-width: 767px) and (min-height: 521px) {
  body:has(.ge-abo-barre:not([hidden])) #ge-chat-btn {
    bottom: calc(96px + env(safe-area-inset-bottom, 0px));
  }
  body:has(.ge-abo-barre:not([hidden])) #ge-chat-win {
    bottom: calc(164px + env(safe-area-inset-bottom, 0px));
  }
}

/* ── Résumé et case unique des conditions ───────────
   Le résumé dit ce qu'on signe en quatre lignes ; la case, ce qu'on
   accepte. Les documents s'ouvrent dans une fenêtre de la page. */

.ge-abo-resume {
  margin: 0 0 18px;
  border: 1.5px solid var(--ge-abo-bord);
  border-radius: var(--ge-abo-rayon);
  background: #fff;
  overflow: hidden;
}

.ge-abo-resume-ligne { padding: 14px 18px; }
.ge-abo-resume-ligne + .ge-abo-resume-ligne { border-top: 1px solid var(--ge-abo-bord); }

.ge-abo-resume-tete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
}

.ge-abo-resume-formule { font-size: 16px; font-weight: 700; color: var(--ge-abo-vert); }
.ge-abo-resume-prix { flex: 0 0 auto; font-size: 16px; font-weight: 700; color: var(--ge-abo-encre); }

.ge-abo-resume dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 4px 14px;
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
}

.ge-abo-resume dt { color: var(--ge-abo-gris); }
.ge-abo-resume dd { margin: 0; color: var(--ge-abo-encre); }

.ge-abo-resume-total {
  display: flex;
  justify-content: space-between;
  padding: 12px 18px;
  background: var(--ge-abo-creme);
  font-size: 15px;
  font-weight: 700;
}

.ge-abo-conditions {
  margin: 0 0 18px;
  border: 1.5px solid var(--ge-abo-bord);
  border-radius: var(--ge-abo-rayon);
  background: #fff;
  transition: border-color .2s ease;
}

.ge-abo-conditions:has([data-role="conditions-case"]:checked) { border-color: var(--ge-abo-vert); }

.ge-abo-conditions-ligne {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 16px 18px;
}

.ge-abo-conditions-ligne input {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  margin: 0;
  accent-color: var(--ge-abo-vert);
  cursor: pointer;
}

.ge-abo-conditions-phrase {
  font-size: 15px;
  font-weight: 600;
  line-height: 1.6;
  cursor: pointer;
}

/* Lien de document. Couleurs redites au survol : le thème repeint les liens. */
.ge-abo a.ge-abo-lien-doc,
.ge-abo a.ge-abo-lien-doc:hover,
.ge-abo a.ge-abo-lien-doc:focus,
.ge-abo a.ge-abo-lien-doc:visited {
  font-weight: 700;
  color: var(--ge-abo-vert);
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 3px;
  overflow-wrap: anywhere;
  cursor: pointer;
}

.ge-abo a.ge-abo-lien-doc:focus-visible {
  outline: 3px solid var(--ge-abo-orange);
  outline-offset: 2px;
}

/* ── Fenêtre de lecture ── */

.ge-abo-fenetre {
  width: min(760px, calc(100vw - 32px));
  max-width: none;
  max-height: calc(100dvh - 48px);
  margin: auto;
  padding: 0;
  border: 0;
  border-radius: 12px;
  background: #fff;
  color: var(--ge-abo-encre);
  box-shadow: 0 24px 64px rgba(9, 9, 9, .28);
  overflow: hidden;
}

.ge-abo-fenetre::backdrop { background: rgba(9, 9, 9, .55); }

.ge-abo-fenetre-cadre {
  display: flex;
  flex-direction: column;
  max-height: calc(100dvh - 48px);
}

.ge-abo-fenetre-entete {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 18px;
  border-bottom: 1px solid var(--ge-abo-bord);
}

.ge-abo-fenetre-entete .ge-abo-doc-version { flex: 0 0 auto; color: var(--ge-abo-gris); opacity: 1; }

.ge-abo-fenetre-entete h3 { flex: 1; margin: 0; font-size: 17px; line-height: 1.3; color: var(--ge-abo-vert); }

.ge-abo button.ge-abo-fenetre-fermer,
.ge-abo button.ge-abo-fenetre-fermer:hover {
  flex: 0 0 auto;
  width: var(--ge-abo-tap);
  height: var(--ge-abo-tap);
  padding: 0;
  border: 1.5px solid var(--ge-abo-bord);
  border-radius: 50%;
  background: #fff;
  font-size: 24px;
  line-height: 1;
  color: var(--ge-abo-encre);
  cursor: pointer;
}

.ge-abo-fenetre .ge-abo-fenetre-texte {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}

.ge-abo-fenetre-pied {
  display: flex;
  justify-content: flex-end;
  padding: 12px 18px;
  border-top: 1px solid var(--ge-abo-bord);
}

/* La page ne défile pas derrière la fenêtre ouverte. */
html:has(.ge-abo-fenetre[open]) { overflow: hidden; }

@media (max-width: 480px) {
  .ge-abo-resume-ligne, .ge-abo-conditions-ligne { padding: 14px; }
  .ge-abo-resume dl { grid-template-columns: 1fr; gap: 0; }
  .ge-abo-resume dd + dt { margin-top: 6px; }
  /* Plein écran : la fenêtre est fixée au viewport, quoi que fasse la page. */
  .ge-abo-fenetre { inset: 0; width: 100%; max-width: 100%; height: 100%; max-height: 100%; margin: 0; border-radius: 0; }
  .ge-abo-fenetre-cadre { height: 100%; max-height: 100%; }
  .ge-abo-fenetre-pied .ge-abo-btn { width: 100%; }
}

/* ── Signature ────────────────────────────────────── */

.ge-abo-sepa {
  margin-bottom: 26px;
  padding: 18px;
  border: 1.5px solid var(--ge-abo-bord);
  border-radius: var(--ge-abo-rayon);
  background: #fff;
}

.ge-abo-signature { animation: ge-abo-entre .5s var(--ge-abo-ease) both; }

.ge-abo-signature-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 6px;
}

.ge-abo-signature-tete h3 { margin: 0; }

/* L'état, en toutes lettres. La couleur double le mot, elle ne le remplace
   jamais : « En attente de vérification », « Identité vérifiée — signez »,
   « Signé » se lisent en niveaux de gris. */
.ge-abo-signature-etat {
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--ge-abo-bord);
  font-size: 12px;
  font-weight: 700;
  color: #4a453f;
  transition: background .35s var(--ge-abo-ease), color .35s var(--ge-abo-ease);
}

.ge-abo-signature.est-signee .ge-abo-signature-etat {
  background: var(--ge-abo-vert);
  color: var(--ge-abo-creme);
}

/* ── Cadre verrouillé, avant vérification de l'identité ──
   Affiché mais inerte : on voit ce qui reste à faire au lieu de découvrir
   un bloc apparu de nulle part une fois le code validé. */
.ge-abo-signature.est-bloquee .ge-abo-canvas-cadre {
  background: var(--ge-abo-creme);
  cursor: not-allowed;
}

.ge-abo-signature.est-bloquee .ge-abo-canvas { pointer-events: none; }

.ge-abo-signature.est-bloquee .ge-abo-canvas-appel,
.ge-abo-signature.est-bloquee .ge-abo-canvas-ligne { opacity: .35; }

.ge-abo-canvas-cadre {
  position: relative;
  margin-bottom: 12px;
  border: 2px dashed var(--ge-abo-bord);
  border-radius: var(--ge-abo-rayon);
  background: #fff;
  transition: border-color .35s var(--ge-abo-ease), border-style .35s var(--ge-abo-ease);
}

/* Signé : le pointillé se referme et passe au vert. Deuxième marque, après
   la pastille — aucune information ne tient à la seule couleur. */
.ge-abo-signature.est-signee .ge-abo-canvas-cadre {
  border-style: solid;
  border-color: var(--ge-abo-vert);
}

.ge-abo-canvas {
  display: block;
  position: relative;
  z-index: 1;
  width: 100%;
  height: 200px;
  /* Sans cela, le geste de signature ferait défiler la page sur mobile. */
  touch-action: none;
  cursor: crosshair;
  background: transparent;
}

@media (min-width: 768px) {
  .ge-abo-canvas { height: 240px; }
}

/* Le trait sur lequel on signe, et l'invite. Tous deux sous le canevas :
   ils ne doivent jamais intercepter le geste. */
.ge-abo-canvas-ligne {
  position: absolute;
  left: 8%;
  right: 8%;
  bottom: 26%;
  height: 1px;
  background: var(--ge-abo-bord);
  transition: background .35s var(--ge-abo-ease);
}

.ge-abo-signature.est-signee .ge-abo-canvas-ligne { background: var(--ge-abo-vert); }

.ge-abo-canvas-appel {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 28%;
  text-align: center;
  font-size: 22px;
  font-weight: 700;
  color: rgba(111, 106, 99, .35);
  pointer-events: none;
  transition: opacity .35s var(--ge-abo-ease);
}

.ge-abo-signature.est-signee .ge-abo-canvas-appel { opacity: 0; }

/* ── Boutons ──────────────────────────────────────── */

.ge-abo-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 26px;
}

/* L'espace élastique entre « Retour » et « Continuer ». Une marge auto
   plutôt qu'un élément vide : rien à rendre côté PHP, rien à annoncer aux
   lecteurs d'écran, et l'espace disparaît de lui-même quand les boutons
   s'empilent. */
.ge-abo-actions > .ge-abo-btn-principal { margin-left: auto; }

.ge-abo-actions-inline { margin-top: 12px; }

.ge-abo-btn {
  position: relative;
  /* Les boutons se dimensionnent sur leur libellé : c'est ce qui laisse
     l'espace élastique jouer. Sur mobile ils repassent en pleine largeur. */
  flex: 0 1 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 52px;
  padding: 0 24px;
  border: 1.5px solid var(--ge-abo-bord);
  border-radius: var(--ge-abo-rayon);
  background: #fff;
  font-family: inherit;
  font-size: 16px;
  font-weight: 700;
  color: #4a453f;
  cursor: pointer;
  transition: transform .16s ease, box-shadow .16s ease, background .16s ease;
}

/* Fond et couleur redits au survol, alors qu'ils ne changent pas : le reset
   du thème pose `button:hover { background-color; color }` à la spécificité
   (0,1,1), qui bat une simple classe. Un état de survol qui ne déclare que
   son mouvement laisse donc le thème repeindre le bouton — et c'est ainsi
   que les boutons fantômes se retrouvaient en vert sur vert, texte invisible.
   Toute règle de survol de ce module doit écrire ses deux couleurs. */
.ge-abo-btn:hover:not(:disabled) {
  background: #fff;
  color: #4a453f;
  transform: translateY(-2px);
  box-shadow: 0 6px 18px rgba(36, 64, 34, .16);
}

/* Le bouton principal garde les siennes : sans cela, la règle ci-dessus le
   repeindrait en blanc au survol. */
.ge-abo-btn-principal:hover:not(:disabled),
.ge-abo-btn-principal:focus-visible {
  background: var(--ge-abo-vert);
  color: var(--ge-abo-creme);
}

.ge-abo-btn:active:not(:disabled) { transform: translateY(0); }

/* Spinner intégré au bouton : le libellé change en même temps, la
   personne sait que quelque chose se passe sans quitter le bouton des yeux. */
.ge-abo-spinner {
  width: 16px;
  height: 16px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: ge-abo-tourne .7s linear infinite;
}

@keyframes ge-abo-tourne { to { transform: rotate(360deg); } }

.ge-abo-btn-principal {
  border-color: var(--ge-abo-vert);
  background: var(--ge-abo-vert);
  color: var(--ge-abo-creme);
}

.ge-abo-btn-discret {
  flex: 0 0 auto;
  min-height: var(--ge-abo-tap);
  padding: 0 14px;
  border-color: transparent;
  background: transparent;
  font-size: 13px;
  font-weight: 600;
  color: var(--ge-abo-gris);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Fantôme : au survol, un fond à peine posé et rien d'autre. Le texte
   fonce au lieu de s'effacer — vert forêt sur crème très clair, le rapport
   reste largement au-dessus du seuil. Les deux couleurs sont écrites, et
   c'est la seule chose qui empêche le thème de reprendre la main. */
.ge-abo-btn-discret:hover:not(:disabled),
.ge-abo-btn-discret:focus-visible {
  background: rgba(36, 64, 34, .07);
  border-color: transparent;
  color: var(--ge-abo-vert);
  transform: none;
  box-shadow: none;
}

.ge-abo-btn-discret:focus-visible {
  outline: 3px solid var(--ge-abo-orange);
  outline-offset: 2px;
}

/* Deux façons d'être désactivé, deux curseurs. Le script pose aria-busy
   pendant qu'une requête est en vol : là seulement le sablier est honnête.
   Un bouton verrouillé faute de choix, lui, n'attend rien — et « Continuer »
   avant qu'une activité soit cochée est exactement ce cas. */
/* Action accomplie : le bouton ne redevient pas cliquable, il annonce le
   résultat. Le libellé change en même temps que la couleur — « Vérifié ✓ »
   se lit aussi en niveaux de gris. Opacité pleine, à la différence d'un
   bouton verrouillé : ce n'est pas un empêchement, c'est une réponse. */
.ge-abo-btn.est-termine,
.ge-abo-btn.est-termine:disabled {
  border-color: var(--ge-abo-succes);
  background: var(--ge-abo-succes);
  color: #fff;
  opacity: 1;
  cursor: default;
}

.ge-abo-btn:disabled { opacity: .55; cursor: not-allowed; }
.ge-abo-btn:disabled[aria-busy="true"] { cursor: progress; }

/* Compte à rebours et quota, sous le bouton « Recevoir mon code ». */
.ge-abo-otp-etat {
  margin: 8px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ge-abo-gris);
}

.ge-abo-btn:focus-visible {
  outline: 3px solid var(--ge-abo-orange);
  outline-offset: 2px;
}

/* ── Apparitions au scroll ────────────────────────── */

/* L'état de départ n'est posé que si le script est là pour le lever :
   sans JavaScript, le contenu doit rester visible. */
.ge-abo.js-anime [data-apparait] {
  opacity: 0;
  transform: translateY(24px);
}

.ge-abo.js-anime [data-apparait].est-apparu {
  opacity: 1;
  transform: none;
  transition: opacity .7s cubic-bezier(.22, 1, .36, 1),
              transform .7s cubic-bezier(.22, 1, .36, 1);
}

/* ── Transition entre étapes ──────────────────────── */

/* La sortie glisse vers le haut et non vers la gauche : l'entrée, elle,
   monte de 20 px (voir le keyframes ci-dessous). Deux axes différents pour
   un même passage se liraient comme deux gestes sans rapport. */
.ge-abo-etape.sort {
  opacity: 0;
  transform: translateY(-12px);
  transition: opacity .35s var(--ge-abo-ease), transform .35s var(--ge-abo-ease);
}

/* Un peu plus longue que la sortie : ce qui arrive s'installe, ce qui part
   s'efface. La durée est portée par la classe et non par le keyframes, qui
   sert aussi à la révélation du bloc de signature. */
.ge-abo-etape.entre {
  /* `backwards` et non `both` : figée sur sa dernière image, l'animation
     laissait un transform identité sur la section, qui devenait alors le
     bloc conteneur de la barre « fixe » du bas — rejetée sous l'écran au
     lieu de rester sous le pouce. */
  animation: ge-abo-entre .45s var(--ge-abo-ease) backwards;
}

@keyframes ge-abo-entre {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: none; }
}

/* ── Confirmation ─────────────────────────────────── */

.ge-abo-confirmation {
  /* Colonne centrée plutôt qu'un empilement de blocs à `margin: 0 auto` :
     tous les éléments partagent alors le même axe vertical, quelle que soit
     la largeur intrinsèque de chacun. Le logo ne peut plus dériver. */
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 56px 32px;
  border-radius: 14px;
  background: var(--ge-abo-vert);
  color: var(--ge-abo-creme);
  text-align: center;
}

/* Rien ne se comprime dans cette colonne, et rien n'en déborde. */
.ge-abo-confirmation > * {
  flex: 0 0 auto;
  max-width: 100%;
}

.ge-abo-confirmation .ge-abo-titre { color: var(--ge-abo-creme); font-size: 30px; }
.ge-abo-confirmation .ge-abo-note { color: var(--ge-abo-creme); opacity: .88; font-size: 16px; }

/* Le sceau paraît à l'ouverture : la personne vient de s'engager sur douze
   mois, l'écran doit marquer le coup. Il porte la marque CREW quand elle est
   réglée, et retombe sur une coche tracée quand elle ne l'est pas. */
.ge-abo-sceau {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 84px;
  height: 84px;
  margin: 0 0 14px;
  border-radius: 50%;
  background: var(--ge-abo-orange);
  position: relative;
  animation: ge-abo-sceau-entre .6s var(--ge-abo-ease) both;
}

/* La marque reprend le rythme d'apparition de la coche qu'elle remplace :
   même retard, même courbe, même agrandissement depuis le centre. */
/* Nom propre, et non `.ge-abo-crew` : cette classe désigne déjà le bloc
   « Et le crew ? » de l'étape 1b, et lui imposer une hauteur de logo
   écrasait tout le bloc à 44 px de haut. */
.ge-abo-sceau-marque {
  display: block;
  height: 40px;
  width: auto;
  max-width: 60px;
  object-fit: contain;
  transform: scale(0);
  animation: ge-abo-marque .45s var(--ge-abo-ease) .28s both;
}

@keyframes ge-abo-marque {
  from { transform: scale(0); }
  to   { transform: scale(1); }
}

/* Marque posée : la coche n'a plus lieu d'être, les deux diraient la même
   chose deux fois, l'une par-dessus l'autre. */
.ge-abo-sceau.est-marque::after { content: none; }

/* La mention qui porte réellement l'information — le sceau, lui, est
   aria-hidden. Lisible en niveaux de gris comme en couleurs. */
.ge-abo-signe {
  margin: 0 0 22px;
  padding: 5px 16px;
  border-radius: 999px;
  background: rgba(243, 230, 218, .16);
  border: 1px solid rgba(243, 230, 218, .45);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ge-abo-creme);
}

.ge-abo-sceau::after {
  content: "";
  position: absolute;
  left: 31px;
  top: 20px;
  width: 16px;
  height: 32px;
  border: solid var(--ge-abo-vert);
  border-width: 0 5px 5px 0;
  transform: rotate(45deg) scale(0);
  transform-origin: center;
  animation: ge-abo-coche .45s cubic-bezier(.22, 1, .36, 1) .28s both;
}

@keyframes ge-abo-sceau-entre {
  from { opacity: 0; transform: scale(.4); }
  to   { opacity: 1; transform: scale(1); }
}

@keyframes ge-abo-coche {
  from { transform: rotate(45deg) scale(0); }
  to   { transform: rotate(45deg) scale(1); }
}

/* ── Le texte suit, en cascade ──
   Décalages fixes plutôt qu'un observateur d'intersection : tout est déjà à
   l'écran au moment où l'étape s'ouvre, il n'y a rien à guetter. Ils
   reprennent après la marque, jamais en même temps. */
.ge-abo-confirmation .ge-abo-signe,
.ge-abo-confirmation .ge-abo-titre,
.ge-abo-confirmation .ge-abo-note,
.ge-abo-confirmation .ge-abo-recap-final,
.ge-abo-confirmation .ge-abo-fin-actions {
  animation: ge-abo-entre .55s var(--ge-abo-ease) both;
}

.ge-abo-confirmation .ge-abo-signe { animation-delay: .75s; }
.ge-abo-confirmation .ge-abo-titre { animation-delay: .9s; }
.ge-abo-confirmation .ge-abo-note { animation-delay: 1.05s; }
.ge-abo-confirmation .ge-abo-recap-final { animation-delay: 1.2s; }
.ge-abo-confirmation .ge-abo-fin-actions { animation-delay: 1.35s; }

/* Le fond translucide ne tenait que 1,38:1 sur le vert. Une bordure crème
   pleine porte le bloc à 9,36:1 : il se voit, et se distingue d'un bouton. */
.ge-abo-recap-final {
  display: inline-block;
  margin-top: 28px;
  padding: 20px 28px;
  border: 1.5px solid rgba(243, 230, 218, .45);
  border-radius: var(--ge-abo-rayon);
  background: rgba(0, 0, 0, .14);
  font-size: 15px;
  line-height: 1.8;
  text-align: left;
}

.ge-abo-recap-final-total {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid rgba(243, 230, 218, .3);
  font-size: 18px;
  font-weight: 700;
}

/* Logo CREW, quand un fichier est fourni. Sinon rien : le titre porte le
   message, et un lettrage improvisé serait pire que pas de logo. */
.ge-abo-fin-actions { margin: 30px 0 0; }

/* Action secondaire : on quitte un écran de confirmation, ce n'est pas
   l'action que l'on vient chercher. Contour crème sur le vert — 9,36:1 —
   mais gabarit resserré, texte plus petit, largeur ajustée au contenu.
   Se remplit au survol, le texte passant en vert sur crème : même rapport,
   lisible dans les deux états. */
.ge-abo-btn-retour {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Largeur au contenu, jamais un bloc pleine largeur. */
  width: auto;
  min-height: var(--ge-abo-tap);
  padding: 8px 18px;
  border: 1px solid rgba(243, 230, 218, .7);
  border-radius: var(--ge-abo-rayon);
  background: transparent;
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--ge-abo-creme);
  text-decoration: none;
  cursor: pointer;
  transition: background .18s ease, color .18s ease, border-color .18s ease, transform .16s ease;
}

/* Même principe que le fantôme sur crème, transposé au fond vert : la
   bordure se ferme, un voile crème très léger apparaît, et le texte RESTE
   crème. L'inversion pleine qui existait ici était lisible, mais elle
   changeait les deux couleurs à la fois — un survol de bouton fantôme ne
   doit jamais faire ça, sans quoi la moindre couleur oubliée éteint le
   libellé. */
.ge-abo-btn-retour:hover,
.ge-abo-btn-retour:focus-visible {
  border-color: var(--ge-abo-creme);
  background: rgba(243, 230, 218, .16);
  color: var(--ge-abo-creme);
  transform: translateY(-2px);
}

.ge-abo-btn-retour:focus-visible {
  outline: 3px solid var(--ge-abo-orange);
  outline-offset: 3px;
}

/* Sur un écran sans pointeur fin, :hover se déclenche au tap et reste collé
   jusqu'au tap suivant. On ne garde alors que l'état choisi, qui, lui, dit
   quelque chose de vrai. */
@media (hover: none) {
  .ge-abo .ge-abo-activite:hover {
    background: #fff;
    border-color: var(--ge-abo-bord);
    color: inherit;
    transform: none;
    box-shadow: none;
  }

  .ge-abo .ge-abo-activite:hover .ge-abo-activite-nom { color: var(--ge-abo-vert); }
  .ge-abo .ge-abo-activite:hover .ge-abo-activite-desc { color: var(--ge-abo-gris); }

  .ge-abo .ge-abo-activite:hover .ge-abo-activite-icone {
    background: var(--ge-abo-creme);
    color: var(--ge-abo-vert);
  }

  /* …sauf si elle est effectivement choisie : cet état-là reprend la main. */
  .ge-abo .ge-abo-activite.est-choisie:hover {
    background: var(--ge-abo-vert);
    border-color: var(--ge-abo-vert);
    color: var(--ge-abo-creme);
  }

  .ge-abo .ge-abo-activite.est-choisie:hover .ge-abo-activite-nom,
  .ge-abo .ge-abo-activite.est-choisie:hover .ge-abo-activite-desc {
    color: var(--ge-abo-creme);
  }
}

/* ═══════════════════════════════════════════════════
   PASSE RESPONSIVE
   Trois paliers : mobile ≤767 (cible 375), tablette 768–1023
   (mode borne), desktop ≥1024. Le palier 480 subsiste à
   l'intérieur du mobile pour ce qui ne tient vraiment plus :
   le fil d'étapes et l'empilement des boutons.
═══════════════════════════════════════════════════ */

/* ── Mobile ≤ 767 px : une colonne partout ── */
@media (max-width: 767px) {
  .ge-abo .ge-abo-activites,
  .ge-abo-formules { grid-template-columns: minmax(0, 1fr); }

  /* Une carte pleine largeur n'a plus besoin de ranger ses variantes en
     colonnes : elles reprennent l'empilement, plus lisible au pouce. */
  .ge-abo-formules.est-seule .ge-abo-variantes { grid-template-columns: minmax(0, 1fr); }
}

/* ── Tablette 768–1023 px : deux colonnes, mode borne ── */
@media (min-width: 768px) and (max-width: 1023px) {
  .ge-abo { padding-left: 24px; padding-right: 24px; }

  .ge-abo .ge-abo-activites { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* auto-fit et non repeat(2) : les pistes vides se replient, et deux
     formules occupent la largeur au lieu de laisser un trou à droite. */
  .ge-abo-formules { grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); }
}

/* ── Desktop ≥ 1024 px : trois colonnes, hauteur uniforme ── */
@media (min-width: 1024px) {
  .ge-abo { padding-left: 24px; padding-right: 24px; }

  /* Les pistes sont explicites plutôt qu'auto-fit : à cette largeur on veut
     trois colonnes, pas « autant que ça rentre ». */
  .ge-abo .ge-abo-activites { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  /* auto-fit et non repeat(3) : les pistes vides se replient, et une formule
     seule à l'écran occupe alors toute la largeur au lieu d'un tiers. Avec
     trois formules ou plus, 900 px de contenu donnent bien trois colonnes. */
  .ge-abo-formules { grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); }

  /* align-items:stretch est la valeur par défaut de la grille : les cartes
     d'une même rangée ont donc déjà la même hauteur. On le réaffirme parce
     qu'une carte hidden puis révélée repartait parfois sur sa hauteur propre. */
  .ge-abo .ge-abo-activites,
  .ge-abo-formules { align-items: stretch; }
  .ge-abo .ge-abo-activite,
  .ge-abo-formule { height: 100%; }
}

/* ── La barre de total libère le bas de page dès qu'elle n'est plus fixée ── */
@media (min-width: 768px) {
  .ge-abo { padding-bottom: 120px; }
}

/* ── Documents contractuels ──
   Une colonne jusqu'à 1023 px, deux seulement à partir de 1024. Sur la
   borne, deux textes juridiques côte à côte se lisent mal : on préfère un
   document à la fois, et le fil d'étapes dit déjà qu'il y en a quatre. */
.ge-abo-docs {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
}

/* Une seule colonne : seul le mandat SEPA passe encore par un cadre, les
   autres documents s'ouvrent depuis la case des conditions. */

/* ── Prélèvement et vérification ──
   auto-fit plutôt que deux pistes fixes : sans prélèvement, le mandat
   n'est pas rendu du tout, et la vérification reprend seule la largeur. */
/* min-width sur les deux blocs : en grille, un enfant vaut au moins sa
   taille min-content, et un IBAN insécable élargissait sa piste au lieu de
   revenir à la ligne. */
.ge-abo-sepa,
.ge-abo-otp { min-width: 0; }

.ge-abo-verif {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 20px;
}

@media (min-width: 768px) {
  .ge-abo-verif {
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    align-items: start;
  }

  /* Dans une demi-largeur, les champs bancaires restent en colonne : deux
     pistes de 150 px ne portent plus un IBAN. */
  .ge-abo-verif .ge-abo-grille { grid-template-columns: minmax(0, 1fr); }
}


/* Pointeur grossier — doigt : aucune cible sous 44 × 44 px. La règle porte
   sur le type de pointeur et non sur la largeur : une tablette branchée à un
   écran large reste tactile. */
@media (pointer: coarse) {
  .ge-abo-fil-item {
    min-height: var(--ge-abo-tap);
    padding: 8px 14px;
  }

  .ge-abo-variante-corps { padding: 18px 16px; }

  /* La case de consentement et son libellé forment une seule cible. */
  .ge-abo-doc-case {
    min-height: var(--ge-abo-tap);
    align-items: center;
  }

  .ge-abo-doc-case input {
    width: 24px;
    height: 24px;
  }

  .ge-abo-btn { min-height: 52px; }
  .ge-abo-btn-retour { min-height: var(--ge-abo-tap); }

  /* Le texte des documents se fait défiler au doigt : plus de hauteur, et
     une inertie qui ne remonte pas à la page. */
  .ge-abo-doc-texte { max-height: 46vh; }
}

/* ── Mobile ≤ 480 px ── */
@media (max-width: 480px) {
  .ge-abo { font-size: 16px; }
  .ge-abo-titre { font-size: 21px; }
  /* Le bouton principal en haut de la pile : c'est celui qu'on cherche. */
  .ge-abo-actions { flex-direction: column-reverse; }
  .ge-abo-btn { width: 100%; }
  .ge-abo-actions > .ge-abo-btn-principal { margin-left: 0; }

  /* Le passage à une colonne est déjà acquis depuis 767 px : ici on ne
     règle plus que la respiration intérieure des cartes. */
  .ge-abo-formules.est-seule .ge-abo-formule { padding: 22px; }
  .ge-abo .ge-abo-activite { min-height: 0; padding: 22px 20px; }
  .ge-abo-prix-n { font-size: 23px; }

  /* Stepper compacté : le numéro seul, sur une seule ligne, jamais tronqué.
     Le libellé reste dans le flux d'accessibilité. */
  .ge-abo-fil {
    flex-wrap: nowrap;
    gap: 5px;
  }

  .ge-abo-fil-item {
    flex: 1 1 0;
    justify-content: center;
    min-width: 0;
    padding: 8px 4px;
  }

  /* On masque, on ne tronque pas : une ellipse au milieu d'un libellé de
     cinq lettres ne dit plus rien. Le texte reste dans le flux
     d'accessibilité — c'est le vocabulaire de .ge-abo-lecture-seule. */
  .ge-abo-fil-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /* …sauf celui de l'étape courante : c'est la seule chose que la personne
     a besoin de lire à cette largeur, et son item prend la place qu'il faut
     pendant que les autres se réduisent à leur pastille. */
  .ge-abo-fil-item.est-actif { flex: 4 1 auto; }

  .ge-abo-fil-item.est-actif .ge-abo-fil-label {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    clip: auto;
    clip-path: none;
    font-size: 12px;
  }

  /* La ligne de variante passe à deux colonnes : le rond garde la sienne,
     le prix descend sous le texte plutôt que de le comprimer à trente
     pixels. Aligné à gauche, sous ce qu'il tarife. */
  .ge-abo-variante-corps { grid-template-columns: auto minmax(0, 1fr); }

  .ge-abo-variante-corps .ge-abo-prix {
    grid-column: 2;
    align-items: flex-start;
    justify-self: start;
    text-align: left;
  }

  .ge-abo-doc-texte { max-height: 200px; }

  /* Champs pleine largeur, frappe confortable. */
  .ge-abo-champ input { min-height: 48px; }

  /* Signature au doigt : toute la largeur, et de la hauteur pour le geste. */
  .ge-abo-canvas { height: 260px; }
}

/* Le total collant a laissé la place à la barre fixée (voir plus haut) :
   elle est hors du flux, ne pousse plus rien, et son bouton « Continuer »
   évite d'avoir à remonter l'écran pour avancer. */

/* En paysage sur tablette ou mobile, la hauteur utile est rare. */
@media (max-height: 520px) {
  .ge-abo-canvas { height: 180px; }
  .ge-abo-doc-texte { max-height: 40vh; }
}

/* Mouvement réduit : aucune animation, et surtout aucun contenu laissé
   invisible en attendant une transition qui ne viendra pas. */
@media (prefers-reduced-motion: reduce) {
  .ge-abo-btn-retour:hover,
  .ge-abo-btn-retour:focus-visible { transform: none; }

  .ge-abo *,
  .ge-abo *::before,
  .ge-abo *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    /* Les délais aussi, et c'est le point le plus important ici : une
       animation « both » retardée d'une seconde laisse son élément à son
       image de départ — donc invisible — pendant toute la durée du délai.
       Sans cette ligne, l'écran de confirmation restait vide une seconde et
       demie pour qui a demandé moins d'animations. */
    animation-delay: 0s !important;
    transition-duration: .001ms !important;
    transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }

  .ge-abo.js-anime [data-apparait],
  .ge-abo.js-anime [data-apparait].est-apparu {
    opacity: 1;
    transform: none;
  }

  .ge-abo-variante:hover .ge-abo-variante-corps,
  .ge-abo .ge-abo-activite:hover,
  .ge-abo-btn:hover:not(:disabled) { transform: none; }

  .ge-abo-etape.sort { opacity: 1; transform: none; }
}

/* ── Formule indisponible ─────────────────────────── */

/* Le prélèvement n'est pas configuré : la carte reste lisible mais se
   retire visiblement du choix, plutôt que de disparaître sans explication. */
.ge-abo-formule.est-bloquee { cursor: not-allowed; }

.ge-abo-formule.est-bloquee {
  border-style: dashed;
  background: var(--ge-abo-creme);
  opacity: .72;
}

.ge-abo-formule.est-bloquee .ge-abo-variante { cursor: not-allowed; }

.ge-abo-formule-indispo {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--ge-abo-bord);
  font-size: 12px;
  font-weight: 700;
  line-height: 1.45;
  color: var(--ge-abo-danger);
}

/* ── Bandeau de l'offre d'un jour ──
   Rendu par PHP le seul jour où il a un sens : il n'y a rien à masquer ni à
   basculer ici, et donc aucun état à maintenir. Le vert sur l'orange plutôt
   que du blanc : c'est le couple déjà utilisé par les badges et les tags, et
   le contraste tient sans avoir à forcer la graisse. */
.ge-abo-flash {
  margin: 0 0 14px;
  padding: 10px 14px;
  border-radius: 8px;
  background: var(--ge-abo-orange);
  font-size: 14px;
  font-weight: 700;
  line-height: 1.45;
  color: var(--ge-abo-vert);
}

/* ── Déjà membre Gallery (tarif membre sans ligne Membre) ──
   Option du chemin « imposé », message de blocage tant que rien n'est
   choisi, et rappel à l'étape de signature. */
.ge-abo-variante-deja .ge-abo-variante-statut { font-weight: 700; }
.ge-abo-membre-requis {
  margin: 12px 0 0;
  padding: 10px 14px;
  border-radius: var(--ge-abo-rayon);
  border: 1.5px solid var(--ge-abo-orange);
  background: #fff4ec;
  color: var(--ge-abo-encre);
  font-weight: 700;
}
.ge-abo-btn.est-bloque { opacity: .55; cursor: not-allowed; }
.ge-abo-btn.est-bloque:hover { transform: none; }
.ge-abo-deja-membre {
  margin: 0 0 18px;
  padding: 12px 14px;
  border-radius: var(--ge-abo-rayon);
  background: var(--ge-abo-creme);
  border-left: 4px solid var(--ge-abo-orange);
  line-height: 1.5;
}

/* ══════════════════════════════════════════════════
   Mandat Stripe (saisie Doinsport active) : texte d'acceptation sous l'IBAN
══════════════════════════════════════════════════ */
.ge-abo-mandat-stripe { font-size: 12px; line-height: 1.5; }
