/* ══════════════════════════════════════════════════════════════════════════
   TRULY FLUENT ACADEMY — COUCHE DE DESIGN
   Chargée en dernier. Elle ne remplace rien : elle re-proportionne.

   POURQUOI ELLE EXISTE
   Le site descend d'une maquette dessinée pour être vue en petit, dans un
   cadre de navigateur factice. D'où des corps de 13 px, des marges de 44 px,
   des rayons de 7 px. Affiché en vrai, en plein écran, cela donne un site
   dense et plat : tout se vaut, rien ne respire, l'œil n'a pas de point
   d'entrée. C'est exactement ce qui rendait la page monotone.

   CE QUE CETTE COUCHE CHANGE
   Une échelle. Le corps de texte, les titres, les blancs, les rayons et les
   ombres sont redéfinis ensemble, en proportion les uns des autres, de sorte
   que la hiérarchie se voie sans lire.

   LES VALEURS DE MOUVEMENT
   Relevées dans la feuille de style de production d'apple.com
   (ac-globalnav.built.css) : la courbe de signature est
   cubic-bezier(.32,.08,.24,1) — une sortie longue et très amortie — et les
   durées réelles se rangent en trois familles : 0,10-0,22 s pour un état
   (survol, appui), 0,28-0,44 s pour une apparition, 0,56-0,64 s pour
   l'ouverture d'une grande surface. Ce fichier n'utilise que ces trois
   familles. Une durée choisie au hasard se remarque ; une durée cohérente
   avec toutes les autres devient invisible, et c'est le but.
   ══════════════════════════════════════════════════════════════════════════ */

:root {
  /* ── mouvement ───────────────────────────────────────────────────────── */
  --e:      cubic-bezier(.32, .08, .24, 1);    /* la signature : sortie amortie */
  --e-in:   cubic-bezier(.8, 0, .68, .92);     /* disparitions */
  --e-soft: cubic-bezier(.04, .04, .12, .96);  /* glissements longs */
  --t-etat: .18s;    /* survol, appui */
  --t-app:  .36s;    /* apparition d'un élément */
  --t-gros: .6s;     /* ouverture d'une grande surface */

  /* ── rayons ──────────────────────────────────────────────────────────────
     Le logo est fait de deux bulles : sa géométrie est ronde, jamais
     anguleuse. Les rayons suivent donc une progression franche, et les
     grandes surfaces vont beaucoup plus loin que les 10-15 px d'origine. */
  --r-xs: 10px;
  --r-s:  14px;
  --r-m:  20px;
  --r-l:  28px;
  --r-xl: 38px;
  --r-pill: 999px;

  /* ── rythme vertical ─────────────────────────────────────────────────────
     Une seule variable commande l'air de tout le site. */
  --sect: clamp(64px, 8.5vw, 128px);   /* entre deux sections */
  --gout: clamp(20px, 4vw, 34px);      /* marge latérale */
  --gap:  clamp(16px, 2vw, 26px);      /* entre deux cartes */

  /* ── ombres ──────────────────────────────────────────────────────────────
     Deux couches : un contact net et très court, une diffusion large et
     faible. Une ombre unique paraît toujours collée ou toujours flottante ;
     deux couches donnent la profondeur sans lourdeur. */
  --sh:  0 1px 2px rgba(9,25,50,.06), 0 6px 20px -8px rgba(9,25,50,.14);
  --sh2: 0 2px 4px rgba(9,25,50,.07), 0 22px 48px -18px rgba(9,25,50,.28);
  --sh3: 0 3px 6px rgba(9,25,50,.09), 0 44px 90px -30px rgba(9,25,50,.42);
}

body.dark {
  --sh:  0 1px 2px rgba(0,0,0,.4),  0 8px 24px -10px rgba(0,0,0,.6);
  --sh2: 0 2px 6px rgba(0,0,0,.5),  0 26px 56px -20px rgba(0,0,0,.72);
  --sh3: 0 4px 10px rgba(0,0,0,.55), 0 50px 100px -32px rgba(0,0,0,.85);
}


/* ══════════════════════════════════════════════════════════════════════════
   1. TYPOGRAPHIE

   `Inter` était en tête de la pile sans jamais être chargée : aucune @font-face,
   aucun lien vers un fournisseur. Le navigateur passait donc silencieusement
   au suivant. On assume : -apple-system en tête, ce qui donne SF Pro sur les
   appareils Apple — le caractère même dont s'inspire la demande — et une
   fonte système propre partout ailleurs, sans une seule requête réseau.
   ══════════════════════════════════════════════════════════════════════════ */
body,
.site {
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI Variable Text",
               "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: 16px;
  line-height: 1.62;
  letter-spacing: -.006em;
  text-rendering: optimizeLegibility;
}

/* Les titres serrent leur interlettrage à mesure qu'ils grossissent : sans
   cela un grand titre paraît toujours délavé. `text-wrap: balance` égalise
   les lignes ; non supporté, il est simplement ignoré. */
.site h1, .site h2, .site h3, .site h4,
.site .atitre, .site h2.t, .site h2.sh {
  text-wrap: balance;
  letter-spacing: -.028em;
  line-height: 1.06;
}
.site p, .site li { text-wrap: pretty; }

.site h2.sh   { font-size: clamp(30px, 3.6vw, 50px); font-weight: 800; margin: 0 0 14px; }
.site h2.t    { font-size: clamp(30px, 3.6vw, 50px); font-weight: 800; margin: 0 0 14px; }
.site h3      { letter-spacing: -.022em; }
.site .card h3{ font-size: 19px; font-weight: 700; margin: 0 0 8px; line-height: 1.25; }

.site p.lead, .site p.lead2 {
  font-size: clamp(16.5px, 1.35vw, 20px);
  line-height: 1.58;
  color: var(--soft);
  /* 60 ch laissait la moitié droite du cadre vide sur un écran large, ce que
     les annotations pointaient comme « negative space ». 70 ch reste dans la
     fourchette lisible (45-75) et remplit la largeur gagnée. */
  max-width: 70ch;
  margin: 0 0 clamp(28px, 3.4vw, 48px);
  letter-spacing: -.011em;
}
.site .card p { font-size: 15px; line-height: 1.6; }

/* Le sur-titre : plus petit, plus espacé, plus discret. Il annonce, il ne
   crie pas. Une pastille le précède — c'est ce qui accroche l'œil, pas la
   capitale rouge. */
.site .eyebrow, .site .sur {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 11.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--crimson);
  margin-bottom: 16px;
}
.site .eyebrow::before, .site .sur::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; flex: 0 0 6px;
  box-shadow: 0 0 0 4px color-mix(in srgb, currentColor 18%, transparent);
}
body.dark .site .eyebrow, body.dark .site .sur { color: #FF7E9C; }

.site .mini { font-size: 13px; line-height: 1.55; }


/* ══════════════════════════════════════════════════════════════════════════
   2. RYTHME — c'est le changement le plus important du fichier

   Les sections passaient toutes à 44 px de haut, quelle que soit leur
   importance : impossible de savoir où commence une idée et où finit la
   précédente. Elles respirent maintenant proportionnellement à l'écran, et
   la largeur utile passe de 1000 à 1160 px.
   ══════════════════════════════════════════════════════════════════════════ */
.site section.blk { padding: var(--sect) var(--gout); }
.site .in         { max-width: 1160px; }
.site .blk2       { padding: var(--sect) var(--gout); max-width: 1160px; }

/* Deux sections voisines n'ont pas besoin de deux fois l'air : sans cette
   règle on obtient un trou de 256 px au milieu de la page.
   ET LE CHANGEMENT DE FOND NON PLUS. Je rendais le rembourrage complet à la
   section qui suit un fond différent : additionné au rembourrage bas de la
   précédente, cela faisait 244 px de vide mesurés entre « Le livre et l'écran »
   et « La collection ». Or quand le fond change, la séparation est déjà faite
   par la couleur — le blanc n'a plus à la refaire. Un tiers suffit. */
.site section.blk + section.blk { padding-top: 0; }
.site section.blk.wt + section.blk:not(.wt),
.site section.blk:not(.wt) + section.blk.wt { padding-top: calc(var(--sect) * .34); }

.site .grid2, .site .grid3, .site .grid4 { gap: var(--gap); }

/* PIÈGE ÉVITÉ DE JUSTESSE — et déjà rencontré sur ce projet.
   `minmax(0, 1fr)` vaut mieux que `1fr`, qui refuse de descendre sous la
   largeur de son contenu. Mais écrit ici en `.site .grid3` (spécificité 0,2,0),
   il écrasait le repli d'assets/grilles_mobile.css, écrit en `.grid3` (0,1,0)
   à l'intérieur d'une media query — et une media query n'ajoute aucune
   spécificité. Résultat mesuré : trois colonnes maintenues à 320 px, du texte
   à une lettre par ligne sur cinq pages et jusqu'à 169 px de débordement.
   La correction n'est donc pas d'ajouter !important, c'est de ne parler que
   là où le repli n'a pas cours : au-dessus de 1 000 px. */
@media (min-width: 1001px) {
  .site .grid3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .site .grid2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .site .grid4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}


/* ══════════════════════════════════════════════════════════════════════════
   3. LES BOUTONS

   Ils étaient à 8 px de haut sur 17 de large : au doigt comme à la souris,
   c'est une cible étroite, et à l'œil c'est un bouton timide. Ils deviennent
   des pastilles pleines, et leur survol fait trois choses en même temps —
   ombre qui gonfle, très légère montée, fond qui s'éclaircit — pendant
   0,18 s. Un seul de ces trois effets se remarque ; les trois ensemble ne se
   remarquent pas, ils se ressentent.
   ══════════════════════════════════════════════════════════════════════════ */
.site .btn, .site .hbtn {
  border-radius: var(--r-pill);
  padding: 12px 24px;
  font-size: 15px; font-weight: 600; letter-spacing: -.008em;
  transition: transform var(--t-etat) var(--e),
              box-shadow var(--t-etat) var(--e),
              background-color var(--t-etat) var(--e),
              border-color var(--t-etat) var(--e),
              color var(--t-etat) var(--e);
  will-change: auto;
}
.site .btn.sm { padding: 9px 18px; font-size: 13.5px; }
.site .btn.lg { padding: 15px 32px; font-size: 16.5px; }

.site .btn:hover  { transform: translateY(-1.5px); box-shadow: 0 10px 26px -10px rgba(192,9,58,.6); }
.site .btn:active { transform: translateY(0) scale(.985); transition-duration: .08s; }
.site .btn.nv:hover { box-shadow: 0 10px 26px -10px rgba(9,45,89,.75); }
.site .btn.gh {
  border-width: 1px;
  border-color: color-mix(in srgb, currentColor 26%, transparent);
  background: transparent;
}
.site .btn.gh:hover {
  background: color-mix(in srgb, currentColor 8%, transparent);
  border-color: color-mix(in srgb, currentColor 45%, transparent);
  box-shadow: none;
}

/* L'anneau de mise au point : il ne doit apparaître qu'au clavier, jamais
   après un clic de souris — d'où :focus-visible et non :focus. */
.site a:focus-visible, .site button:focus-visible,
.site input:focus-visible, .site select:focus-visible, .site [tabindex]:focus-visible {
  outline: 2px solid var(--crimson);
  outline-offset: 3px;
  border-radius: var(--r-xs);
}


/* ══════════════════════════════════════════════════════════════════════════
   4. LES CARTES — et le projecteur qui suit le curseur

   `--mx` / `--my` sont écrits par anim.js : la position du curseur À
   L'INTÉRIEUR de la carte, en pourcentage. Une seule lueur radiale suit le
   pointeur. C'est très peu de chose, et c'est ce qui fait qu'une grille de
   cartes cesse d'être une grille de rectangles.
   ══════════════════════════════════════════════════════════════════════════ */
.site .card {
  border-radius: var(--r-m);
  padding: clamp(22px, 2.4vw, 32px);
  box-shadow: var(--sh);
  position: relative;
  isolation: isolate;
  transition: transform var(--t-app) var(--e),
              box-shadow var(--t-app) var(--e),
              border-color var(--t-app) var(--e);
}
.site .card::before {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--t-app) var(--e);
  background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 0%),
              color-mix(in srgb, var(--crimson) 13%, transparent), transparent 62%);
}
.site .card:hover {
  transform: translateY(-4px);
  box-shadow: var(--sh2);
  border-color: color-mix(in srgb, var(--crimson) 32%, var(--brd));
}
.site .card:hover::before { opacity: 1; }

/* La carte « la plus choisie » : l'étiquette devient une pastille posée,
   pas une écharpe collée au bord. */
.site .card.hi { border-width: 1px; box-shadow: var(--sh2); }
.site .card.hi::after {
  top: -12px; right: 22px;
  font-size: 10.5px; letter-spacing: .1em; font-weight: 700;
  padding: 5px 13px; border-radius: var(--r-pill);
  box-shadow: 0 6px 18px -6px rgba(192,9,58,.8);
}
.site .card .ico {
  width: 46px; height: 46px; border-radius: var(--r-s);
  font-size: 21px; margin-bottom: 16px;
  transition: transform var(--t-app) var(--e);
}
.site .card:hover .ico { transform: scale(1.06) rotate(-3deg); }


/* ══════════════════════════════════════════════════════════════════════════
   5. LA BARRE DU HAUT

   Elle devient une lame de verre : fine, translucide, floutée. Le flou coûte
   un repeint à chaque défilement — on le limite donc à une bande basse, avec
   un fond opaque de repli là où backdrop-filter n'existe pas.
   ══════════════════════════════════════════════════════════════════════════ */
.site .topnav, body.dark .site .topnav,
.site .topnav.collante, body.dark .site .topnav.collante {
  padding: 13px var(--gout);
  gap: 20px;
  border-bottom: 1px solid transparent;
  transition: background-color var(--t-app) var(--e),
              border-color var(--t-app) var(--e),
              backdrop-filter var(--t-app) var(--e),
              box-shadow var(--t-app) var(--e);
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .site .topnav.collante { background: color-mix(in srgb, var(--paper) 72%, transparent); }
  body.dark .site .topnav.collante { background: rgba(9,13,20,.62); }
  /* posé par anim.js dès que la page a défilé */
  /* 20 px de flou et 170 % de saturation coûtaient, à la mesure, 7 images
     longues sur un défilement de 4 000 px contre 2 sans le verre : chaque
     image redessine la bande floutée. À 14 px l'effet est le même à l'œil et
     la dépense retombe. */
  .site .topnav.collante.pose {
    -webkit-backdrop-filter: blur(14px) saturate(140%);
            backdrop-filter: blur(14px) saturate(140%);
    border-bottom-color: color-mix(in srgb, var(--ink) 9%, transparent);
  }
  body.dark .site .topnav.collante.pose {
    background: rgba(9,13,20,.78);
    border-bottom-color: rgba(255,255,255,.08);
  }
}
.site .topnav .lk {
  font-size: 14px; font-weight: 500;
  padding: 7px 2px; position: relative;
  transition: color var(--t-etat) var(--e);
}
/* Le trait sous le lien pousse depuis le centre. Il est en transform, donc
   composé par le GPU : aucun recalcul de mise en page au survol. */
.site .topnav .lk::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 1px; height: 1.5px;
  background: currentColor; border-radius: 2px;
  transform: scaleX(0); transform-origin: 50% 50%;
  transition: transform var(--t-etat) var(--e);
}
.site .topnav .lk:hover::after, .site .topnav .lk.act::after { transform: scaleX(1); }
.site .topnav .lg { transition: transform var(--t-etat) var(--e); }
.site .topnav .lg:hover { transform: scale(1.03); }
.site .tiroirbtn { border-radius: var(--r-xs); width: 40px; height: 38px;
  transition: background-color var(--t-etat) var(--e), border-color var(--t-etat) var(--e); }


/* ══════════════════════════════════════════════════════════════════════════
   6. LE GRAND BANDEAU D'ACCUEIL
   ══════════════════════════════════════════════════════════════════════════ */
.site .hero { padding: clamp(72px, 9vw, 148px) var(--gout) clamp(76px, 9vw, 152px); }
.site .heroin { max-width: 1160px; gap: clamp(32px, 5vw, 72px); }
.site .hero h1 {
  font-size: clamp(38px, 5.6vw, 78px);
  letter-spacing: -.035em; line-height: 1.02; font-weight: 800;
  margin: 0 0 20px;
}
.site .hero p { font-size: clamp(17px, 1.5vw, 21px); max-width: 30ch; margin-bottom: 32px; line-height: 1.5; }
.site .hero .kick {
  border-radius: var(--r-pill); padding: 7px 16px; font-size: 12px; letter-spacing: .09em;
  margin-bottom: 22px;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
.site .hero .cta { gap: 14px; }


/* ══════════════════════════════════════════════════════════════════════════
   7. PROFONDEUR — la parallaxe

   Les décors de fond (les bulles en filigrane, les lueurs) sont poussés loin
   sur l'axe Z.

   DEUX MESURES, PAS UNE — et c'est le point où l'on se trompe
   Une couche FIXE (le filigrane du site, épinglé au viewport) ne bouge pas du
   tout : elle est à l'infini. Pour la rapprocher, il faut la faire remonter
   un peu à mesure qu'on descend. Elle lit `--sy`, le défilement global.
   Une couche EN FLUX (les lueurs du bandeau) monte déjà à pleine vitesse avec
   la page. Pour l'éloigner, il faut la faire redescendre — la retenir. Elle
   lit `--py`, calculé par rapport à sa propre position dans l'écran.
   Appliquer la même formule aux deux donne l'inverse de l'effet voulu sur
   l'une des deux : le fond « colle » au curseur au lieu de traîner.

   Tout passe par translate3d et opacity, les deux seules propriétés que le
   compositeur sait animer sans repasser par la mise en page.
   ══════════════════════════════════════════════════════════════════════════ */
.site [data-prof] {
  transform: translate3d(0, var(--py, 0px), 0);
}

/* le filigrane général du site : la couche la plus lointaine */
body::before {
  transform: translate3d(0, calc(var(--sy, 0px) * .11), 0) scale(1.12);
  transition: opacity var(--t-gros) var(--e);
}

/* Les lueurs du bandeau d'accueil deviennent deux vraies couches, l'une
   derrière l'autre, qui dérivent à des vitesses différentes. */
.site .hero { isolation: isolate; }
.site .hero .lueur {
  position: absolute; border-radius: 50%; pointer-events: none; z-index: 0;
}
/* Pas de `filter: blur()` ici : un dégradé radial dont la transparence part
   du centre EST déjà un flou, et il ne coûte rien. Le filtre par-dessus
   refaisait le travail une seconde fois, en rasterisant à chaque image une
   surface de 900 px de côté — 4 images longues de plus sur un défilement. */
.site .hero .lueur.a {
  left: -8%; top: -18%; width: 62%; aspect-ratio: 1;
  background: radial-gradient(circle closest-side,
              rgba(41,102,178,.55) 0%, rgba(41,102,178,.28) 42%,
              rgba(41,102,178,.08) 68%, transparent 100%);
}
.site .hero .lueur.b {
  right: -12%; bottom: -28%; width: 58%; aspect-ratio: 1;
  background: radial-gradient(circle closest-side,
              rgba(178,20,64,.46) 0%, rgba(178,20,64,.22) 44%,
              rgba(178,20,64,.06) 70%, transparent 100%);
}
.site .heroin { z-index: 2; }


/* ══════════════════════════════════════════════════════════════════════════
   8. APPARITIONS AU DÉFILEMENT

   État de départ posé par anim.js, et par lui seul : si le script ne tourne
   pas — vieux navigateur, erreur JS, robot d'indexation — la classe n'est
   jamais posée et tout reste visible. Une apparition au défilement écrite
   en CSS pur cache le contenu à ceux qui n'ont pas le script. Jamais.
   ══════════════════════════════════════════════════════════════════════════ */
.site .vient {
  opacity: 0;
  transform: translate3d(0, 24px, 0);
  transition: opacity .6s var(--e), transform .6s var(--e);
  transition-delay: var(--retard, 0ms);
}
.site .vient.la { opacity: 1; transform: none; }


/* ══════════════════════════════════════════════════════════════════════════
   9. LA COLLECTION — présenter des livres comme des objets

   CE QUI N'ALLAIT PAS
   Chaque manuel était une carte : un cadre blanc, une bordure, et sous la
   couverture une ligne « 10,00 € · Disponible ». On regardait des fiches
   produit, pas des livres.

   CE QUI CHANGE
   Le cadre disparaît. Il ne reste que la couverture, posée sur la page, avec
   son ombre. Trois détails la font exister comme objet :
     · une tranche — un liseré sombre le long du bord gauche, comme le dos
       d'un livre vu de face ;
     · une lumière — une bande claire qui traverse la couverture en diagonale
       au survol, une fois, comme un reflet quand on incline un livre ;
     · une ombre qui s'ouvre en même temps que le livre monte, ce qui donne
       la hauteur. Sans elle, la montée ressemble à un glissement.

   Le prix ne figure plus ici : sur une planche de cinq couvertures, cinq
   étiquettes de prix transforment une collection en rayonnage de supermarché.
   Il reste sur la page Tarifs, où on le cherche vraiment.
   ══════════════════════════════════════════════════════════════════════════ */
.site .books { gap: clamp(16px, 2.2vw, 30px); }

.site .book {
  background: transparent !important;
  border: 0 !important;
  border-radius: 0;
  overflow: visible;
  box-shadow: none !important;
  transition: transform var(--t-app) var(--e);
}
.site .book .cv {
  border-radius: var(--r-s);
  overflow: hidden;
  box-shadow: var(--sh2);
  transform-origin: 50% 100%;
  transition: transform var(--t-app) var(--e), box-shadow var(--t-app) var(--e);
  isolation: isolate;
}
/* la tranche */
.site .book .cv::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 9px; z-index: 3;
  pointer-events: none;
  background: linear-gradient(90deg,
              rgba(0,0,0,.42) 0%, rgba(0,0,0,.14) 46%,
              rgba(255,255,255,.16) 72%, rgba(255,255,255,0) 100%);
}
/* le reflet — hors cadre au repos, il traverse au survol */
.site .book .refl {
  position: absolute; inset: -40% -60%; z-index: 4; pointer-events: none;
  background: linear-gradient(102deg, transparent 38%,
              rgba(255,255,255,.30) 48%, rgba(255,255,255,.06) 55%, transparent 62%);
  transform: translate3d(-130%, 0, 0);
  opacity: 0;
}
.site .book:hover .cv    { transform: translateY(-12px) scale(1.022); box-shadow: var(--sh3); }
.site .book:hover .refl  { animation: tfa-reflet .78s var(--e) forwards; }
@keyframes tfa-reflet {
  0%   { transform: translate3d(-130%, 0, 0); opacity: 0; }
  18%  { opacity: 1; }
  100% { transform: translate3d(130%, 0, 0);  opacity: 0; }
}

/* la pastille de niveau : elle avance légèrement avec la couverture */
.site .book .cv .lvl {
  top: 12px; right: 12px;
  border-radius: var(--r-pill); padding: 4px 11px; font-size: 10.5px; letter-spacing: .06em;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  transition: transform var(--t-app) var(--e);
}
.site .book:hover .cv .lvl { transform: translateY(-2px); }

/* la légende : elle ne porte plus le prix, seulement l'état, et elle
   s'éclaircit quand on survole le livre */
.site .book .ft {
  padding: 14px 2px 0;
  display: block;
  font-size: 13.5px;
  color: var(--soft2);
  transition: color var(--t-etat) var(--e);
  border: 0 !important;
}
.site .book .ft b  { display: block; font-size: 15px; font-weight: 600; color: var(--ink);
                     letter-spacing: -.015em; transition: color var(--t-etat) var(--e); }
.site .book .ft em { font-style: normal; font-size: 13px; color: var(--soft2); }
.site .book:hover .ft b { color: var(--crimson); }
body.dark .site .book .ft b { color: #EAF1FA; }
body.dark .site .book:hover .ft b { color: #FF7E9C; }

/* les manuels encore à écrire : sourds, mais pas éteints */
.site .book.a-venir .cv { box-shadow: var(--sh); }
.site .book.a-venir:hover .cv { transform: translateY(-6px); box-shadow: var(--sh2); }
.site .book .cv .bientot {
  bottom: 14px; border-radius: var(--r-pill); padding: 4px 12px; font-size: 10px;
  letter-spacing: .12em;
}


/* ══════════════════════════════════════════════════════════════════════════
   10. TFA TV — LES VIGNETTES

   L'ancien survol ouvrait une fausse fenêtre de lecture : badge « APERÇU »
   clignotant, minuteur qui défilait, barre de progression qui avançait. Rien
   de tout cela n'était vrai, et un intervalle JavaScript tournait à chaque
   passage de souris.

   Le nouveau survol dit la vérité et tient en trois gestes : la vignette
   grandit un peu, son image s'éloigne en se floutant, et le titre monte
   par-dessus. Le flou n'est pas décoratif — c'est lui qui rend le texte
   lisible sans avoir à poser un rectangle noir.
   ══════════════════════════════════════════════════════════════════════════ */
.site .tvtrack { gap: clamp(12px, 1.4vw, 20px); padding: 38px var(--gout); }
.site .tvcard  { transition: transform var(--t-app) var(--e); }
.site .tvcard .thumb {
  border-radius: var(--r-s);
  box-shadow: 0 4px 16px rgba(0,0,0,.42);
  transition: box-shadow var(--t-app) var(--e);
}
.site .tvcard .thumb .g {
  transition: transform var(--t-gros) var(--e), filter var(--t-app) var(--e);
}
.site .tvcard.hot { transform: scale(1.09); z-index: 30; }
.site .tvcard.hot .thumb { box-shadow: 0 22px 54px -12px rgba(0,0,0,.72); }
.site .tvcard.hot .thumb .g { transform: scale(1.1); filter: blur(7px) brightness(.62) saturate(1.15); }

/* la couche d'information : elle n'existe qu'au survol */
.site .tvcard .ov {
  position: absolute; inset: 0; z-index: 4;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: 13px 14px;
  opacity: 0;
  transition: opacity var(--t-app) var(--e);
  pointer-events: none;
}
.site .tvcard.hot .ov { opacity: 1; }
.site .tvcard .ov b {
  display: block; font-size: 13.5px; font-weight: 600; color: #fff; line-height: 1.3;
  letter-spacing: -.012em;
  transform: translate3d(0, 10px, 0);
  transition: transform var(--t-app) var(--e);
  text-shadow: 0 1px 12px rgba(0,0,0,.6);
}
.site .tvcard .ov small {
  display: block; font-size: 11.5px; color: #C9DAEF; margin-top: 3px;
  transform: translate3d(0, 14px, 0);
  transition: transform var(--t-app) var(--e);
}
.site .tvcard.hot .ov b, .site .tvcard.hot .ov small { transform: none; }
/* le rond de lecture, au centre */
.site .tvcard .ov .pl {
  position: absolute; left: 50%; top: 46%; width: 46px; height: 46px; margin: -23px 0 0 -23px;
  border-radius: 50%; display: grid; place-items: center;
  background: rgba(255,255,255,.94); color: #08152A; font-size: 14px; padding-left: 3px;
  transform: scale(.7); opacity: 0;
  transition: transform var(--t-app) var(--e), opacity var(--t-app) var(--e);
  box-shadow: 0 8px 24px rgba(0,0,0,.4);
}
.site .tvcard.hot .ov .pl { transform: scale(1); opacity: 1; }

/* on efface l'ancienne fenêtre : elle n'est plus construite, cette règle
   protège seulement d'un cache de page ancienne */
.site .tvcard .pop { display: none !important; }

/* l'étiquette Premium : un filet, du verre, et un léger recul au survol —
   plus le petit cartouche opaque d'avant */
.site .prem {
  top: 10px; right: 10px;
  font-size: 9px; letter-spacing: .16em; font-weight: 700;
  border-radius: var(--r-pill); padding: 4px 11px;
  border: 1px solid rgba(255,255,255,.42);
  background: rgba(10,18,34,.28);
  -webkit-backdrop-filter: blur(8px) saturate(150%); backdrop-filter: blur(8px) saturate(150%);
  transition: transform var(--t-app) var(--e), opacity var(--t-app) var(--e),
              border-color var(--t-app) var(--e);
}
.site .tvcard.hot .prem {
  transform: translateY(-3px);
  border-color: rgba(255,255,255,.75);
}
.site .tvcard .thumb .lk2, .site .tvcard .thumb .dur {
  border-radius: var(--r-pill); padding: 3px 9px; font-size: 10px; letter-spacing: .04em;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  transition: opacity var(--t-app) var(--e);
}
.site .tvcard.hot .thumb .lk2, .site .tvcard.hot .thumb .dur { opacity: 0; }
.site .tvcard .cap { font-size: 13px; margin-top: 11px; font-weight: 500;
  transition: color var(--t-etat) var(--e); }
.site .tvcard.hot .cap { color: #fff; }

/* les filtres : ils étaient trop serrés pour le doigt comme pour l'œil */
.site .tvchip {
  padding: 9px 18px; border-radius: var(--r-pill); font-size: 13px; font-weight: 500;
  transition: background-color var(--t-etat) var(--e), color var(--t-etat) var(--e),
              border-color var(--t-etat) var(--e), transform var(--t-etat) var(--e);
}
.site .tvchip:hover { transform: translateY(-1px); }
.site .tvrangee, .site .tvrow h3 { font-size: clamp(17px, 1.6vw, 22px); margin: 30px 0 6px; }
.site .tvnav { border-radius: var(--r-s); width: 44px; }


/* ══════════════════════════════════════════════════════════════════════════
   11. LES DEUX BANDES BEIGES

   « Vous cherchez plutôt des cours pour votre enfant ? » et « Vous êtes
   professeur d'anglais ? » étaient peintes en sable et rose poudré —
   #F2E9DB / #E7DAC3 — héritées d'une palette qui n'a jamais été celle de la
   marque. En thème sombre elles viraient au brun (#2B2318), une couleur qui
   n'existe nulle part ailleurs sur le site. Elles reviennent dans le bleu
   nuit maison, chacune avec sa lueur : violette pour Kids, bleue pour les
   professeurs, de sorte qu'on les distingue sans sortir de la charte.
   ══════════════════════════════════════════════════════════════════════════ */
.site .kidsband, body.dark .site .kidsband,
.site .b2b,      body.dark .site .b2b {
  border-radius: var(--r-l);
  padding: clamp(26px, 3.2vw, 44px) clamp(24px, 3.4vw, 48px);
  gap: clamp(20px, 2.6vw, 34px);
  position: relative; overflow: hidden; isolation: isolate;
  color: #fff;
  border: 1px solid rgba(255,255,255,.10);
  box-shadow: var(--sh2);
}
.site .kidsband, body.dark .site .kidsband {
  background:
    radial-gradient(120% 150% at 8% 10%, rgba(126,74,196,.55), transparent 58%),
    linear-gradient(118deg, #14213D 0%, #1B2C51 55%, #241A47 100%);
}
.site .b2b, body.dark .site .b2b {
  background:
    radial-gradient(120% 150% at 92% 12%, rgba(31,96,178,.52), transparent 58%),
    linear-gradient(118deg, #0C1E38 0%, #102A4C 58%, #0B2038 100%);
}
.site .kidsband h3, .site .b2b h3,
body.dark .site .kidsband h3, body.dark .site .b2b h3 {
  color: #fff; font-size: clamp(21px, 2.2vw, 30px); font-weight: 700; margin: 0 0 10px;
}
.site .kidsband p, .site .b2b p,
body.dark .site .kidsband p, body.dark .site .b2b p {
  color: #BFD2E9; font-size: clamp(14.5px, 1.15vw, 16.5px); max-width: 62ch; margin: 0;
}
.site .kidsband .kf {
  width: 84px; height: 84px; flex: 0 0 84px; border-radius: var(--r-m); font-size: 34px;
  background: linear-gradient(150deg, #C0093A, #7E4AC4);
  box-shadow: 0 16px 40px -12px rgba(126,74,196,.75);
  transition: transform var(--t-app) var(--e);
}
.site .kidsband:hover .kf { transform: rotate(-6deg) scale(1.05); }
.site .kidsband .badge, .site .b2b .badge {
  background: rgba(255,255,255,.10); border: 1px solid rgba(255,255,255,.17);
  color: #DCE9F8; border-radius: var(--r-pill); padding: 5px 13px; font-size: 12px;
}
.site .kidsband .mini, .site .b2b .mini { color: #9FB6D2; }
.site .kidsband .mini b, .site .b2b .mini b { color: #fff !important; }
/* la bande porte le filigrane des bulles, très en retrait */
.site .kidsband::after, .site .b2b::after {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  right: -70px; bottom: -110px; width: 320px; height: 320px;
  background: var(--bul) center/contain no-repeat; opacity: .06;
  transform: translate3d(0, var(--py, 0px), 0);
}
.site .b2b .btn.nv { background: #fff; color: #0C1E38; }
.site .b2b .btn.nv:hover { background: #fff; box-shadow: 0 12px 30px -10px rgba(255,255,255,.45); }


/* ══════════════════════════════════════════════════════════════════════════
   12. LA BASCULE MENSUEL / ANNUEL

   LE DÉFAUT
   Trois éléments disaient l'état en même temps — l'étiquette « Mensuel », le
   curseur, l'étiquette « Annuel » — et le curseur passait au rouge vif en
   position annuelle. On lisait donc un rouge de marque à côté d'un bouton
   d'appel rouge, et l'œil ne savait plus lequel des trois était l'état
   courant. S'y ajoutait un curseur animé sur `left`, propriété qui déclenche
   un recalcul de position à chaque image.

   LA CORRECTION
   Un seul objet dit l'état : une pastille pleine qui glisse sous l'étiquette
   active, en transform. Les étiquettes ne changent que de couleur. Et
   l'économie annuelle s'affiche enfin, ce qui était l'intérêt du bouton.
   ══════════════════════════════════════════════════════════════════════════ */
.site .billsw {
  position: relative;
  gap: 0; padding: 4px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--ink) 7%, transparent);
  border: 1px solid color-mix(in srgb, var(--ink) 8%, transparent);
  isolation: isolate;
}
body.dark .site .billsw {
  background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.09);
}
/* LA PASTILLE EST MESURÉE, PAS CALCULÉE.
   Elle était posée à `width: calc(50% - 4px)` — or « Mensuel » et « Annuel »
   n'ont pas la même largeur : elle débordait de l'un et flottait dans
   l'autre. Elle utilise maintenant exactement le même élément et le même
   script que le sélecteur Adultes / Enfants, largeur relevée sur l'étiquette
   visée, et glissement au survol en annonce du clic. */
.site .billsw .audpuce {
  position: absolute; flex: none; z-index: 0;
  top: 4px; bottom: 4px; left: 0;
}

/* `:not(.audpuce)` — la pastille est un `span` elle aussi. Sans cette
   exclusion, `.site .billsw span` (spécificité 0,2,1) l'emportait sur
   `.site .audpuce` (0,2,0) : la pastille devenait un troisième élément de la
   rangée, en position relative et sans fond. On la voyait alors flotter à
   gauche de « Mensuel », grise et décalée de 45 px. */
.site .billsw span:not(.audpuce) {
  position: relative; z-index: 1;
  flex: 1 1 50%; text-align: center; white-space: nowrap;
  padding: 9px 22px; border-radius: var(--r-pill);
  font-size: 14px; font-weight: 600; letter-spacing: -.01em;
  background: none !important;
  color: var(--soft);
  transition: color var(--t-app) var(--e);
}
.site .billsw span.on, body.dark .site .billsw span.on { color: #fff; }
.site .billsw:hover span.on { color: var(--soft); }
.site .billsw span:not(.audpuce):hover,
body.dark .site .billsw span:not(.audpuce):hover { color: #fff; }
/* le curseur séparé n'a plus lieu d'être : la pastille EST le curseur */
.site .billsw .knob { display: none !important; }
/* l'économie, annoncée à sa place */
.site .billsw .eco {
  position: absolute; left: 100%; top: 50%; transform: translate(14px, -50%);
  white-space: nowrap; font-size: 12.5px; font-weight: 600; color: var(--green);
  opacity: 0; transition: opacity var(--t-app) var(--e);
  pointer-events: none;
}
body.annual .site .billsw .eco { opacity: 1; }
@media (max-width: 560px) {
  .site .billsw { width: 100%; max-width: 340px; }
  .site .billsw .eco { position: static; display: block; transform: none; margin-top: 8px; text-align: center; }
}


/* ══════════════════════════════════════════════════════════════════════════
   13. LE PIED DE PAGE ET LES RÉSEAUX

   Les réseaux n'étaient qu'une colonne de mots, indistincts des liens de
   navigation d'à côté. Chacun reçoit sa marque et sa couleur officielle. Au
   repos le logo reste sobre — sept logos en couleurs vives dans un pied de
   page, c'est un feu d'artifice ; la couleur ne s'allume qu'au survol.
   ══════════════════════════════════════════════════════════════════════════ */
.site footer.site { padding-top: clamp(48px, 5vw, 76px); }
.site footer.site .cols { gap: clamp(26px, 3vw, 52px); }
.site footer.site h5 {
  font-size: 11.5px; letter-spacing: .14em; font-weight: 700; margin-bottom: 16px;
}
.site footer.site a {
  transition: color var(--t-etat) var(--e), transform var(--t-etat) var(--e);
}
.site footer.site .cols a:not(.res) { display: block; padding: 5px 0; }
.site footer.site .cols a:not(.res):hover { transform: translateX(3px); }

.site footer.site a.res {
  display: flex; align-items: center; gap: 11px;
  padding: 7px 0; font-size: 14px;
}
.site footer.site a.res svg {
  width: 19px; height: 19px; flex: 0 0 19px;
  fill: currentColor; color: var(--soft2);
  transition: color var(--t-app) var(--e), transform var(--t-app) var(--e);
}
.site footer.site a.res:hover svg { color: var(--rc, var(--crimson)); transform: scale(1.16); }
.site footer.site a.res:hover { color: var(--ink); }
body.dark .site footer.site a.res:hover { color: #fff; }
.site footer.site a.res[href=""], .site footer.site a.res:not([href]) { opacity: .45; }
.site footer.site .bot { margin-top: clamp(30px, 3.4vw, 52px); padding-top: 22px; gap: 18px; }


/* ══════════════════════════════════════════════════════════════════════════
   14. DÉTAILS QUI SE VOIENT PARTOUT
   ══════════════════════════════════════════════════════════════════════════ */
.site .badge, .site .puce, .site .tag {
  border-radius: var(--r-pill); padding: 5px 13px; font-size: 12px; letter-spacing: 0;
}
.site .bande, .site .attente, .site .exres, .site .authcarte, .site .authcote,
.site .serie, .site .kpicard, .site .txtblk, .site .ex, .site .illus {
  border-radius: var(--r-m);
}
.site .bande, .site .attente { padding: clamp(26px, 3.2vw, 44px) clamp(24px, 3.4vw, 48px); }
.site .bande h3, .site .attente h3 { font-size: clamp(21px, 2.2vw, 30px); font-weight: 700; }
.site .ain, .site .attente input {
  border-radius: var(--r-s); padding: 13px 16px; font-size: 15px;
  transition: border-color var(--t-etat) var(--e), background-color var(--t-etat) var(--e);
}
.site a.serie { transition: transform var(--t-app) var(--e), box-shadow var(--t-app) var(--e),
                            border-color var(--t-app) var(--e); }
.site a.serie:hover { transform: translateY(-4px); box-shadow: var(--sh2); }
.site .clubkpi > div, .site .kpicard { transition: transform var(--t-app) var(--e); }
.site .clubkpi > div:hover { transform: translateY(-3px); }

/* Les images ne doivent jamais apparaître d'un coup à leur pleine netteté
   pendant que la page se met en place : `decoding=async` est posé par
   anim.js, et un fondu de 0,44 s absorbe l'arrivée. */
.site img[data-fondu] { opacity: 0; transition: opacity .44s var(--e); }
.site img[data-fondu].la { opacity: 1; }


/* ══════════════════════════════════════════════════════════════════════════
   15. MOUVEMENT RÉDUIT — obligation, pas option

   Le critère WCAG 2.3.3 « Animation from Interactions » cite nommément la
   parallaxe. On ne coupe pas les animations à zéro : `animation: none` casse
   les scripts qui attendent un événement `animationend`. On les ramène à une
   durée imperceptible, ce qui déclenche les événements sans produire de
   mouvement. Les fondus d'opacité, eux, restent : ils ne sont pas du
   mouvement et ils aident à comprendre ce qui change.
   ══════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  .site *, .site *::before, .site *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .site .vient { opacity: 1 !important; transform: none !important; }
  .site [data-prof], .site .hero .lueur, body::before,
  .site .kidsband::after, .site .b2b::after { transform: none !important; }
  .site .books.deploie .book { animation: none !important; }
  .site .book:hover .cv, .site .card:hover, .site .tvcard.hot { transform: none !important; }
}


/* ══════════════════════════════════════════════════════════════════════════
   16. L'ÉVENTAIL DE LA COLLECTION

   Sur la page produit d'Apple, la gamme entre à l'écran groupée puis s'ouvre :
   les appareils s'écartent, se redressent et grandissent ensemble. C'est la
   seule animation de la page qui présente plusieurs objets d'un coup, et
   c'est pour cela qu'on la retient.

   Ici les cinq manuels font la même entrée, mais à leur manière : ils
   arrivent penchés vers le centre, comme des livres serrés sur une étagère,
   et se redressent un par un. La rotation de départ est donnée par --rot,
   écrit par anim.js selon la distance au centre de la rangée — le livre du
   milieu ne tourne pas, ceux des extrémités tournent le plus.

   L'animation ne se joue qu'une fois, à la première entrée à l'écran. Rejouée
   à chaque passage, elle deviendrait un tic.
   ══════════════════════════════════════════════════════════════════════════ */
.site .books.deploie .book {
  animation: tfa-eventail .82s var(--e) both;
  animation-delay: calc(var(--i, 0) * 65ms);
}
@keyframes tfa-eventail {
  from {
    opacity: 0;
    transform: translate3d(var(--dx, 0px), 34px, 0) rotate(var(--rot, 0deg)) scale(.9);
  }
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0) rotate(0deg) scale(1);
  }
}


/* ══════════════════════════════════════════════════════════════════════════
   17. LE BANDEAU D'ACCUEIL (page d'accueil)

   Il faisait 470 px de haut, quelle que soit la taille de l'écran : sur un
   grand moniteur c'était une bande, pas une ouverture. Il occupe maintenant
   une part de la hauteur disponible, et son image dérive plus lentement que
   la page — c'est la parallaxe la plus lisible du site, parce que le regard
   s'y trouve déjà.

   DEUX MOUVEMENTS SUR UN SEUL ÉLÉMENT
   L'image répondait déjà à la souris, par un `transform` écrit en ligne
   depuis tfa.js. Ajouter la parallaxe de défilement au même endroit aurait
   fait que le dernier des deux efface l'autre. Les deux écrivent donc chacun
   leur variable, et le transform les additionne ici, une fois pour toutes.
   ══════════════════════════════════════════════════════════════════════════ */
.site .hero2 {
  height: auto;
  min-height: clamp(480px, 76vh, 760px);
  display: grid;
}
.site .hero2 > * { grid-area: 1 / 1; }
.site .hero2 .heroimg {
  transform: translate3d(var(--mx2, 0px), calc(var(--my2, 0px) + var(--py, 0px)), 0) scale(1.07);
  transition: transform .5s var(--e-soft);
}
.site .hero2 .heroctn { padding: clamp(30px, 6vw, 80px) var(--gout); position: relative; }
.site .hero2 .heroctn h1 {
  font-size: clamp(36px, 5.4vw, 76px);
  letter-spacing: -.036em; line-height: 1.02; font-weight: 800;
  max-width: 17ch; margin: 0 0 20px;
}
.site .hero2 .heroctn .kick2 {
  font-size: 11.5px; letter-spacing: .34em; font-weight: 600; margin-bottom: 22px;
}
.site .hero2 .heroctn p {
  font-size: clamp(16.5px, 1.5vw, 21px); max-width: 56ch; line-height: 1.5; margin-bottom: 32px;
}
.site .hero2 .heroctn .cta { gap: 14px; }
.site .hbtn {
  border-radius: var(--r-pill); padding: 14px 30px; font-size: 16px; font-weight: 600;
  letter-spacing: -.01em;
}
.site .hbtn:hover { transform: translateY(-1.5px); }

/* la flèche du bas : elle respire au lieu de sauter */
.site .scrollhint {
  bottom: 26px; font-size: 22px;
  animation: tfa-respire 2.6s var(--e) infinite;
}
@keyframes tfa-respire {
  0%, 100% { transform: translate(-50%, 0);    opacity: .5; }
  50%      { transform: translate(-50%, 8px);  opacity: .95; }
}
@media (prefers-reduced-motion: reduce) {
  .site .scrollhint { animation: none; transform: translateX(-50%); }
}


/* ══════════════════════════════════════════════════════════════════════════
   18. LA BARRE DU HAUT, MESURÉE

   Élargir les boutons et les écarts a fait passer la barre de une à quatre
   lignes : sept liens, un sélecteur, trois appels à l'action et un logo, cela
   fait 1 499 px de contenu pour 1 372 px utiles. Une barre qui se replie sur
   quatre lignes n'est plus une barre.

   Les valeurs ci-dessous sont ajustées à la mesure, pas à l'estime : la barre
   tient sur une ligne jusqu'à 1 300 px, et en dessous les liens rejoignent le
   tiroir ☰ — qui les contient déjà tous. Mieux vaut un menu assumé qu'une
   barre qui déborde.
   ══════════════════════════════════════════════════════════════════════════ */
.site .topnav, body.dark .site .topnav,
.site .topnav.collante, body.dark .site .topnav.collante {
  gap: 15px; row-gap: 8px;
  padding-left: clamp(16px, 2.2vw, 26px);
  padding-right: clamp(16px, 2.2vw, 26px);
}
.site .topnav .lk { font-size: 13.5px; }
.site .topnav .lg b { font-size: 10.5px; letter-spacing: .07em; }
.site .topnav .rt { gap: 9px; }
.site .topnav .rt .btn { padding: 8px 15px; font-size: 13px; }
.site .topnav .rt .btn.sm { padding: 7px 13px; font-size: 12.5px; }
.site .topnav .audsw a { padding: 6px 13px; font-size: 12px; }
.site .tiroirbtn { width: 38px; height: 36px; }

/* En dessous de 1 300 px les liens passent dans le tiroir. La règle d'origine
   attendait 980 px, largeur à laquelle la barre débordait depuis longtemps. */
@media (max-width: 1340px) {
  .site .topnav .lk { display: none; }
}
@media (max-width: 1080px) {
  .site .topnav .audsw { display: none; }
}
@media (max-width: 860px) {
  .site .topnav .rt .btn.gh { display: none; }
}


/* ══════════════════════════════════════════════════════════════════════════
   19. LE FILIGRANE — moins, mais mieux placé

   « Très peu de logo, il faut que ce soit épuré » : les bulles étaient à 5-7 %
   partout à la fois — fond de page, pied de page, bandes — et l'addition
   finissait par se voir. Elles descendent à 2,5-3,5 % et le pied de page n'en
   garde qu'une seule, à droite.
   ══════════════════════════════════════════════════════════════════════════ */
body::before      { opacity: .028 !important; }
body.dark::before { opacity: .035 !important; }
.site footer.site::before { display: none; }
.site footer.site::after  { opacity: .03; width: 300px; height: 230px; right: -90px; }

/* « Se connecter » redevient un lien, pas un bouton — c'est l'arbitrage que
   font Apple, Stripe ou Linear en haut de leurs pages. Trois surfaces pleines
   côte à côte se disputent l'attention, et surtout elles ne tiennent pas :
   1 412 px de contenu pour 1 388 utiles à 1 440 px d'écran. Après ce
   dégraissage — lien plutôt que bouton, sélecteur resserré, logo d'un demi-
   point plus petit, écarts à 13 px — il reste 1 340 px, soit 48 px de marge.
   Et l'œil sait enfin lequel des trois est l'action principale. */
.site .topnav .authbtns .lgin,
body.dark .site .topnav .authbtns .lgin {
  background: transparent !important; border: 0 !important; box-shadow: none !important;
  padding: 8px 4px; font-weight: 500; color: var(--ink);
  transition: color var(--t-etat) var(--e);
}
body.dark .site .topnav .authbtns .lgin { color: #DCE7F5; }
.site .topnav .authbtns .lgin:hover { color: var(--crimson); }
body.dark .site .topnav .authbtns .lgin:hover { color: #FF7E9C; }
.site .topnav .authbtns { gap: 8px; }
.site .topnav .rt { gap: 8px; }
.site .topnav, .site .topnav.collante,
body.dark .site .topnav, body.dark .site .topnav.collante { gap: 13px; }
.site .topnav .audsw a { padding: 5px 11px; font-size: 11.5px; }
.site .topnav .lg b { font-size: 10px; letter-spacing: .06em; }
@media (max-width: 1200px) { .site .topnav .audsw { display: none; } }


/* ══════════════════════════════════════════════════════════════════════════
   20. TROIS DÉFAUTS TROUVÉS EN REGARDANT LE RÉSULTAT

   a) LA PASTILLE DE NIVEAU EN DOUBLE
   Les couvertures portent déjà le niveau imprimé — la bulle « A1 » fait
   partie de l'illustration. Le site en superposait une seconde, en CSS. Tant
   qu'elles se recouvraient exactement, cela ne se voyait pas ; dès que la
   couverture s'est mise à monter au survol, on a vu les deux. On retire la
   pastille CSS là où la couverture existe : l'information est déjà là.

   b) L'ÉCONOMIE ANNONCÉE DEUX FOIS
   J'avais ajouté « 2 mois offerts » à côté de la bascule. Le badge d'à côté
   disait déjà « 2 mois offerts · jusqu'à −17 % » : les deux se chevauchaient
   littéralement. Le badge suffit ; il passe simplement en vert, parce qu'une
   économie n'est pas un appel à l'action et n'a rien à faire en rouge de
   marque à côté du bouton « S'abonner ».

   c) LE TITRE ÉCRIT DEUX FOIS SUR LES VIGNETTES
   Au survol, le titre s'affiche sur l'image floutée alors que la légende sous
   la vignette le répète. La légende s'efface le temps du survol.
   ══════════════════════════════════════════════════════════════════════════ */
.site .book.avec-couv .cv .lvl { display: none; }

.site .billbadge {
  background: color-mix(in srgb, var(--green) 14%, transparent);
  color: var(--green);
  border: 1px solid color-mix(in srgb, var(--green) 32%, transparent);
  border-radius: var(--r-pill); padding: 7px 15px; font-size: 12.5px;
  font-weight: 600; letter-spacing: 0;
}
body.dark .site .billbadge { color: #5FCB8A; border-color: rgba(95,203,138,.3);
  background: rgba(95,203,138,.12); }
.site .billrow { gap: 16px; margin-bottom: clamp(30px, 3.4vw, 48px); }

.site .tvcard.hot .cap { opacity: 0; }
.site .tvcard .cap { transition: opacity var(--t-app) var(--e), color var(--t-etat) var(--e); }

/* La vignette grandit vers le centre de la rangée. Sans cela, la première
   déborde par la gauche et la dernière par la droite — elles se font couper
   au lieu de s'ouvrir. */
.site .tvcard { transform-origin: center center; }
.site .tvcard:first-child  { transform-origin: left center; }
.site .tvcard:last-child   { transform-origin: right center; }


/* ══════════════════════════════════════════════════════════════════════════
   21. TÉLÉPHONE — ce que l'audit a relevé à 320 px

   Les valeurs généreuses du bureau ne se transposent pas telles quelles : un
   bouton de 32 px de rembourrage tient dans une carte de 1 100 px, pas dans
   un écran de 320. Chaque correction ci-dessous répond à un défaut mesuré,
   pas à une intuition.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 760px) {
  /* le bouton de la bande Kids dépassait de 5 px de son cadre */
  .site .kidsband .btn.lg, .site .b2b .btn.lg,
  .site .kidsband .kbtn, .site .b2b .kbtn {
    width: 100%; text-align: center; padding-left: 16px; padding-right: 16px;
    margin-left: 0;
  }
  .site .kidsband > div:last-child, .site .b2b > *:last-child { width: 100%; }

  /* la signature du pied de page sortait de 14 px : la ligne du bas doit
     pouvoir passer à la ligne comme n'importe quelle autre */
  .site footer.site .bot {
    flex-wrap: wrap; row-gap: 10px; gap: 14px;
  }
  .site footer.site .bot em { margin-left: 0; }

  .site .hero2 { min-height: clamp(440px, 72vh, 560px); }
  .site .hero2 .heroctn h1 { font-size: clamp(30px, 8.6vw, 42px); }
  .site .billrow { flex-direction: column; }
}

@media (max-width: 400px) {
  .site .btn.lg { padding: 13px 20px; font-size: 15px; }
  .site .card { padding: 20px 18px; }
  .site .kidsband, .site .b2b { padding: 24px 20px; }
}


/* ══════════════════════════════════════════════════════════════════════════
   22. LA RANGÉE TFA TV AU DOIGT

   DÉFAUT MESURÉ : la première vignette collait au bord gauche de l'écran,
   sans marge, alors que la piste porte bien 20 px de rembourrage.

   CAUSE : `scroll-snap-align: start` aligne le bord de la vignette sur le
   début de la ZONE DE DÉFILEMENT, pas sur le bord intérieur du rembourrage.
   Le navigateur faisait donc défiler la rangée de 20 px dès l'affichage pour
   satisfaire l'accrochage — scrollLeft valait 20 au chargement, exactement la
   valeur du rembourrage. Le remède n'est pas de retirer la marge, c'est de
   dire au défilement où commence vraiment le contenu : `scroll-padding`.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 900px), (hover: none) {
  .site .tvviewport {
    scroll-padding-inline: 20px;
    /* le masque adoucit le bord droit : on voit que la rangée continue */
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 40px), transparent 100%);
            mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 40px), transparent 100%);
  }
  .site .tvtrack { padding: 16px 20px 20px; gap: 12px; }
  .site .tvcard, .site .tvcard:first-child, .site .tvcard:last-child { transform: none !important; }
  .site .tvcard .ov { display: none; }     /* il n'y a pas de survol au doigt */
  .site .tvcard.hot .cap { opacity: 1; }
  .site .tvchip { padding: 8px 15px; font-size: 12.5px; }
}

/* ── l'interlettrage des sur-titres est calculé pour le bureau ── */
@media (max-width: 620px) {
  .site .eyebrow, .site .sur { letter-spacing: .1em; font-size: 11px; }
  .site .hero2 .heroctn .kick2 { letter-spacing: .18em; font-size: 10.5px; }
}


/* ══════════════════════════════════════════════════════════════════════════
   23. TROIS PLANS DANS LE BANDEAU D'ACCUEIL

   La profondeur ne vient pas d'une couche qui bouge : elle vient de trois
   couches qui bougent à des vitesses différentes.
     · la photo, la plus lointaine, retenue de 60 px ;
     · deux lueurs colorées entre la photo et le voile, retenues de 90 et
       150 px, en `screen` pour qu'elles éclairent l'image au lieu de la
       salir ;
     · le titre, qui suit la page à sa vitesse.
   ══════════════════════════════════════════════════════════════════════════ */
.site .hero2 .lueur {
  position: absolute; border-radius: 50%; pointer-events: none;
  z-index: 1; mix-blend-mode: screen; opacity: .8;
}
.site .hero2 .heroscrim { z-index: 2; }
.site .hero2 .heroctn   { z-index: 3; }
.site .hero2 .scrollhint{ z-index: 4; }
.site .hero2 .lueur.a {
  left: -14%; top: -22%; width: 66%; aspect-ratio: 1;
  background: radial-gradient(circle closest-side,
              rgba(56,126,214,.50) 0%, rgba(56,126,214,.22) 44%, transparent 100%);
}
.site .hero2 .lueur.b {
  right: -16%; bottom: -30%; width: 62%; aspect-ratio: 1;
  background: radial-gradient(circle closest-side,
              rgba(214,36,88,.44) 0%, rgba(214,36,88,.18) 46%, transparent 100%);
}
@media (prefers-reduced-motion: reduce) { .site .hero2 .lueur { display: none; } }


/* ══════════════════════════════════════════════════════════════════════════
   24. LA COLLECTION EN GRAND — « Explore the lineup »

   Les couvertures faisaient 168 px de large dans une grille de cinq. Un
   manuel de 248 pages présenté à la taille d'une vignette de contact ne se
   vend pas : on ne lit ni le titre imprimé, ni l'illustration, ni le soin
   qu'il y a dedans.

   La rangée passe donc en défilement horizontal, avec des cartes de 300 à
   380 px : deux et demie tiennent à l'écran, la troisième dépasse — c'est
   ce débord qui dit qu'il y en a d'autres, sans avoir à l'écrire.
   ══════════════════════════════════════════════════════════════════════════ */
.site .lineup {
  display: flex;
  gap: clamp(18px, 2.2vw, 34px);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scroll-padding-inline: 2px;
  padding: 6px 2px 14px;
  margin: 0 -2px;
  scrollbar-width: none;
  -ms-overflow-style: none;
  /* le bord droit s'estompe : la rangée continue visiblement */
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 56px), transparent 100%);
          mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 56px), transparent 100%);
}
.site .lineup::-webkit-scrollbar { display: none; }
.site .lineup.aufond {                       /* dernière carte atteinte */
  -webkit-mask-image: none; mask-image: none;
}

.site .lvcard {
  flex: 0 0 clamp(268px, 27vw, 372px);
  scroll-snap-align: start;
  display: flex; flex-direction: column;
  position: relative;
}

/* ── la scène : le fond teinté sur lequel l'objet est posé ── */
.site .lvscene {
  position: relative;
  aspect-ratio: 1 / 1.3;
  border-radius: var(--r-l);
  overflow: hidden;
  /* FLEX ET NON GRID — la nuance a des conséquences.
     Dans une grille dont la piste est `auto`, la piste est dimensionnée par
     son contenu ; résoudre un `max-height: 100 %` de l'image contre cette
     piste serait circulaire, alors le navigateur l'ignore. Mesuré : une
     couverture de 390 px dans une boîte de 333, débordant de 16 px sous le
     cadre. En flex, le pourcentage se résout contre la boîte de contenu du
     conteneur, qui est définie par l'aspect-ratio. La couverture tient. */
  display: flex; align-items: center; justify-content: center;
  padding: clamp(18px, 2.2vw, 32px) clamp(26px, 3.2vw, 46px);
  margin-bottom: 22px;
  background:
    radial-gradient(120% 90% at 50% 6%, var(--t1) 0%, transparent 66%),
    linear-gradient(168deg, var(--t2) 0%, var(--t3) 100%);
  transition: transform var(--t-app) var(--e);
}
.site .lvcard[data-niv="a1"] { --t1:rgba(214,36,88,.28); --t2:#1A1020; --t3:#0C0F18; --tc:#FF7E9C; }
.site .lvcard[data-niv="a2"] { --t1:rgba(20,160,178,.26); --t2:#08181E; --t3:#0A1018; --tc:#5FD6E4; }
.site .lvcard[data-niv="b1"] { --t1:rgba(38,166,110,.24); --t2:#0A1A14; --t3:#0A1018; --tc:#5FCB8A; }
.site .lvcard[data-niv="b2"] { --t1:rgba(214,140,30,.24); --t2:#1C1509; --t3:#0F1016; --tc:#E7A63C; }
.site .lvcard[data-niv="c1"] { --t1:rgba(140,90,220,.26); --t2:#150F26; --t3:#0B0E18; --tc:#B79BF0; }

/* La couverture : elle flotte, avec son ombre portée.
   `height: 100%` la faisait déborder par le bas — dans une grille, 100 %
   se calcule sur la zone de grille et non sur la boîte de contenu, si bien
   que l'image mangeait le rembourrage inférieur et se trouvait coupée net
   au ras du cadre. On contraint par les MAXIMA et on laisse l'image choisir
   sa taille : elle se réduit jusqu'à tenir, sans jamais être rognée. */
.site .lvscene .cvimg {
  position: static;
  width: auto; height: auto;
  max-width: 100%; max-height: 100%;
  object-fit: contain;
  border-radius: 6px;
  box-shadow:
    0 2px 4px rgba(0,0,0,.4),
    0 26px 50px -14px rgba(0,0,0,.72);
  transition: transform var(--t-gros) var(--e), box-shadow var(--t-gros) var(--e);
}
.site .lvscene .cvimg::selection { background: transparent; }
/* la tranche, comme sur un livre vu de face */
.site .lvscene::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.05);
}
/* le reflet qui traverse la couverture au survol */
.site .lvscene .refl {
  position: absolute; inset: -30% -60%; z-index: 3; pointer-events: none;
  background: linear-gradient(102deg, transparent 40%,
              rgba(255,255,255,.22) 49%, rgba(255,255,255,.04) 56%, transparent 63%);
  transform: translate3d(-130%, 0, 0); opacity: 0;
}
.site .lvcard:hover .cvimg { transform: translateY(-10px) scale(1.035); }
.site .lvcard:hover .refl  { animation: tfa-reflet .8s var(--e) forwards; }

.site .lvfictif { text-align: center; color: #fff; }
.site .lvfictif b { display: block; font-size: clamp(20px, 2.4vw, 30px); font-weight: 800;
  letter-spacing: -.03em; line-height: 1.05; }
.site .lvfictif span { display: block; margin-top: 10px; font-size: 11px; letter-spacing: .18em;
  color: rgba(255,255,255,.5); }

.site .lvcard.a-venir .cvimg { filter: grayscale(.45) brightness(.62); }
.site .lvcard.a-venir .lvscene { opacity: .9; }
.site .lvscene .bientot {
  position: absolute; left: 50%; bottom: 18px; transform: translateX(-50%); z-index: 4;
  font-size: 10px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  color: #fff; background: rgba(8,12,18,.5);
  border: 1px solid rgba(255,255,255,.3);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  border-radius: var(--r-pill); padding: 5px 14px; white-space: nowrap;
}

/* ── le bloc de texte, sous l'objet ── */
.site .lvniv {
  display: inline-block; align-self: flex-start;
  font-size: 11px; font-weight: 700; letter-spacing: .14em;
  color: var(--tc); border: 1px solid color-mix(in srgb, var(--tc) 34%, transparent);
  background: color-mix(in srgb, var(--tc) 11%, transparent);
  border-radius: var(--r-pill); padding: 4px 12px; margin-bottom: 14px;
}
.site .lvcard h3 {
  font-size: clamp(21px, 1.9vw, 27px); font-weight: 700; letter-spacing: -.026em;
  margin: 0 0 10px; line-height: 1.15;
}
.site .lvacc {
  font-size: 15px; line-height: 1.5; color: var(--soft); margin: 0 0 12px;
  min-height: 3em;
}
.site .lvmeta { font-size: 13.5px; color: var(--soft2); margin: 0 0 20px; }
.site .lvacts { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; margin-top: auto; }
.site .lvacts .btn { padding: 11px 24px; font-size: 15px; }
.site .lvlien {
  font-size: 15px; font-weight: 500; color: var(--navy-l); text-decoration: none;
  display: inline-flex; align-items: center; gap: 5px;
  transition: color var(--t-etat) var(--e);
}
body.dark .site .lvlien { color: #8FB6F0; }
.site .lvlien i { font-style: normal; transition: transform var(--t-etat) var(--e); }
.site .lvlien:hover i { transform: translateX(4px); }

/* ── les flèches, comme sur la rangée d'Apple ── */
.site .lvrail { position: relative; }
.site .lvnav2 {
  position: absolute; top: 30%; width: 44px; height: 44px; border-radius: 50%;
  border: 0; cursor: pointer; z-index: 5;
  background: color-mix(in srgb, var(--ink) 10%, transparent);
  color: var(--ink); font-size: 17px; line-height: 1;
  display: grid; place-items: center;
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  opacity: 0; pointer-events: none;
  transition: opacity var(--t-app) var(--e), background-color var(--t-etat) var(--e),
              transform var(--t-etat) var(--e);
}
body.dark .site .lvnav2 { background: rgba(255,255,255,.12); color: #fff; }
.site .lvnav2.utile { opacity: 1; pointer-events: auto; }
.site .lvnav2:hover { transform: scale(1.08); background: color-mix(in srgb, var(--ink) 18%, transparent); }
body.dark .site .lvnav2:hover { background: rgba(255,255,255,.2); }
.site .lvnav2.g { left: -10px; }
.site .lvnav2.d { right: -10px; }

@media (max-width: 760px) {
  .site .lvcard { flex-basis: 78vw; }
  .site .lvnav2 { display: none; }
  .site .lvacc { min-height: 0; }
}


/* ══════════════════════════════════════════════════════════════════════════
   25. LES LIENS REVIENNENT DANS LA BARRE

   Je les avais renvoyés dans le tiroir sous 1 340 px pour faire tenir la
   barre. C'était le mauvais arbitrage : sur un écran réglé à 150 %, un
   1 920 px devient 1 280 px de CSS, et les liens disparaissaient sur des
   moniteurs parfaitement grands.

   Le poste supprimé est ailleurs : le mot « TRULY FLUENT ACADEMY » écrit à
   côté du logo pèse 140 px et répète ce que le logo dit déjà. Il s'efface
   sous 1 460 px — la marque reste, par ses deux bulles. C'est exactement ce
   que fait Apple, qui n'écrit jamais son nom à côté de sa pomme.
   Les liens tiennent alors jusqu'à 1 060 px.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 1460px) {
  .site .topnav .lg b { display: none; }
}
@media (max-width: 1340px) {
  .site .topnav .lk { display: block; }      /* on annule la règle précédente */
}
@media (max-width: 1060px) {
  .site .topnav .lk { display: none; }
}
@media (max-width: 1160px) { .site .topnav .audsw { display: none; } }
@media (min-width: 1161px) { .site .topnav .audsw { display: inline-flex; } }


/* ══════════════════════════════════════════════════════════════════════════
   26. LE PANNEAU DE NAVIGATION

   Il descend sous la barre, sur toute la largeur, dans le même verre. Sa
   hauteur est animée ; ses contenus se croisent en fondu. Les deux durées
   sont différentes à dessein : la surface met 0,36 s à se redimensionner, le
   contenu 0,28 s à apparaître, de sorte que le texte arrive dans un cadre
   déjà presque en place et non pendant qu'il bouge encore.
   ══════════════════════════════════════════════════════════════════════════ */
.site .mega {
  position: absolute; left: 0; right: 0; top: 100%;
  z-index: 79;
  pointer-events: none;
  /* La barre déclare `white-space: nowrap` pour que ses liens ne se coupent
     jamais en deux. Le panneau vit maintenant dedans : sans cette remise à
     zéro, il en héritait et les phrases de la carte de droite sortaient du
     cadre au lieu de passer à la ligne. */
  white-space: normal;
}
.site .topnav.collante { position: sticky; }
.site .mega .megabg {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity var(--t-app) var(--e);
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
  box-shadow: 0 24px 48px -28px rgba(9,25,50,.5);
}
body.dark .site .mega .megabg {
  background: rgba(9,13,20,.9);
  border-bottom-color: rgba(255,255,255,.08);
  box-shadow: 0 30px 60px -30px rgba(0,0,0,.8);
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .site .mega .megabg {
    -webkit-backdrop-filter: blur(18px) saturate(150%);
            backdrop-filter: blur(18px) saturate(150%);
  }
}
.site .mega.ouvert { pointer-events: auto; }
.site .mega.ouvert .megabg { opacity: 1; }

.site .megaboite {
  position: relative;
  height: 0; overflow: hidden;
  transition: height var(--t-app) var(--e);
}
.site .megapan {
  position: absolute; left: 0; right: 0; top: 0;
  opacity: 0; transform: translate3d(0, -10px, 0);
  pointer-events: none;
  /* `visibility` et non seulement `opacity` : un élément à opacité nulle
     reste dans l'ordre de tabulation. Sans cette ligne, six panneaux fermés
     glissaient une quarantaine de liens invisibles entre la barre et le
     contenu — au clavier, on tabulait dans le vide. Le délai la maintient
     visible le temps que le fondu de sortie se termine. */
  visibility: hidden;
  transition: opacity .28s var(--e), transform .28s var(--e), visibility 0s linear .28s;
}
.site .megapan.la {
  opacity: 1; transform: none; pointer-events: auto;
  visibility: visible; transition-delay: 0s;
}

.site .megain {
  max-width: 1160px; margin: 0 auto;
  padding: 34px clamp(20px, 4vw, 34px) 40px;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)) 1.15fr;
  gap: clamp(24px, 3vw, 56px);
  align-items: start;
}
.site .megacol h6 {
  margin: 0 0 14px; font-size: 11px; font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--soft2);
}
.site .megacol a {
  display: block; padding: 6px 0;
  font-size: 16px; font-weight: 500; letter-spacing: -.012em;
  color: var(--ink); text-decoration: none;
  transition: color var(--t-etat) var(--e), transform var(--t-etat) var(--e);
}
body.dark .site .megacol a { color: #E6EEF9; }
.site .megacol a:hover { color: var(--crimson); transform: translateX(3px); }
body.dark .site .megacol a:hover { color: #FF7E9C; }

/* la carte de droite : une seule mise en avant par panneau, jamais deux */
.site .megamise {
  display: block; text-decoration: none;
  border-radius: var(--r-m); padding: 22px 24px;
  background: linear-gradient(150deg, #0F2444 0%, #16345C 60%, #241A47 100%);
  color: #fff; position: relative; overflow: hidden;
  transition: transform var(--t-app) var(--e), box-shadow var(--t-app) var(--e);
}
.site .megamise:hover { transform: translateY(-3px); box-shadow: var(--sh2); }
.site .megamise .ms { display: block; font-size: 10.5px; font-weight: 700;
  letter-spacing: .16em; color: #FF9CB6; margin-bottom: 10px; }
.site .megamise b { display: block; font-size: 19px; font-weight: 700;
  letter-spacing: -.02em; margin-bottom: 8px; }
.site .megamise .mt { display: block; font-size: 14px; line-height: 1.5;
  color: #BFD2E9; margin-bottom: 14px; }
.site .megamise .ml { display: inline-flex; align-items: center; gap: 6px;
  font-size: 14px; font-weight: 600; color: #fff; }
.site .megamise .ml i { font-style: normal; transition: transform var(--t-etat) var(--e); }
.site .megamise:hover .ml i { transform: translateX(4px); }

/* le lien dont le panneau est ouvert reste allumé */
.site .topnav .lk.deroule { color: var(--crimson); }
body.dark .site .topnav .lk.deroule { color: #FF7E9C; }
.site .topnav .lk.deroule::after { transform: scaleX(1); }

@media (max-width: 1060px) { .site .mega { display: none; } }
@media (prefers-reduced-motion: reduce) {
  .site .megaboite { transition: none; }
  .site .megapan { transition: opacity .01ms; transform: none; }
}


/* ══════════════════════════════════════════════════════════════════════════
   27. ADULTES / ENFANTS — la pastille glissante

   Même principe que la bascule tarifaire, avec une différence qui compte :
   ce sélecteur-ci change de page. La pastille ne peut donc pas glisser au
   clic, elle serait coupée net par le chargement. Elle glisse au SURVOL, en
   annonce de ce qui va arriver, et le passage d'une page à l'autre est pris
   en charge par la transition de vue ci-dessous.

   Sa largeur est mesurée sur le lien visé, jamais fixée à 50 % : « Adultes »
   et « Enfants » ne font pas la même largeur, et une pastille à moitié
   déborderait de l'un en flottant dans l'autre.
   ══════════════════════════════════════════════════════════════════════════ */
.site .audsw {
  position: relative; isolation: isolate;
  border-radius: var(--r-pill); padding: 3px;
  background: color-mix(in srgb, var(--ink) 7%, transparent);
  border: 1px solid color-mix(in srgb, var(--ink) 8%, transparent);
}
body.dark .site .audsw { background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.09); }
.site .audpuce {
  position: absolute; z-index: 0; top: 3px; bottom: 3px; left: 0;
  border-radius: var(--r-pill);
  background: var(--navy);
  box-shadow: 0 3px 12px -4px rgba(9,45,89,.6);
  transition: transform var(--t-app) var(--e), width var(--t-app) var(--e);
}
body.dark .site .audpuce { background: #23477B; }
.site .audsw a, body.dark .site .audsw a {
  position: relative; z-index: 1;
  background: none !important;
  color: var(--soft);
  transition: color var(--t-app) var(--e);
}
.site .audsw a.on, body.dark .site .audsw a.on { color: #fff; }
.site .audsw:hover a.on { color: var(--soft); }
.site .audsw a:hover, body.dark .site .audsw a:hover { color: #fff; }


/* ══════════════════════════════════════════════════════════════════════════
   28. LE PASSAGE D'UNE PAGE À L'AUTRE

   Adultes → Enfants était une coupure franche : la page blanchissait, puis
   l'autre apparaissait. Une seule règle demande au navigateur de fondre
   l'ancienne page dans la nouvelle. Là où elle n'est pas comprise, la
   navigation reste ce qu'elle était — rien à prévoir en repli.
   ══════════════════════════════════════════════════════════════════════════ */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: tfa-sortie .22s var(--e-in) both; }
::view-transition-new(root) { animation: tfa-entree .34s var(--e) both; }
@keyframes tfa-sortie { to { opacity: 0; transform: scale(.994); } }
@keyframes tfa-entree { from { opacity: 0; transform: scale(1.006); } }
/* la barre du haut ne doit pas fondre avec le reste : elle est commune aux
   deux pages, elle doit paraître ne pas avoir bougé */
.site .topnav.collante { view-transition-name: tfa-barre; }
::view-transition-group(tfa-barre) { animation-duration: .28s; }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}

/* La cible de « Voir les tarifs › » mesurait 94 × 24 px : sous le doigt,
   c'est trop petit — le seuil confortable est 44 px de haut. On l'atteint par
   le rembourrage vertical plutôt qu'en grossissant le texte, qui doit rester
   secondaire par rapport au bouton plein d'à côté. */
.site .lvlien { padding: 11px 2px; }


/* ══════════════════════════════════════════════════════════════════════════
   29. LE THÈME CLAIR — la barre en verre bleu

   Le blanc pur d'origine n'appartenait pas à la marque : la charte est bâtie
   sur le bleu nuit #092D59, et rien ne le rappelait en haut de page. La barre
   prend une teinte tirée de ce bleu même, éclaircie et rendue translucide —
   c'est la couleur de la marque vue à travers du verre, pas une couleur de
   plus.

   Le panneau déroulant reprend exactement la même recette, un ton plus dense :
   deux surfaces de verre superposées doivent se ressembler, sinon la seconde
   a l'air posée par-dessus au lieu d'appartenir à la première.
   ══════════════════════════════════════════════════════════════════════════ */
body:not(.dark) {
  --verre:      color-mix(in srgb, var(--navy) 12%, #FFFFFF);
  --verre-fort: color-mix(in srgb, var(--navy) 15%, #FFFFFF);
  --verre-brd:  color-mix(in srgb, var(--navy) 18%, transparent);
}
body:not(.dark) .site .topnav.collante {
  background: color-mix(in srgb, var(--verre) 78%, transparent);
}
body:not(.dark) .site .topnav.collante.pose {
  background: color-mix(in srgb, var(--verre) 86%, transparent);
  border-bottom-color: var(--verre-brd);
  box-shadow: 0 1px 0 rgba(255,255,255,.7) inset;
}
/* Opaque, et non translucide : sous la barre il n'y a pas un fond neutre mais
   la photo du bandeau, blanche et contrastée. À 8 % de transparence le titre
   « L'anglais enfin » traversait encore le panneau et rendait les liens
   illisibles. Le verre est réservé à la barre elle-même, qui est fine. */
body:not(.dark) .site .mega .megabg {
  background: var(--verre-fort);
  border-bottom-color: var(--verre-brd);
  box-shadow: 0 26px 50px -30px rgba(9,45,89,.42);
}
/* le tiroir, le sélecteur et les pastilles suivent la même teinte */
body:not(.dark) .site .audsw,
body:not(.dark) .site .billsw,
body:not(.dark) .site .tiroirbtn {
  background: color-mix(in srgb, var(--navy) 6%, transparent);
  border-color: var(--verre-brd);
}
body:not(.dark) .site .megacol h6 { color: color-mix(in srgb, var(--navy) 62%, transparent); }
body:not(.dark) .site .megacol a  { color: var(--navy); }


/* ══════════════════════════════════════════════════════════════════════════
   30. LE PRIX SUR LA COUVERTURE — le bandeau crimson

   LE CONCEPT VENAIT DE VOUS : un bandeau de marque qui monte du bas, le prix
   en grand, l'information secondaire à droite, le niveau en dessous. Ce qui
   change ici tient en quatre points, et chacun répond à un défaut visible sur
   la maquette d'origine.

   1. LE BANDEAU EST TRANSLUCIDE, PAS OPAQUE.
      Un aplat plein efface la moitié de la couverture au moment précis où
      l'on vient la regarder. Un dégradé de crimson à 88 % en bas et 72 % en
      haut laisse deviner l'illustration dessous : on voit qu'il y a un objet,
      pas un rectangle rouge posé dessus.

   2. IL NE COUVRE QUE 38 % DE LA HAUTEUR, PAS LA MOITIÉ.
      Assez pour deux lignes confortables, assez peu pour que la scène reste
      la vedette.

   3. TROIS ENTRÉES DÉCALÉES PLUTÔT QU'UNE.
      Le bandeau monte, puis le prix arrive par la gauche, puis le niveau se
      lève. Trente millisecondes séparent chaque départ. C'est ce décalage qui
      donne la sensation d'un mouvement composé plutôt que d'un bloc qui
      apparaît — le même principe que le tiroir du menu.

   4. LA COUVERTURE S'ASSOMBRIT SOUS LE BANDEAU.
      Sans cela le texte blanc lutte contre une illustration claire et il
      faut lui ajouter une ombre portée, qui salit tout.

   Le nombre de chapitres remplace le nombre de pages : c'est l'unité dans
   laquelle nos manuels sont réellement découpés, et la seule que je puisse
   afficher sans l'inventer.
   ══════════════════════════════════════════════════════════════════════════ */
.site .lvprix {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 5;
  height: 38%;
  display: flex; flex-direction: column; justify-content: center; gap: 6px;
  padding: 0 clamp(20px, 2.2vw, 30px);
  color: #fff;
  background: linear-gradient(0deg,
              rgba(176, 8, 53, .90) 0%,
              rgba(192, 9, 58, .84) 62%,
              rgba(206, 16, 68, .74) 100%);
  border-top: 1px solid rgba(255, 255, 255, .16);
  transform: translate3d(0, 101%, 0);
  visibility: hidden;
  transition: transform var(--t-app) var(--e), visibility 0s linear var(--t-app);
  pointer-events: none;
}
.site .lvcard:hover .lvprix,
.site .lvcard:focus-within .lvprix {
  transform: none; visibility: visible; transition-delay: 0s;
}

/* ligne 1 : le prix, très grand, et ce qu'il y a dedans, à droite */
.site .lvp1 { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; }
.site .lvprix b {
  font-size: clamp(30px, 3vw, 42px); font-weight: 600; letter-spacing: -.04em;
  line-height: 1; white-space: nowrap;
  /* il entre par la gauche, comme sur votre exemple */
  transform: translate3d(-24px, 0, 0); opacity: 0;
  transition: transform .34s var(--e), opacity .3s var(--e);
}
.site .lvprix .lvp1 span {
  font-size: 13px; font-weight: 500; color: rgba(255, 255, 255, .82);
  white-space: nowrap;
  transform: translate3d(16px, 0, 0); opacity: 0;
  transition: transform .34s var(--e), opacity .3s var(--e);
}
/* ligne 2 : le niveau, en français puis en code CECRL */
.site .lvp2 {
  font-size: 15px; font-weight: 500; letter-spacing: -.01em;
  color: rgba(255, 255, 255, .92);
  transform: translate3d(0, 12px, 0); opacity: 0;
  transition: transform .34s var(--e), opacity .3s var(--e);
}
.site .lvcard:hover .lvprix b,
.site .lvcard:focus-within .lvprix b { transform: none; opacity: 1; transition-delay: .07s; }
.site .lvcard:hover .lvprix .lvp1 span,
.site .lvcard:focus-within .lvprix .lvp1 span { transform: none; opacity: 1; transition-delay: .11s; }
.site .lvcard:hover .lvp2,
.site .lvcard:focus-within .lvp2 { transform: none; opacity: 1; transition-delay: .14s; }

/* la couverture recule sous le bandeau */
.site .lvscene .cvimg { transition: transform var(--t-gros) var(--e), filter var(--t-app) var(--e); }
.site .lvcard:hover .cvimg { transform: translateY(-8px) scale(1.03); filter: brightness(.74) saturate(.95); }
.site .lvcard.a-venir:hover .cvimg { filter: grayscale(.45) brightness(.5); }

/* le « Bientôt » remonte pour ne pas se faire recouvrir */
.site .lvscene .bientot { transition: transform var(--t-app) var(--e), opacity var(--t-app) var(--e); }
.site .lvcard:hover .lvscene .bientot { opacity: 0; transform: translate(-50%, 14px); }

@media (hover: none) {
  /* Au doigt il n'y a pas de survol : le bandeau reste affiché en permanence,
     sinon l'information disparaît purement et simplement pour la moitié des
     visiteurs. Mais il reste À SA PLACE, au bas de la scène.
     `position: static` était une erreur : `.lvprix` est un enfant de
     `.lvscene`, qui est une boîte flexible centrant la couverture. Sorti du
     flux absolu, le bandeau devenait un deuxième élément de cette rangée et
     se rangeait À DROITE du livre, moitié hors cadre. Constaté à l'écran.
     Il reste donc absolu, simplement toujours visible et plus discret : un
     voile sombre au lieu de l'aplat crimson, qui pèserait en permanence. */
  .site .lvprix {
    height: auto; transform: none; visibility: visible;
    padding: 18px clamp(18px, 4vw, 24px) 16px; gap: 3px;
    background: linear-gradient(0deg, rgba(6,10,16,.92) 0%, rgba(6,10,16,.72) 60%, rgba(6,10,16,0) 100%);
    border-top: 0;
  }
  .site .lvprix b, .site .lvprix .lvp1 span, .site .lvp2 {
    transform: none; opacity: 1; transition: none;
  }
  .site .lvprix b { font-size: clamp(24px, 6vw, 30px); color: #fff; }
  .site .lvprix .lvp1 span, .site .lvp2 { color: rgba(255,255,255,.78); }
  .site .lvp2 { font-size: 14px; }
  .site .lvcard .lvmeta { display: none; }
  .site .lvcard .lvscene .bientot { bottom: 96px; }
}

@media (prefers-reduced-motion: reduce) {
  .site .lvprix, .site .lvprix b, .site .lvprix .lvp1 span, .site .lvp2 {
    transition-duration: .01ms !important;
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   31. LES FAMILLES DE LA COLLECTION

   Quatre pastilles encadrées, chacune avec un badge de comptage à fond plein :
   huit surfaces colorées pour dire quatre choses. Elles deviennent un
   sélecteur d'un seul tenant, avec une piste qui glisse sous l'onglet actif —
   la même mécanique que la bascule tarifaire et que le sélecteur Adultes /
   Enfants, si bien que le site n'a plus qu'une seule façon de dire « voici
   les options, en voici une de choisie ».

   Le nombre passe en exposant : il informe sans peser.
   ══════════════════════════════════════════════════════════════════════════ */
.site .famtabs {
  position: relative; isolation: isolate;
  display: inline-flex; flex-wrap: nowrap; gap: 0;
  margin: clamp(20px, 2.4vw, 32px) 0 clamp(22px, 2.6vw, 34px);
  padding: 4px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  border: 1px solid color-mix(in srgb, var(--ink) 8%, transparent);
  max-width: 100%; overflow-x: auto; scrollbar-width: none;
}
.site .famtabs::-webkit-scrollbar { display: none; }
body.dark .site .famtabs { background: rgba(255,255,255,.05); border-color: rgba(255,255,255,.08); }
body:not(.dark) .site .famtabs {
  background: color-mix(in srgb, var(--navy) 6%, transparent); border-color: var(--verre-brd);
}

/* La pastille se déplace en DEUX dimensions et prend la hauteur de sa cible.
   Les neuf catégories de TFA TV tiennent sur une ligne à 1 440 px et sur deux
   à 1 100 : une pastille qui ne bouge qu'horizontalement se retrouverait sur
   la mauvaise ligne dès le premier repli. */
.site .ftpuce {
  /* ORIGINE À ZÉRO, ET NON À 4 px.
     La position de la puce est calculée en JavaScript à partir de
     `offsetLeft`, qui se mesure depuis le bord intérieur de l'hôte. Un
     décalage de 4 px écrit ici s'AJOUTAIT à ce calcul, et la bordure de 1 px
     de l'hôte s'y ajoutait encore : la puce tombait 5 px trop à droite et
     5 px trop bas, sur chaque onglet. Assez peu pour passer pour un défaut
     de rendu, assez pour se voir. Une position calculée et une position
     déclarée ne doivent jamais se cumuler. */
  position: absolute; z-index: 0; top: 0; left: 0; width: 0; height: 0;
  border-radius: var(--r-pill);
  background: var(--navy);
  box-shadow: 0 4px 14px -5px rgba(9,45,89,.55);
  transition: transform var(--t-app) var(--e), width var(--t-app) var(--e),
              height var(--t-app) var(--e), background-color var(--t-app) var(--e);
}
.site .tvchips .ftpuce { top: 0; left: 0; }
body.dark .site .ftpuce { background: #23477B; }

.site .ftab, body.dark .site .ftab {
  position: relative; z-index: 1;
  font: inherit; border: 0; background: none; cursor: pointer;
  white-space: nowrap;
  display: inline-flex; align-items: baseline; gap: 7px;
  padding: 9px 18px; border-radius: var(--r-pill);
  font-size: 14px; font-weight: 500; letter-spacing: -.01em;
  color: var(--soft);
  transition: color var(--t-app) var(--e);
}
.site .ftab i {
  font-style: normal; font-size: 11px; font-weight: 600;
  color: color-mix(in srgb, currentColor 55%, transparent);
  background: none; padding: 0; border-radius: 0;
}
.site .ftab:hover { color: var(--ink); }
body.dark .site .ftab:hover { color: #EAF1FA; }
.site .ftab.on, body.dark .site .ftab.on { color: #fff; }
.site .ftab.on i { color: rgba(255,255,255,.66); }


/* ══════════════════════════════════════════════════════════════════════════
   32. LE TIROIR ☰ — ouverture à la manière d'Apple

   L'ancienne ouverture : `translateX(-100%)` en 0,22 s, courbe `ease`, et le
   contenu déjà en place à l'arrivée. Le panneau arrivait comme un volet qui
   claque.

   Ce qui change :
   · le panneau glisse en 0,5 s sur la courbe de signature — assez lent pour
     qu'on voie qu'il vient de la gauche, assez amorti pour qu'il ne rebondisse
     pas ;
   · les entrées ne sont pas là quand il arrive : elles montent une par une,
     30 ms d'écart, pendant que le panneau finit sa course. C'est ce décalage
     qui fait qu'un menu paraît « se déployer » plutôt que « se montrer » ;
   · l'icône ☰ se replie en croix : le trait du haut descend et pivote de 45°,
     celui du milieu s'efface en se rétractant, celui du bas remonte et pivote
     de −45°. C'est le même bouton, il change d'état ; deux icônes qui se
     remplacent liraient comme deux boutons différents.
   ══════════════════════════════════════════════════════════════════════════ */
.site .tiroir, body.dark .site .tiroir, .tiroir {
  transition: transform .5s var(--e);
  will-change: transform;
}
.voile { transition: opacity .44s var(--e), visibility 0s linear .44s; }
.voile.ouvert { transition: opacity .34s var(--e), visibility 0s; }

.tiroir h4, .tiroir a, .tiroir .tnote {
  opacity: 0;
  transform: translate3d(-14px, 0, 0);
  transition: opacity .42s var(--e), transform .42s var(--e);
}
.tiroir.ouvert h4, .tiroir.ouvert a, .tiroir.ouvert .tnote {
  opacity: 1; transform: none;
  transition-delay: calc(140ms + var(--i, 0) * 26ms);
}
/* l'en-tête ne fait pas partie du déploiement : il appartient au panneau */
.tiroir .thead, .tiroir .thead * { opacity: 1; transform: none; transition: none; }

/* l'icône qui se replie en croix */
.site .tiroirbtn .hamb path {
  transform-box: fill-box; transform-origin: center;
  transition: transform .44s var(--e), opacity .2s var(--e);
}
body.menu-ouvert .site .tiroirbtn .h1,
body.menu-ouvert .tiroirbtn .h1 { transform: translateY(5px) rotate(45deg); }
body.menu-ouvert .site .tiroirbtn .h2,
body.menu-ouvert .tiroirbtn .h2 { opacity: 0; transform: scaleX(.15); }
body.menu-ouvert .site .tiroirbtn .h3,
body.menu-ouvert .tiroirbtn .h3 { transform: translateY(-5px) rotate(-45deg); }

@media (prefers-reduced-motion: reduce) {
  .tiroir h4, .tiroir a, .tiroir .tnote { opacity: 1; transform: none; transition: none; }
  .site .tiroirbtn .hamb path { transition: none; }
}

/* ── LA BARRE RESTE AU-DESSUS DU TIROIR ──────────────────────────────────────
   Le tiroir recouvrait la barre : l'icône avait beau se replier en croix,
   personne ne la voyait — 100 % du travail d'animation était invisible. Chez
   Apple la barre ne bouge jamais ; c'est elle qui porte l'état ouvert/fermé,
   et le panneau glisse dessous. On inverse donc l'ordre d'empilement, on
   décale le contenu du tiroir de la hauteur de la barre, et son en-tête
   interne disparaît : il ferait doublon avec la vraie barre restée en place,
   logo compris. */
.site .topnav.collante, body.dark .site .topnav.collante { z-index: 200; }
.tiroir { padding-top: 78px; z-index: 190; }
.tiroir .thead { display: none; }
.voile { z-index: 180; }


/* ══════════════════════════════════════════════════════════════════════════
   33. OCCUPER LA PLACE

   Le constat, annoté sur les captures : « negative space » à gauche et à
   droite, « trop petit » au milieu. Les deux vont ensemble. Le contenu était
   enfermé dans 1 160 px au centre d'un écran qui en offre 1 280 à 1 900, et
   comme la colonne était étroite, tout ce qu'elle contenait avait été réduit
   pour y tenir. On élargit d'abord, on agrandit ensuite : dans l'autre sens
   on obtient du texte qui déborde.

   1 320 px, et jusqu'à 1 480 px au-delà de 1 700 px d'écran. Au-delà, on
   arrête : une ligne de texte de plus de 90 caractères se lit mal, quelle que
   soit la taille de l'écran.
   ══════════════════════════════════════════════════════════════════════════ */
.site .in, .site .blk2, .site .megain { max-width: 1320px; }
@media (min-width: 1700px) {
  .site .in, .site .blk2, .site .megain { max-width: 1480px; }
  .site .heroin, .site .hero2 .heroctn { max-width: 1480px; }
}
.site .heroin { max-width: 1320px; }

/* Les colonnes intérieures suivent : une grille de deux colonnes dans un
   cadre élargi doit répartir le gain, pas le laisser au milieu. */
.site .grid2 { gap: clamp(24px, 3vw, 52px); }
.site .grid3 { gap: clamp(20px, 2.2vw, 34px); }
.site .grid4 { gap: clamp(16px, 1.8vw, 28px); }
/* Encadré dans une requête média À PARTIR de 1 081 px — et pour la troisième
   fois de ce projet, pour la même raison : `.site .methode .grid2` (0,3,0)
   écrase le repli mobile écrit `.grid2` (0,1,0), et une requête média
   n'ajoute aucune spécificité. Sans ce cadre, la section restait à deux
   colonnes à 320 px et le texte débordait de 18 px. */
@media (min-width: 1081px) {
  .site .methode .grid2 { grid-template-columns: 1.06fr .94fr; }
}


/* ══════════════════════════════════════════════════════════════════════════
   34. DENSITÉ — que personne n'ait à plisser les yeux

   Relevé automatique : 219 déclarations de moins de 13 px dans les feuilles
   d'origine, dont une bonne part portait de vraies phrases. La maquette avait
   été dessinée pour être vue en petit ; ces corps-là n'ont jamais été prévus
   pour être lus en vrai.

   Distinction faite ici : une ÉTIQUETTE (un niveau, une durée, un état) peut
   rester petite, mais pas sous 11 px ; un TEXTE qui forme une phrase passe à
   15 px au minimum, 16,5 px dans les cartes.
   ══════════════════════════════════════════════════════════════════════════ */
.site .card p, .site .mrow p, .site .qcard2 span,
.site .exres div, .site .kcard p, .site .profcard p {
  font-size: 16.5px; line-height: 1.6;
}
.site .mrow h4, .site .kcard h4 { font-size: 18px; letter-spacing: -.018em; margin-bottom: 5px; }
.site .mrow { gap: 16px; margin-bottom: 22px; align-items: flex-start; }
.site .mnum {
  width: 30px; height: 30px; flex: 0 0 30px; font-size: 13.5px; font-weight: 700;
}
.site .mini, .site .apk, .site .serie .smeta, .site p.mini { font-size: 14px; line-height: 1.6; }
.site ul.ck li { font-size: 15.5px; padding: 6px 0 6px 26px; }
.site ul.ck li::before { font-size: 13px; }
.site .samplebox {
  font-size: 15.5px; line-height: 1.68;
  padding: clamp(20px, 2.2vw, 28px) clamp(22px, 2.4vw, 32px);
  border-radius: var(--r-m);
}
.site .badge, .site .tag, .site .puce, .site .nivtag, .site .sniv,
.site .clubkpi span, .site .exres .lb, .site .lb {
  font-size: 11.5px; letter-spacing: .06em;
}
.site .clubkpi b, .site .kpicard b { font-size: clamp(28px, 2.6vw, 38px); }
.site .price, .site .pm .p2 { font-size: clamp(30px, 2.8vw, 40px); }
.site .price small, .site .pm .p2 small { font-size: 14px; }
.site .pm { padding: clamp(18px, 1.8vw, 26px) clamp(16px, 1.7vw, 24px); }
.site .pm h4 { font-size: 17px; }
.site .pm .u { font-size: 13.5px; margin-bottom: 14px; }
.site .pm ul { font-size: 14.5px; line-height: 1.6; }
.site .pm ul li { padding: 4px 0 4px 18px; }
.site .doc p, .site .txtblk .corps { font-size: 17px; line-height: 1.8; }
.site .exemples, .site .attention { font-size: 16px; padding: 18px 22px; border-radius: var(--r-s); }
.site .ex li { font-size: 16px; padding: 10px 0; }
.site .nmots { font-size: 14.5px; padding: 14px 22px; }
.site .st3 { font-size: 15px; }
.site .ltfacts b { font-size: clamp(20px, 2vw, 26px); }
.site .quickrow .qcard2 b { font-size: 16px; }
.site .authcote li { font-size: 15px; }
.site .tvcard .cap { font-size: 14px; }


/* ══════════════════════════════════════════════════════════════════════════
   35. TFA TV — LES AFFICHES VERTICALES

   Les vidéos sont tournées en 9:16 pour Instagram et TikTok. Les présenter
   dans un cadre 16:9 revenait à recadrer une image verticale en horizontale :
   on perdait la tête et les pieds, c'est-à-dire tout ce qui compte. La
   vignette devient donc une affiche verticale — le format des jaquettes de la
   référence Netflix, et celui du fichier lui-même.

   Conséquence de cadrage : une affiche verticale est HAUTE. À 180 px de large
   elle fait 320 px de haut ; six tiennent encore de front sur un écran de
   1 320 px, ce qui garde la rangée lisible sans la transformer en mur.
   ══════════════════════════════════════════════════════════════════════════ */
.site .tvrow { margin-bottom: clamp(34px, 3.6vw, 56px); }
.site .tvrow h3 {
  font-size: clamp(19px, 1.7vw, 25px); font-weight: 700; letter-spacing: -.024em;
  margin: 0 0 16px; display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
}
.site .tvrow h3 span { font-size: 14px; font-weight: 500; color: #93AECD; letter-spacing: 0; }

/* la zone de défilement : une vraie, avec molette, doigt et clavier */
.site .tvviewport {
  position: relative;
  overflow-x: auto; overflow-y: visible;
  scroll-snap-type: x proximity;
  scroll-padding-inline: 2px;
  scrollbar-width: none; -ms-overflow-style: none;
  padding: 6px 0 10px;
}
.site .tvviewport::-webkit-scrollbar { display: none; }
.site .tvtrack {
  display: flex; gap: clamp(12px, 1.1vw, 18px);
  padding: 0; transform: none !important; width: max-content;
}

.site .tvcard {
  flex: 0 0 clamp(146px, 12.2vw, 194px);
  width: auto; scroll-snap-align: start;
  cursor: pointer; position: relative;
  transition: transform var(--t-app) var(--e);
  transform-origin: center bottom;
}
.site .tvcard .thumb {
  aspect-ratio: 9 / 16;                 /* le format des vidéos, pas un choix */
  border-radius: var(--r-s);
  overflow: hidden; position: relative;
  box-shadow: 0 4px 18px rgba(0,0,0,.45);
  transition: box-shadow var(--t-app) var(--e);
}
.site .tvcard.hot { transform: translateY(-8px) scale(1.05); z-index: 30; }
.site .tvcard.hot .thumb { box-shadow: 0 26px 60px -14px rgba(0,0,0,.78); }
.site .tvcard .thumb .g {
  position: absolute; inset: 0;
  transition: transform var(--t-gros) var(--e), filter var(--t-app) var(--e);
}
.site .tvcard.hot .thumb .g { transform: scale(1.08); filter: blur(9px) brightness(.55) saturate(1.2); }

/* les étiquettes : niveau en haut, durée en bas */
.site .tvcard .thumb .lk2 { position: absolute; top: 9px; left: 9px; }
.site .tvcard .thumb .dur { position: absolute; right: 9px; bottom: 11px; }
.site .tvcard .thumb .lk2, .site .tvcard .thumb .dur {
  background: rgba(6,10,16,.55); color: #EAF2FB;
  border-radius: var(--r-pill); padding: 3px 10px;
  font-size: 11px; font-weight: 600; letter-spacing: .03em;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  transition: opacity var(--t-app) var(--e);
}
.site .tvcard.hot .thumb .lk2, .site .tvcard.hot .thumb .dur { opacity: 0; }
.site .tvcard .thumb .prog { position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: rgba(255,255,255,.24); }
.site .tvcard .thumb .prog i { display: block; height: 100%; background: #F2436E; }

/* la couche d'information, verticale elle aussi */
.site .tvcard .ov { padding: 14px 13px; }
.site .tvcard .ov .pl { top: 42%; }
.site .tvcard .ov b { font-size: 14px; }
.site .tvcard .ov small { font-size: 12px; }
.site .tvcard .cap { margin-top: 10px; font-weight: 500; line-height: 1.35; }

/* ── le classement, comme sur la référence ──
   Le chiffre est posé DERRIÈRE l'affiche, en creux : plein, il pèserait
   autant qu'elle et l'on ne saurait plus lequel des deux regarder. */
.site .tvrow.top .tvcard { padding-left: 30px; }
.site .tvrow.top .rang {
  position: absolute; left: -6px; z-index: 0;
  /* le chiffre s'aligne sur le BAS DE L'AFFICHE, pas sur le bas de la carte :
     la légende occupe 2,7 em sous l'affiche, et une valeur fixe faisait
     chevaucher le chiffre et le titre dès que celui-ci passait à la ligne */
  bottom: calc(2.7em + 14px);
  font-size: 82px; font-weight: 800; line-height: .8; letter-spacing: -.06em;
  color: transparent;
  -webkit-text-stroke: 2px rgba(255,255,255,.30);
  transition: -webkit-text-stroke-color var(--t-app) var(--e), transform var(--t-app) var(--e);
  pointer-events: none;
}
.site .tvrow.top .tvcard.hot .rang {
  -webkit-text-stroke-color: rgba(242,67,110,.85);
  transform: translateX(-4px);
}

/* ── les flèches ── */
.site .tvnav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 46px; height: 46px; border-radius: 50%;
  background: rgba(8,14,24,.62); border: 1px solid rgba(255,255,255,.16);
  color: #fff; font-size: 19px; line-height: 1;
  display: grid; place-items: center; cursor: pointer; z-index: 20;
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  opacity: 0; pointer-events: none;
  transition: opacity var(--t-app) var(--e), transform var(--t-etat) var(--e),
              background-color var(--t-etat) var(--e);
}
.site .tvnav.l { left: 6px; }
.site .tvnav.r { right: 6px; }
.site .tvviewport:hover .tvnav.utile { opacity: 1; pointer-events: auto; }
.site .tvnav:hover { background: rgba(8,14,24,.9); transform: translateY(-50%) scale(1.08); }

/* le bord droit s'estompe tant qu'il reste des titres */
.site .tvviewport:not(.aufond) {
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 64px), transparent 100%);
          mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 64px), transparent 100%);
}
/* … mais un masque coupe aussi ce qui grandit au survol. On le retire dès
   qu'une vignette est ouverte, sinon elle se fait rogner en s'agrandissant. */
.site .tvviewport:hover { -webkit-mask-image: none; mask-image: none; }


/* ══════════════════════════════════════════════════════════════════════════
   36. LA FLÈCHE DU BANDEAU, ET LE RECOUVREMENT

   Une flèche blanche pulsait sous le titre, à moitié cachée par la rangée
   d'accès rapide. Personne ne pouvait deviner ce qu'elle faisait : elle ne
   cliquait rien, elle ne désignait rien de précis, et elle était coupée. Un
   élément d'interface que l'on ne comprend pas est pire que pas d'élément
   du tout — on le retire.

   Ce qui invite à descendre, c'est la rangée d'accès rapide elle-même : elle
   chevauche volontairement le bas du bandeau, si bien qu'on voit qu'il y a
   quelque chose en dessous. Ce chevauchement était accidentel et mal empilé ;
   il devient délibéré, avec sa propre ombre et son propre plan.
   ══════════════════════════════════════════════════════════════════════════ */
.site .scrollhint { display: none; }

.site .hero2 + section.blk,
.site .hero2 ~ section.blk:first-of-type { position: relative; z-index: 5; }
.site .quickrow {
  position: relative; z-index: 5;
  margin-top: clamp(-72px, -5vw, -40px);
  gap: clamp(12px, 1.2vw, 20px);
}
.site .quickrow .qcard2 {
  border-radius: var(--r-m);
  padding: clamp(18px, 1.8vw, 26px) clamp(14px, 1.4vw, 20px);
  box-shadow: var(--sh2);
  transition: transform var(--t-app) var(--e), box-shadow var(--t-app) var(--e),
              border-color var(--t-app) var(--e);
}
.site .quickrow .qcard2:hover { transform: translateY(-5px); box-shadow: var(--sh3); }
.site .quickrow .i2 {
  width: 42px; height: 42px; border-radius: var(--r-s); font-size: 19px;
  margin: 0 auto 12px;
  transition: transform var(--t-app) var(--e);
}
.site .quickrow .qcard2:hover .i2 { transform: scale(1.08) rotate(-4deg); }


/* ══════════════════════════════════════════════════════════════════════════
   37. TROIS APPAREILS, TROIS MISES EN PAGE

   Un site « responsive » n'est pas un site qui rétrécit : c'est un site qui
   change de raisonnement. Trois seuils, trois intentions.

   ORDINATEUR (au-delà de 1 080 px) — la largeur est une ressource : plusieurs
   colonnes, la barre complète, le survol.
   TABLETTE (641 à 1 080 px) — deux colonnes, la barre passe au tiroir, les
   cibles grossissent parce qu'on y touche avec le doigt, mais l'écran reste
   assez large pour comparer deux choses côte à côte.
   TÉLÉPHONE (jusqu'à 640 px) — une seule colonne, tout à pleine largeur, les
   rangées défilent au doigt, et rien ne descend sous 15 px.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── TABLETTE ────────────────────────────────────────────────────────────── */
@media (min-width: 641px) and (max-width: 1080px) {
  :root { --sect: clamp(56px, 7.5vw, 92px); --gout: clamp(24px, 3.6vw, 40px); }
  .site h2.sh, .site h2.t { font-size: clamp(28px, 4.4vw, 40px); }
  .site .hero2 { min-height: clamp(440px, 64vh, 620px); }
  .site .hero2 .heroctn h1 { font-size: clamp(34px, 6vw, 54px); }
  .site .methode .grid2 { grid-template-columns: 1fr; gap: 30px; }
  .site .quickrow { margin-top: -34px; }
  /* deux colonnes de manuels : la couverture reste grande sans forcer le
     défilement horizontal pour trois titres */
  .site .lvcard { flex-basis: clamp(240px, 42vw, 320px); }
  .site .tvcard { flex: 0 0 clamp(140px, 20vw, 170px); }
  .site .card { padding: 24px 22px; }
  /* toute cible tactile atteint 44 px : c'est le seuil confortable du doigt */
  .site .btn, .site .hbtn, .site .tvchip, .site .ftab, .site .audsw a { min-height: 44px; }
  .site .btn, .site .hbtn { display: inline-flex; align-items: center; justify-content: center; }
}

/* ── TÉLÉPHONE ───────────────────────────────────────────────────────────── */
@media (max-width: 640px) {
  :root { --sect: clamp(48px, 12vw, 72px); --gout: 20px; --gap: 14px; }
  .site h2.sh, .site h2.t { font-size: clamp(26px, 7.6vw, 34px); line-height: 1.1; }
  .site p.lead, .site p.lead2 { font-size: 16.5px; }
  .site .card p, .site .mrow p, .site .qcard2 span { font-size: 16px; }
  .site .mini, .site .apk { font-size: 14px; }
  .site ul.ck li { font-size: 15.5px; }
  .site .quickrow { margin-top: -26px; }
  .site .quickrow .qcard2 { padding: 18px 16px; }
  .site .btn, .site .hbtn { min-height: 46px; display: inline-flex;
    align-items: center; justify-content: center; }
  .site .hero2 .heroctn .cta { flex-direction: column; align-items: stretch; }
  .site .hero2 .heroctn .cta > * { width: 100%; }
  /* les rangées d'étiquettes défilent au doigt plutôt que de se replier
     sur quatre lignes */
  .site .tvchips, .site .chapmeta {
    display: flex; flex-wrap: nowrap; overflow-x: auto;
    scrollbar-width: none; padding-bottom: 4px;
    scroll-padding-inline: 20px;
  }
  .site .tvchips::-webkit-scrollbar, .site .chapmeta::-webkit-scrollbar { display: none; }
  .site .tvchips > * , .site .chapmeta > * { flex: 0 0 auto; }
  .site .tvcard { flex: 0 0 39vw; }
  .site .tvrow.top .tvcard { padding-left: 20px; }
  .site .tvrow.top .rang { font-size: 52px; -webkit-text-stroke-width: 1.5px; }
  .site .prem { font-size: 8.5px; padding: 3px 8px; letter-spacing: .12em; }
  .site .tvcard .thumb .lk2, .site .tvcard .thumb .dur { font-size: 10.5px; padding: 2px 8px; }
  .site .tvnav { display: none; }
  .site .lvcard { flex-basis: 76vw; }
  .site .megain { display: none; }
  .site footer.site .cols { gap: 26px; }
}

/* Entre 1 081 et 1 300 px — le petit ordinateur portable : les colonnes de
   quatre deviennent des colonnes de trois, sinon chaque carte tombe à 230 px
   et son texte redevient illisible. */
@media (min-width: 1081px) and (max-width: 1300px) {
  .site .grid4:not([style*="grid-template-columns"]) {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* ── Les rangées s'alignent sur le reste du site ──
   `#tvrows` est posé directement sous `.site`, sans conteneur : les affiches
   partaient donc du bord gauche de l'écran pendant que tout le reste de la
   page commençait à 1 320 px centrés. Et `scroll-snap-align: start` faisait
   défiler la rangée de la valeur du rembourrage dès l'affichage — d'où la
   première affiche coupée. `scroll-padding-inline` dit au défilement où
   commence vraiment le contenu. */
.site .tvrow > h3, .site .tvviewport {
  max-width: 1320px; margin-inline: auto;
  padding-inline: var(--gout);
}
.site .tvviewport { scroll-padding-inline: var(--gout); }
@media (min-width: 1700px) {
  .site .tvrow > h3, .site .tvviewport { max-width: 1480px; }
}
/* Une hauteur MINIMALE, pas un plafond. J'avais d'abord tronqué la légende à
   deux lignes : l'audit a relevé 19 px de texte masqué sur TFA TV, c'est-à-dire
   des titres de vidéo coupés en silence. Les affiches sont alignées par le
   haut ; une légende plus longue que les autres ne décale donc rien. */
.site .tvcard .cap { min-height: 2.7em; }
.site .tvchips {
  max-width: 1320px; margin-inline: auto; padding-inline: var(--gout);
}
@media (min-width: 1700px) { .site .tvchips { max-width: 1480px; } }

/* Six accès rapides empilés en pleine largeur, c'est trois écrans de
   défilement pour six mots. Deux colonnes : la rangée tient en un regard,
   et chaque pastille reste largement au-dessus de la cible tactile. */
@media (max-width: 760px) {
  .site .quickrow { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .site .quickrow .qcard2 { padding: 16px 12px; }
  .site .quickrow .qcard2 b { font-size: 15px; }
  .site .quickrow .qcard2 span { font-size: 13px; }
  .site .quickrow .i2 { width: 36px; height: 36px; font-size: 17px; margin-bottom: 9px; }
}


/* ══════════════════════════════════════════════════════════════════════════
   38. LE REMBOURRAGE DES PANNEAUX — le relevé complet

   Passage automatique sur les feuilles d'origine : 82 panneaux déclarant un
   rayon d'au moins 9 px avec moins de 22 px de rembourrage. Autrement dit
   82 surfaces dessinées comme des cartes et remplies comme des étiquettes.
   Le bandeau du Club en faisait partie — 26 px pour un bloc de 1 480 px de
   large, c'est ce qui donnait l'impression d'étouffement pointée sur la
   capture.

   Distinction tenue ici : une PASTILLE (badge, étiquette, puce) garde son
   rembourrage serré, c'est sa nature ; un PANNEAU (une surface qui contient
   plusieurs choses) passe à un rembourrage proportionnel à sa taille.
   ══════════════════════════════════════════════════════════════════════════ */
.site .clubband, .site .clubwrap {
  border-radius: var(--r-l);
  padding: clamp(28px, 2.8vw, 46px) clamp(26px, 3vw, 48px);
  gap: clamp(28px, 3vw, 54px);
  margin-bottom: 0;
}
.site .clubband h3 { font-size: clamp(22px, 2.1vw, 30px); font-weight: 700; margin: 0 0 12px; }
.site .clubband p { font-size: 16px; line-height: 1.66; max-width: 58ch; }
.site .clubband .cta { margin-top: clamp(20px, 2vw, 30px) !important; gap: 14px; }
.site .clubkpi { gap: clamp(12px, 1.2vw, 18px); }
.site .clubkpi > div {
  border-radius: var(--r-m);
  padding: clamp(18px, 1.8vw, 26px) clamp(12px, 1.2vw, 18px);
}

/* les autres panneaux relevés par le passage automatique */
.site .next, .site .archbox, .site .tcard, .site .ladder, .site .nat,
.site .exdemo, .site .fmt, .site .chit, .site .opt, .site .age,
.site .legend, .site .qsm, .site .starnote, .site .noteedit, .site .rgpd,
.site .accessbar, .site .pcard, .site .mascots {
  border-radius: var(--r-m);
  padding: clamp(20px, 2vw, 28px) clamp(18px, 1.9vw, 26px);
}
.site .child, .site .incl { padding: 12px 18px; border-radius: var(--r-s); }
.site .authtabs { padding: 5px; border-radius: var(--r-pill); }
.site .atab { border-radius: var(--r-pill); padding: 11px 0; font-size: 14.5px; }
.site .social { padding: 14px 0; border-radius: var(--r-s); font-size: 15px; }
.site .asexe label { padding: 13px 0; border-radius: var(--r-s); font-size: 14.5px; }
.site .attente input, .site .composer .fld2 { padding: 13px 16px; border-radius: var(--r-s); }
.site .acctmenu { padding: 8px; border-radius: var(--r-m); }
.site #installer { padding: 14px 18px; border-radius: var(--r-m); }
.site .counter { padding: 7px 15px; }

/* LES DEUX BOUTONS DU BANDEAU DU CLUB SE TOUCHAIENT — écart mesuré : 0 px.
   `display: flex` n'était déclaré que sur `.hero .cta` et `.heroctn .cta` ;
   ailleurs, `.cta` restait un bloc ordinaire, `gap` n'y avait aucun effet, et
   comme le balisage ne laisse aucune espace entre les deux liens, ils se
   collaient bord à bord. Ils lisaient comme un seul objet coupé en deux.
   La règle vaut maintenant partout où le nom est employé. */
.site .cta {
  display: flex; flex-wrap: wrap; align-items: center; gap: 14px;
}
.site .hbtn { padding: 14px 30px; border-radius: var(--r-pill); }


/* ══════════════════════════════════════════════════════════════════════════
   39. TFA TV — LE SURVOL PLUS VIF

   Le survol attendait 240 ms puis s'ouvrait en 360 ms : près de six dixièmes
   de seconde entre le moment où l'on pose la souris et celui où l'image est
   nette. C'est la bonne durée pour un panneau qui se déplie, pas pour une
   vignette que l'on survole en série. On descend à 90 ms d'attente et 220 ms
   d'ouverture — assez pour ne pas clignoter quand on traverse la rangée,
   assez vif pour répondre à l'intention.
   ══════════════════════════════════════════════════════════════════════════ */
.site .tvcard,
.site .tvcard .thumb,
.site .tvcard .thumb .g,
.site .tvcard .ov,
.site .tvcard .ov b,
.site .tvcard .ov small,
.site .tvcard .ov .pl,
.site .tvcard .cap,
.site .tvcard .prem,
.site .tvcard .thumb .lk2,
.site .tvcard .thumb .dur,
.site .tvrow.top .rang {
  transition-duration: .22s;
}
.site .tvcard.hot .thumb .g { filter: blur(8px) brightness(.55) saturate(1.2); }

/* Un élément de flexbox refuse par défaut de descendre sous la largeur de son
   contenu (`min-width: auto`). La bulle de message, élargie par le nouveau
   rembourrage, sortait donc de 45 px du fil de discussion à 320 px — l'audit
   l'a relevé. `min-width: 0` lève ce plancher ; le texte se replie alors
   normalement, y compris un pseudonyme sans espace. */
.site .msg, .site .msg .bb, .site .chit, .site .room {
  min-width: 0;
}
.site .msg .bb, .site .msg .nm { overflow-wrap: anywhere; }

/* Le message vocal déclarait `min-width: 190px` — une largeur absolue dans une
   bulle qui, à 320 px d'écran, n'en fait que 100. Il en sortait de 45 px.
   Une largeur minimale fixe est toujours une promesse qu'on ne peut pas tenir
   sur petit écran : elle devient une largeur souhaitée, plafonnée par la
   place réellement disponible. */
.site .voice { min-width: 0; flex-wrap: wrap; row-gap: 6px; }
.site .voice .wave { flex: 1 1 60px; min-width: 0; }

/* LE RELEVÉ COMPLET DES LARGEURS MINIMALES FIXES — six déclarations, toutes
   écrites pour un cadre de maquette large. Sur un écran de 320 px elles
   deviennent des promesses intenables : le champ de saisie du fil de
   discussion en réclamait 220, la barre d'accès 260. Une largeur minimale
   n'a de sens que relative à la place disponible.
   `min(valeur, 100 %)` conserve l'intention — « aussi large que possible » —
   sans jamais dépasser le cadre. */
.site .composer .fld2 { min-width: min(220px, 100%); }
.site .accessbar .txt { min-width: min(260px, 100%); }
.site .attente input  { min-width: min(180px, 100%); }
.site .fld            { min-width: min(88px, 100%); }
.site .ex input, .site .ex select { min-width: min(96px, 100%); max-width: 100%; }
.site .composer, .site .accessbar { flex-wrap: wrap; }


/* ══════════════════════════════════════════════════════════════════════════
   40. LES BULLES TARIFAIRES

   TROIS DÉFAUTS, UNE MÊME ORIGINE
   La rangée de tarifs de la page d'accueil n'utilise pas `.card` mais `.pm` —
   un composant distinct, hérité de la maquette, que la couche de design
   n'avait jamais repris. D'où : aucune élévation au survol, aucune lueur, et
   des accents de couleur laissés tels quels.

   Ces accents étaient faux, et vérifiables : « TFA Kids » portait
   `--acc: #1B4A85` (bleu) sous un bandeau cyan, et « Les manuels »
   `--acc: #092D59` — un bleu nuit presque invisible sur fond sombre — sous un
   bandeau vert. Le prix et les ✓ de cette bulle étaient donc illisibles.
   Chaque accent reprend maintenant la teinte claire de SON bandeau.

   La lisibilité en thème clair est traitée séparément : une teinte choisie
   pour ressortir sur du bleu nuit n'a pas le contraste voulu sur du blanc.
   ══════════════════════════════════════════════════════════════════════════ */
.site .pm, .site .card { --acc-l: var(--acc, var(--navy)); }
body:not(.dark) .site .pm, body:not(.dark) .site .card {
  --acc-l: color-mix(in srgb, var(--acc, var(--navy)) 78%, #000);
}

/* la couleur du prix et des coches suit la bulle */
.site .pm .p2, .site .pm .p2 span, .site .card .price { color: var(--acc-l); }
.site .pm ul li::before, .site .card ul.ck li::before { color: var(--acc-l); }
.site .pm ul li.no::before, .site .card ul.ck li.no::before {
  color: color-mix(in srgb, var(--crimson) 72%, var(--soft2));
}
body.dark .site .pm ul li.no::before, body.dark .site .card ul.ck li.no::before {
  color: #FF7E9C;
}
.site .pm h4 { color: var(--acc-l); }

/* ── l'élévation et la lueur, comme partout ailleurs sur le site ──
   Trois choses en même temps : la bulle monte, son ombre s'ouvre, et une
   lueur crimson suit le curseur à l'intérieur. Une seule de ces trois se
   remarque ; les trois ensemble se ressentent. */
.site .pm {
  position: relative; isolation: isolate;
  border-radius: var(--r-m);
  transition: transform var(--t-app) var(--e), box-shadow var(--t-app) var(--e),
              border-color var(--t-app) var(--e);
}
.site .pm::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit; pointer-events: none; opacity: 0;
  transition: opacity var(--t-app) var(--e);
  background: radial-gradient(320px circle at var(--mx, 50%) var(--my, 0%),
              color-mix(in srgb, var(--crimson) 22%, transparent), transparent 64%);
}
.site .pm:hover {
  transform: translateY(-6px);
  box-shadow: var(--sh2);
  border-color: color-mix(in srgb, var(--crimson) 40%, var(--brd));
}
.site .pm:hover::before { opacity: 1; }
/* le bandeau de couleur s'anime avec elle */
.site .pm .cap2 { transition: transform var(--t-app) var(--e), filter var(--t-app) var(--e); }
.site .pm:hover .cap2 { filter: brightness(1.25) saturate(1.15); }

/* la lueur des cartes classiques était trop discrète pour se voir */
.site .card::before {
  background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 0%),
              color-mix(in srgb, var(--crimson) 22%, transparent), transparent 64%);
}
.site .card:hover { transform: translateY(-6px); }


/* ══════════════════════════════════════════════════════════════════════════
   41. LA PASTILLE PASSE AU ROUGE QUAND ELLE CHANGE DE CÔTÉ

   La pastille glisse déjà au survol, en annonce du clic. Elle change aussi de
   couleur : bleu nuit tant qu'elle désigne l'option EN COURS, crimson dès
   qu'elle annonce un CHANGEMENT. Le sélecteur porte alors les deux couleurs
   de la marque, et l'on distingue d'un regard « voici où vous êtes » de
   « voici où vous allez ».

   Pour les familles de la collection — quatre onglets — la pastille alterne
   franchement : bleu, rouge, bleu, rouge. La couleur devient un repère de
   position autant qu'un état.
   ══════════════════════════════════════════════════════════════════════════ */
.site .audpuce, .site .ftpuce {
  background: var(--teinte, var(--navy));
  box-shadow: 0 4px 14px -4px color-mix(in srgb, var(--teinte, var(--navy)) 60%, transparent);
  transition: transform var(--t-app) var(--e), width var(--t-app) var(--e),
              background-color var(--t-app) var(--e), box-shadow var(--t-app) var(--e);
}
body.dark .site .audpuce, body.dark .site .ftpuce {
  background: var(--teinte, #23477B);
}


/* ══════════════════════════════════════════════════════════════════════════
   42. LES CATÉGORIES DE TFA TV

   Elles reçoivent la mécanique des onglets de la collection : une pastille
   qui glisse sous l'élément visé, bleu nuit et crimson en alternance.

   UNE DIFFÉRENCE DE TRAITEMENT, ET ELLE EST VOULUE : pas de capsule autour de
   la rangée. Neuf étiquettes qui se replient sur deux lignes dans un cadre
   arrondi donnent une forme bâtarde, ni ligne ni bloc. Ici la pastille se
   déplace seule sur un fond transparent — et les neuf contours individuels
   disparaissent, ce qui rend la rangée nettement plus calme.
   ══════════════════════════════════════════════════════════════════════════ */
.site .tvchips {
  position: relative; isolation: isolate;
  gap: 4px 6px; padding-bottom: 10px;
}
.site .tvchip, body.dark .site .tvchip {
  position: relative; z-index: 1;
  border-color: transparent;
  background: transparent;
  color: #A8C0DC;
  font-weight: 500;
  transition: color var(--t-app) var(--e), transform var(--t-etat) var(--e);
}
.site .tvchip:hover, body.dark .site .tvchip:hover { color: #fff; border-color: transparent; }
.site .tvchip.on, body.dark .site .tvchip.on {
  background: transparent; border-color: transparent; color: #fff; font-weight: 600;
}
/* quand la pastille est ailleurs, l'onglet actif reprend sa couleur de repos :
   deux étiquettes blanches en même temps ne disent plus laquelle est choisie */
.site .tvchips:hover .tvchip.on { color: #A8C0DC; }
.site .tvchips .tvchip:hover { color: #fff; }


/* ══════════════════════════════════════════════════════════════════════════
   43. L'OUVERTURE DU BANDEAU D'ACCUEIL

   ══ UN DÉFAUT SILENCIEUX, TROUVÉ EN PRÉPARANT CETTE ANIMATION ══
   `.heroimg` portait encore l'animation `kenburns` héritée de la maquette —
   26 secondes, `transform: scale(1)` → `scale(1.07)`, en boucle. Or une
   animation CSS écrase la propriété qu'elle anime, quelle que soit la
   spécificité du sélecteur qui la déclare par ailleurs.

   Conséquence : la parallaxe du bandeau n'a JAMAIS fonctionné. Mesuré à
   l'instant — `--py` passe bien de 4,73 px à 47,40 px au défilement, et la
   matrice réellement appliquée reste `matrix(1.0011, 0, 0, 1.0011, 0, 0)`.
   Aucune translation. Idem pour le suivi de la souris.

   Je ne l'avais pas vu parce que je vérifiais que les VARIABLES étaient
   écrites, pas que le mouvement était appliqué. On mesure l'effet, jamais
   l'entrée.

   ══ CE QUE FAIT L'OUVERTURE ══
   L'image entre à 1,34 et se pose à 1,07 en 2,6 s — un mouvement long, qui
   ralentit à l'arrivée au lieu de s'arrêter net. Le bandeau sort d'un noir
   plein qui s'efface en 1,9 s. Puis le sur-titre, le titre, le texte et les
   boutons se lèvent l'un après l'autre, 130 ms d'écart.

   `@property` permet d'animer `--zoom` comme un nombre. Là où il n'est pas
   compris — moins de 6 % des navigateurs — la variable ne s'anime pas : le
   bandeau s'affiche directement à sa taille finale, ce qui est exactement le
   repli souhaité.
   ══════════════════════════════════════════════════════════════════════════ */
@property --zoom {
  syntax: '<number>';
  inherits: false;
  initial-value: 1.07;
}

.site .hero2 .heroimg {
  /* une seule déclaration porte les trois mouvements : le suivi de la souris,
     la parallaxe de défilement, et l'échelle d'ouverture */
  animation: none;                      /* on coupe le `kenburns` de la maquette */
  transform: translate3d(var(--mx2, 0px), calc(var(--my2, 0px) + var(--py, 0px)), 0)
             scale(var(--zoom));
}
/* Les animations d'ouverture sont GARDÉES PAR UNE CLASSE, et pas posées en
   dur. C'est la condition pour pouvoir les relancer : retirer la classe
   supprime l'animation, la remettre la redémarre de zéro. Changer un simple
   `animation-delay` ne relance rien — l'animation est déjà terminée, elle
   reste terminée. */
body.ouvre .site .hero2 .heroimg {
  animation: tfa-ouverture 2.6s var(--e-soft) 3.7s both;
}
@keyframes tfa-ouverture {
  from { --zoom: 1.34; }
  to   { --zoom: 1.07; }
}

/* PAS DE DÉRIVE PERPÉTUELLE. J'avais d'abord reporté le `kenburns` sur
   l'image elle-même, pour ne rien perdre. Mesuré : une animation infinie sur
   un PNG de 500 Ko force le compositeur à retravailler cette couche à chaque
   image, même quand le bandeau est sorti de l'écran — 20 % d'images longues
   en plus sur un défilement complet.
   Et elle n'a plus de raison d'être : ce `kenburns` existait justement parce
   que RIEN d'autre ne bougeait dans le bandeau. Maintenant que la parallaxe
   fonctionne, c'est elle qui donne le mouvement, et elle s'arrête d'elle-même
   dès que le bandeau quitte l'écran. */

/* le noir dont le bandeau émerge.
   IL EST TRANSPARENT AU REPOS. L'animation, gardée par `body.ouvre`, le pose
   à l'opaque puis l'efface. Écrit dans l'autre sens — opaque au repos — le
   bandeau resterait NOIR chez qui n'a pas de JavaScript, chez qui a demandé
   moins d'animation, et sur toute page où la classe n'est pas posée. La règle
   de repos doit toujours être l'état SÛR. */
.site .hero2::after {
  content: ""; grid-area: 1 / 1; z-index: 2;
  background: #05090F; opacity: 0; pointer-events: none;
}
body.ouvre .site .hero2::after {
  animation: tfa-devoile 1.9s var(--e) 3.7s both;
}
@keyframes tfa-devoile {
  0%   { opacity: 1; }
  100% { opacity: 0; }
}

/* le texte se lève, un élément après l'autre */
body.ouvre .site .hero2 .heroctn > * {
  animation: tfa-leve .95s var(--e) both;
}
@keyframes tfa-leve {
  from { opacity: 0; transform: translate3d(0, 30px, 0); }
  to   { opacity: 1; transform: none; }
}
body.ouvre .site .hero2 .heroctn > *:nth-child(1) { animation-delay: 4.30s; }
body.ouvre .site .hero2 .heroctn > *:nth-child(2) { animation-delay: 4.43s; }
body.ouvre .site .hero2 .heroctn > *:nth-child(3) { animation-delay: 4.56s; }
body.ouvre .site .hero2 .heroctn > *:nth-child(4) { animation-delay: 4.69s; }
body.ouvre .site .hero2 .heroctn > *:nth-child(5) { animation-delay: 4.82s; }

/* les deux lueurs arrivent en dernier, quand le cadre est posé */
body.ouvre .site .hero2 .lueur {
  animation: tfa-lueur 1.6s var(--e) 4.8s both;
}
@keyframes tfa-lueur { from { opacity: 0; } to { opacity: .8; } }

@media (prefers-reduced-motion: reduce) {
  body.ouvre .site .hero2 .heroimg,
  body.ouvre .site .hero2::after,
  body.ouvre .site .hero2 .heroctn > *,
  body.ouvre .site .hero2 .lueur { animation: none; }
  .site .hero2::after { display: none; }
}


/* ══════════════════════════════════════════════════════════════════════════
   44. L'OUVERTURE DU LOGO — arrivée, séparation, rideau

   Trois temps, enchaînés sans coupure :

   1. L'ARRIVÉE (0 → 1,15 s) — le logo entre de dos, en pivotant sur trois
      axes, et vient se poser de face. Deux copies fantômes le suivent avec
      70 et 140 ms de retard : ce sont elles qui donnent la traînée de la
      référence Apple. Une seule copie ne se verrait pas, quatre feraient une
      bouillie.

   2. LA SIGNATURE ET LA PAUSE (0,95 → 3,15 s) — le nom se pose sous les
      bulles, ligne après ligne, et TOUT S'ARRÊTE DEUX SECONDES.

      Cette pause est le seul but de la séquence. Une marque qu'on ne lit pas
      n'est pas une marque : le mouvement attire l'œil, l'immobilité lui donne
      le temps de lire. Deux secondes, c'est la durée mesurée d'une lecture
      confortable de trois lignes courtes — en dessous on devine, au-dessus on
      s'impatiente. Le reste de la chronologie a été décalé d'autant plutôt
      que raccourci : on n'achète pas du temps de lecture en accélérant le
      geste qui l'entoure.

   3. LA SÉPARATION (3,15 → 4,20 s) — les deux bulles, jointes comme dans le
      logo, s'écartent chacune vers son côté en pivotant légèrement. Chacune
      part vers le panneau de SA couleur et s'y fond : le rideau n'apparaît
      pas de nulle part, ce sont les bulles qui deviennent le rideau. Le nom
      s'efface vers le haut au même instant, il ne reste pas orphelin.

   4. LE RIDEAU (3,80 → 4,80 s) — les deux panneaux s'ouvrent. La courbe
      démarre lentement puis file : un rideau a du poids, il ne part pas
      instantanément. Le bandeau d'accueil est déjà en train de jouer son
      ouverture derrière — on le découvre en mouvement, pas à l'arrêt.

   ══ POURQUOI ELLE REJOUE ══
   Elle jouait avant UNE FOIS PAR SESSION, retenue dans `sessionStorage` :
   trois secondes de péage à chaque page semblaient trop cher. Le compromis
   est levé à la demande — elle rejoue à chaque chargement, et à la demande en
   survolant le logo de la barre plus d'une seconde.

   C'est la seconde de survol qui rend la chose tenable : personne ne pose sa
   souris une seconde pleine sur un logo par accident. Un déclenchement au
   simple survol relancerait le rideau chaque fois qu'on va vers le menu.
   ══════════════════════════════════════════════════════════════════════════ */
.site .intro {
  position: absolute; inset: 0; z-index: 12;
  overflow: hidden;
  perspective: 1100px;
  pointer-events: none;
}
.site .intro .rideau {
  position: absolute; top: -2px; bottom: -2px; width: 50.4%;
  animation: none;
}
/* LES PANNEAUX COMMENCENT PRESQUE NOIRS.
   Première version : ils portaient d'emblée le bleu nuit et le crimson de la
   marque. Résultat vérifié à l'écran — la bulle bleue sur un panneau bleu et
   la bulle rouge sur un panneau rouge : on ne voyait plus que les contours.
   Ils partent donc d'un noir bleuté et prennent leur couleur PENDANT la
   séparation. La bulle verse littéralement sa teinte dans son panneau, et
   c'est ce qui fait comprendre que le rideau, c'est elle. */
.site .intro .rideau.g {
  left: 0;
  background: #070B12;
  animation: tfa-teinte-g .55s var(--e) 3.52s both,
             tfa-rideau-g 1s cubic-bezier(.62, 0, .2, 1) 3.8s both;
}
.site .intro .rideau.d {
  right: 0;
  background: #070B12;
  animation: tfa-teinte-d .55s var(--e) 3.52s both,
             tfa-rideau-d 1s cubic-bezier(.62, 0, .2, 1) 3.8s both;
}
@keyframes tfa-teinte-g { to { background: #0C2B52; } }
@keyframes tfa-teinte-d { to { background: #B4082F; } }
@keyframes tfa-rideau-g { from { transform: none; } to { transform: translate3d(-101%, 0, 0); } }
@keyframes tfa-rideau-d { from { transform: none; } to { transform: translate3d(101%, 0, 0); } }

/* le logo, au centre */
/* LE BLOC EST REMONTÉ, PAS CENTRÉ. Le nom pend sous les bulles : centrer les
   bulles laisse la signature basse et la composition penche. On remonte donc
   l'ensemble de la moitié de la hauteur du nom, pour que ce soit le GROUPE qui
   soit centré — c'est ce que voit l'œil, pas la boîte. */
.site .intro .ilogo {
  /* LA HAUTEUR COMPTE AUTANT QUE LA LARGEUR.
     `clamp(230px, 27vw, 430px)` ne connaît que la largeur de l'écran. Sur un
     téléphone posé en paysage, ou sur un navigateur dont les barres mangent
     la moitié de la fenêtre, le bandeau devient bas et le groupe logo +
     signature — qui mesure 1,02 fois cette valeur en hauteur — se fait
     couper par le `overflow: hidden` du rideau. Et pendant la culbute, où le
     logo pivote sur trois axes, il balaie encore plus large que sa boîte.
     On borne donc aussi par la hauteur disponible. */
  --lw: clamp(180px, min(27vw, 40vh), 430px);
  position: absolute; left: 50%; top: 50%;
  width: var(--lw); aspect-ratio: 420 / 312;
  /* la signature mesure .282 × --lw de haut, creux compris : on remonte de la
     moitié, pour que le GROUPE soit centré et non la seule boîte des bulles */
  transform: translate(-50%, calc(-50% - var(--lw) * .141));
  transform-style: preserve-3d;
}
.site .intro .ib {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain;
}
/* l'arrivée : le conteneur pivote, les bulles restent jointes */
.site .intro .tour {
  position: absolute; inset: 0;
  animation: tfa-arrive 1.15s cubic-bezier(.2, .7, .2, 1) both;
  transform-origin: 50% 50%;
}
@keyframes tfa-arrive {
  from { opacity: 0; transform: rotate3d(.25, 1, .12, -168deg) scale(.28) translateZ(-260px); }
  55%  { opacity: 1; }
  to   { opacity: 1; transform: rotate3d(.25, 1, .12, 0deg) scale(1) translateZ(0); }
}
/* ── LES TRAÎNÉES DOIVENT AVOIR DISPARU QUAND LE LOGO SE POSE ──────────────
   Elles s'effaçaient de 0,72 à 1,28 s, alors que l'arrivée se termine à
   1,15 s. Pendant les quatre dernières dixièmes, les trois copies étaient
   donc EXACTEMENT superposées — et deux d'entre elles sont floutées. Ce que
   l'on voyait n'était pas une traînée de mouvement mais un logo dédoublé,
   et le premier mot de la personne qui le regarde est « c'est flou ».

   Une traînée n'a de sens que TANT QUE ÇA BOUGE. Elles s'effacent maintenant
   entre 0,38 et 0,52 s, en pleine course, quand l'écart entre les copies est
   encore lisible comme de la vitesse. Le logo se pose net.

   Le flou lui-même est divisé par deux : à cette vitesse il n'a pas besoin
   d'être appuyé pour se lire, et un flou léger sur une copie qui s'efface
   vite coûte beaucoup moins cher à composer. */
.site .intro .tour.fantome { pointer-events: none; }
.site .intro .tour.f1 { opacity: .30; filter: blur(.5px); }
.site .intro .tour.f2 { opacity: .15; filter: blur(1.2px); }
.site .intro .tour.fantome { animation-name: tfa-arrive, tfa-efface; animation-duration: 1.15s, .34s; }
.site .intro .tour.f1 { animation-delay: .07s, .38s; }
.site .intro .tour.f2 { animation-delay: .14s, .32s; }
@keyframes tfa-efface { to { opacity: 0; } }

/* la séparation : chaque bulle rejoint son panneau */
.site .intro .tour.vraie .bleue  { animation: tfa-part-g 1.05s cubic-bezier(.5, 0, .25, 1) 3.15s both; }
.site .intro .tour.vraie .rouge  { animation: tfa-part-d 1.05s cubic-bezier(.5, 0, .25, 1) 3.15s both; }
@keyframes tfa-part-g {
  0%   { transform: none; opacity: 1; }
  100% { transform: translate3d(-124%, 0, 0) rotate(-13deg) scale(1.1); opacity: 0; }
}
@keyframes tfa-part-d {
  0%   { transform: none; opacity: 1; }
  100% { transform: translate3d(124%, 0, 0) rotate(13deg) scale(1.1); opacity: 0; }
}

/* ── LA SIGNATURE ──────────────────────────────────────────────────────────
   « TRULY FLUENT / ACADEMY / Fluency starts here », DÉCOUPÉE DANS LE LOGO
   OFFICIEL et non recomposée en CSS.

   Elle l'a d'abord été, en texte : trois `letter-spacing` réglés à l'œil pour
   approcher le dessin. C'était honnête et ce n'était pas le logo — ni la
   bonne fonte, ni le bon interlettrage, et une compensation de `text-indent`
   à maintenir pour que le centrage ne parte pas de travers. Le fichier de la
   charte existe : on y découpe le texte, et la question ne se pose plus.

   ══ LES PROPORTIONS VIENNENT DU LOGO, PAS D'UN CHOIX ══
   Mesuré sur le fichier officiel : les bulles font 1403 px de large, « TRULY
   FLUENT » 1410, « ACADEMY » 1401, l'accroche 1192. Les deux premières lignes
   sont calées sur la MÊME largeur que le dessin, la troisième sur 85 %. Ce
   sont ces rapports qui sont écrits ici, en fraction de `--lw`.

   C'est aussi ce qui règle la question du responsive. La version en texte
   avait un défaut mesuré : le logo touche son plancher de 230 px dès 851 px
   de large et n'y bouge plus, tandis que le texte, lui, continuait de
   rétrécir avec la fenêtre — à 768 px on obtenait de grosses bulles
   surmontant une signature de 19 px. Une largeur exprimée en fraction du
   logo ne peut pas avoir ce défaut : il n'y a plus qu'une seule mesure.
   ────────────────────────────────────────────────────────────────────────── */
.site .intro .imot {
  position: absolute; left: 50%; top: 100%;
  margin-top: calc(var(--lw) * .0385);      /* le creux du logo : 54 / 1403 */
  transform: translateX(-50%);
  width: var(--lw);
  display: flex; flex-direction: column; align-items: center;
  filter: drop-shadow(0 2px 20px rgba(0, 0, 0, .55));
}
.site .intro .imot img { display: block; height: auto; }
.site .intro .imot .m1 { width: calc(var(--lw) * 1.005); }
.site .intro .imot .m2 { width: calc(var(--lw) * .999); margin-top: calc(var(--lw) * .0335); }
.site .intro .imot .m3 { width: calc(var(--lw) * .850); margin-top: calc(var(--lw) * .052); }

/* Entrée puis sortie, DEUX animations et non une seule longue.
   La sortie est en `forwards` et pas en `both` : `both` remplirait aussi vers
   l'ARRIÈRE, c'est-à-dire que sa première image écraserait l'entrée pendant
   toute la durée de l'attente — la ligne resterait invisible. `forwards` ne
   retient que la fin. C'est la seule combinaison qui laisse les deux
   animations cohabiter sur les mêmes propriétés. */
.site .intro .imot > * {
  animation: tfa-mot .8s var(--e) both,
             tfa-mot-sort .52s var(--e) forwards;
}
.site .intro .imot > *:nth-child(1) { animation-delay: .95s, 3.15s; }
.site .intro .imot > *:nth-child(2) { animation-delay: 1.07s, 3.19s; }
.site .intro .imot > *:nth-child(3) { animation-delay: 1.20s, 3.23s; }
@keyframes tfa-mot {
  from { opacity: 0; transform: translate3d(0, 15px, 0); filter: blur(7px); }
  to   { opacity: 1; transform: none; filter: blur(0); }
}
@keyframes tfa-mot-sort {
  to { opacity: 0; transform: translate3d(0, -16px, 0); filter: blur(5px); }
}

/* une lueur qui naît au moment de la séparation, sur la ligne de partage */
.site .intro .fente {
  position: absolute; left: 50%; top: 0; bottom: 0; width: 2px; margin-left: -1px;
  background: linear-gradient(180deg, transparent, rgba(255,255,255,.85), transparent);
  opacity: 0;
  animation: tfa-fente 1.15s var(--e) 3.65s both;
}
@keyframes tfa-fente {
  0%   { opacity: 0; transform: scaleY(.2); }
  28%  { opacity: 1; transform: scaleY(1); }
  100% { opacity: 0; transform: scaleY(1) scaleX(3); }
}

@media (prefers-reduced-motion: reduce) {
  .site .intro { display: none; }
}


/* ══════════════════════════════════════════════════════════════════════════
   45. LE TÉLÉPHONE PRIS AU SÉRIEUX

   La section 37 posait déjà trois paliers, et annonçait « rien ne descend
   sous 15 px ». Mesuré sur un vrai téléphone de 390 px : 83 éléments de
   texte sous 13 px sur la seule page d'accueil, 77 sur la page du Club.
   L'intention était juste, la règle ne l'était pas — elle nommait une
   poignée de sélecteurs (`p.lead`, `.card p`, `.mini`, `ul.ck li`) et
   laissait tout le reste au réglage de l'ordinateur.

   ══ POURQUOI CE N'EST PAS UNE QUESTION DE GOÛT ══
   Sur un écran d'ordinateur, l'œil est à 60 cm et la fenêtre fait 1 400 px de
   large : 11,5 px y sont un gris typographique discret, exactement ce qu'on
   veut pour une mention secondaire. Sur un téléphone tenu à 30 cm, le même
   11,5 px occupe le même angle visuel qu'un 6 px sur l'écran — c'est-à-dire
   qu'il n'est plus discret, il est illisible. La taille de police n'est pas
   une valeur esthétique portable d'un appareil à l'autre.

   La règle n'est donc plus une liste de noms mais une liste de RÔLES : les
   listes d'avantages, les mentions de pied de page, les étiquettes, les
   horodatages, les compteurs. Un rôle survit à la prochaine page ; un nom de
   classe, non.

   ══ ET LES CIBLES TACTILES ══
   Mesuré au même endroit : 24 liens et boutons de moins de 40 px de haut,
   dont onze liens de pied de page à 30 px et sept icônes sociales à 37 px.
   Le seuil confortable du doigt est de 44 px — ce n'est pas une convention
   d'Apple, c'est la largeur moyenne d'une pulpe de pouce. En dessous, on ne
   rate pas la cible une fois sur dix : on la rate, on revient, et on finit
   par ne plus essayer.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 640px) {

  /* ── 1. LE PLANCHER DE LISIBILITÉ ────────────────────────────────────── */

  /* les listes d'avantages : le contenu le plus lu de toute la page tarifs */
  .site ul.ck li,
  .site .pm ul li,
  .site .card ul li,
  .site .rlist li { font-size: 15px; line-height: 1.5; }

  /* le pied de page : onze liens à 12,5 px, c'est le plan du site rendu
     inutilisable au moment précis où l'on en a besoin */
  .site footer.site .cols a,
  .site footer.site .cols p { font-size: 15px; }
  .site footer.site .cols h5 { font-size: 13px; letter-spacing: .1em; }
  .site footer.site .bot a,
  .site footer.site .bot span,
  .site footer.site .bot em { font-size: 13.5px; }
  .site footer.site .cols .lg b { font-size: 13px; }

  /* les sur-titres : ils sont en capitales espacées, ils peuvent rester
     petits — mais pas à 11 px */
  .site .eyebrow, .site .kick2 { font-size: 12.5px; }

  /* étiquettes, pastilles, compteurs */
  .site .badge, .site .billbadge, .site .lb, .site .bientot,
  .site .tags2 span, .site .chit span, .site .clubkpi span,
  .site .member .row span, .site .ladder .res b, .site .ladder .st3 span,
  .site table.inv th { font-size: 12.5px; }

  /* la salle de discussion du Club : c'est là que le texte était le plus
     petit de tout le site — jusqu'à 9 px, soit une ligne qu'on ne lit pas,
     qu'on devine */
  .site .roomgrp { font-size: 12.5px; }
  .site .room .rt b { font-size: 14px; }
  .site .room .rt span, .site .mini2 { font-size: 12.5px; }
  .site .msg .nm { font-size: 13px; }
  .site .msg .tm, .site .msg .mtag { font-size: 11.5px; }
  .site .msg .bb { font-size: 15px; }

  /* la barre du haut : « Se connecter » et « S'inscrire » sont les deux
     seuls mots de la barre sur téléphone, ils ne peuvent pas être les plus
     petits de l'écran */
  .site .topnav .authbtns a { font-size: 13.5px; }

  /* ── 2. LES CIBLES DU DOIGT ──────────────────────────────────────────── */

  /* On agrandit par le REMBOURRAGE et non par la hauteur : une hauteur
     imposée sur un lien en ligne ne fait rien du tout — la boîte d'un élément
     `inline` ignore `height`. Le rembourrage vertical, lui, agrandit bien la
     zone cliquable. C'est la même raison qui fait qu'un `min-height` sur un
     `<a>` de pied de page n'aurait rien changé aux 30 px mesurés. */
  .site footer.site .cols a {
    display: block; padding-block: 8px; margin-block: -2px;
  }
  .site footer.site .bot a { display: inline-block; padding-block: 9px; }
  /* `.res` DÉSIGNE DEUX CHOSES DIFFÉRENTES. Écrite sans portée, la règle
     `width: 44px; height: 44px` a touché les deux : les liens sociaux du pied
     de page, qui font toute la largeur, se sont écrasés en carrés de 44 px —
     icône par-dessus le texte — et la ligne de résultat du test de niveau,
     qui porte la même classe pour une raison sans rapport, s'est repliée en
     une colonne de 44 px qui débordait sur la section suivante.
     Une cible tactile s'agrandit par sa HAUTEUR MINIMALE, jamais par une
     largeur imposée : la largeur appartient à la mise en page. */
  .site footer.site a.res { min-height: 44px; display: flex; align-items: center; }
  .site .ftab, .site .tvchip, .site .chip { min-height: 44px; }
  .site .btn, .site .btn.gh, .site .btn.sm, .site .btn.nv, .site .hbtn {
    min-height: 46px;
  }
  .site .topnav .tiroirbtn, .site .topnav .lg { min-height: 44px; min-width: 44px; }
  .site .topnav .authbtns a { display: inline-flex; align-items: center; min-height: 44px; }

  /* ── 3. LES BULLES TARIFAIRES : LE VIDE REMONTE ──────────────────────── */

  /* Les six formules défilent au doigt, côte à côte, et le défilement les
     met toutes à la hauteur de la plus grande. La formule « Découverte », qui
     a trois avantages là où « Premium » en a huit, se retrouvait donc avec
     81 px de vide MESURÉS sous son bouton — un trou au bas de la carte, qui
     la fait paraître inachevée.
     On ne peut pas supprimer ce vide : l'égalité des hauteurs est ce qui rend
     la comparaison lisible. On le DÉPLACE. Le bouton est poussé contre le bas
     de la carte ; le vide passe au-dessus de lui, où il devient une respiration
     entre la liste et l'action, et les six boutons s'alignent sur une même
     ligne d'un bout à l'autre du défilement. */
  .site .grid4 > .card { display: flex; flex-direction: column; }
  .site .grid4 > .card > .btn { margin-top: auto; }
  .site .grid4 > .card > .sep { margin-top: auto; }
  .site .grid4 > .card > .sep ~ .btn { margin-top: 0; }

  /* ── 4. LA RESPIRATION VERTICALE ─────────────────────────────────────── */

  /* Le passage d'une section à l'autre coûtait jusqu'à 110 px de noir plein
     sur un écran qui en fait 844. On resserre — mais sur la SECTION, pas sur
     les cartes : c'est l'espace entre les blocs qui était trop grand, pas
     celui à l'intérieur. Le confort de lecture vient du second. */
  :root { --sect: clamp(40px, 9.5vw, 58px); }
  .site .quickrow { margin-top: -22px; }
}

/* ── TABLETTE : le même plancher, un cran au-dessus ───────────────────────
   Entre 641 et 1 080 px on tient l'appareil à la même distance qu'un
   téléphone — c'est un écran tactile, pas un petit ordinateur. Le plancher
   s'applique donc aussi, simplement moins haut : la place ne manque pas. */
@media (min-width: 641px) and (max-width: 1080px) {
  .site ul.ck li, .site .pm ul li, .site .card ul li { font-size: 14.5px; }
  .site footer.site .cols a { font-size: 14.5px; display: block; padding-block: 7px; }
  .site footer.site .cols h5 { font-size: 12.5px; }
  .site .eyebrow, .site .kick2 { font-size: 12px; }
  .site .roomgrp, .site .room .rt span, .site .mini2 { font-size: 12.5px; }
  .site .msg .nm { font-size: 13px; }
  .site .msg .tm, .site .msg .mtag { font-size: 11.5px; }
  /* `.res` DÉSIGNE DEUX CHOSES DIFFÉRENTES. Écrite sans portée, la règle
     `width: 44px; height: 44px` a touché les deux : les liens sociaux du pied
     de page, qui font toute la largeur, se sont écrasés en carrés de 44 px —
     icône par-dessus le texte — et la ligne de résultat du test de niveau,
     qui porte la même classe pour une raison sans rapport, s'est repliée en
     une colonne de 44 px qui débordait sur la section suivante.
     Une cible tactile s'agrandit par sa HAUTEUR MINIMALE, jamais par une
     largeur imposée : la largeur appartient à la mise en page. */
  .site footer.site a.res { min-height: 44px; display: flex; align-items: center; }
}


/* ══════════════════════════════════════════════════════════════════════════
   46. LE PLANCHER, DEUXIÈME PASSE — ET POURQUOI `.site.site`

   La passe précédente a fait tomber le compte de 83 à 36 sur l'accueil, mais
   une partie des déclarations n'a rien changé : `.site .bientot` restait à
   10 px, `.site .kick2` à 10,5 px. Ce ne sont pas des règles oubliées, ce
   sont des règles ÉCRASÉES — la règle d'origine était plus spécifique que la
   correction, et une requête de média n'ajoute aucune spécificité.

   C'est le même piège que la grille à trois colonnes qui refusait de se
   replier, et que la pastille du sélecteur mensuel/annuel. Trois fois le
   même. La parade adoptée ici : répéter la classe racine. `.site.site` vaut
   deux classes au lieu d'une, ce qui suffit à passer devant sans recourir à
   `!important` — lequel n'aurait fait que déplacer le problème d'un cran
   plus loin, en rendant la règle suivante impossible à écrire.

   Répéter une classe est laid. C'est aussi la seule façon honnête de dire
   « ceci l'emporte » sans fermer la porte à la correction d'après.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 640px) {
  .site.site .bientot, .site.site .kick2, .site.site .eyebrow,
  .site.site .lb, .site.site .sub, .site.site .badge,
  .site.site .tcard .foot span, .site.site .kpicard .sub,
  .site.site .nslot, .site.site .rn2, .site.site .rv, .site.site .acc,
  .site.site .clubkpi span, .site.site .chit span, .site.site .roomgrp,
  .site.site .room .rt span, .site.site .mini2,
  .site.site .cal .dn, .site.site .cal .slot, .site.site .cal .slot span,
  .site.site .st, .site.site table.tb th, .site.site table.inv th,
  .site.site .ladder .st3 span, .site.site .ladder .res b,
  .site.site .tvhero .meta span, .site.site .tvhero .tag,
  .site.site .tvfoot span { font-size: 13px; }

  .site.site .msg .tm, .site.site .msg .mtag { font-size: 12px; }
  .site.site .msg .nm { font-size: 13.5px; }

  /* LES AFFICHES DE TFA TV GROSSISSENT PLUTÔT QUE LEURS ÉTIQUETTES.
     La durée et la mention « Premium » sont posées SUR l'affiche : les
     grossir seules les ferait manger la vignette. On élargit donc l'affiche
     — de 39 à 45 % de la largeur d'écran, soit deux et quart visibles au lieu
     de deux et demie — et les étiquettes suivent en proportion. On perd un
     quart d'affiche de vue d'ensemble, on gagne une image lisible. */
  .site.site .tvcard { flex: 0 0 45vw; }
  .site.site .prem { font-size: 10px; padding: 3px 7px; letter-spacing: .1em; }
  .site.site .tvcard .thumb .lk2,
  .site.site .tvcard .thumb .dur { font-size: 12px; padding: 3px 8px; }
  .site.site .tvchip { font-size: 13.5px; }

  /* LE BOUTON CONTRE LE BAS DE LA CARTE.
     `margin-top: auto` était bien écrit mais perdait contre une règle plus
     spécifique — d'où les 76 px de vide qui subsistaient sous le bouton.
     `:last-child` ajoute le poids qui manquait. */
  .site.site .grid4 > .card { display: flex; flex-direction: column; }
  .site.site .grid4 > .card > .btn:last-child { margin-top: auto; }

  /* LE TABLEAU DÉFILE, IL NE DÉBORDE PAS.
     Mesuré : 504 px de contenu dans une carte de 338. Un tableau de cinq
     colonnes ne se replie pas — le rapport entre les colonnes EST
     l'information. On le fait donc défiler horizontalement dans sa carte,
     comme les rangées d'affiches, plutôt que de le laisser pousser la page
     entière de 159 px vers la droite. */
  .site.site .card > table.tb,
  .site.site .card > table.inv { display: block; overflow-x: auto; }
  .site.site table.tb, .site.site table.inv { min-width: 100%; }
}


/* ══════════════════════════════════════════════════════════════════════════
   47. LE STYLE EN LIGNE QUI GAGNAIT TOUJOURS

   Trois tentatives pour pousser le bouton d'une carte tarifaire contre son
   bas — `margin-top: auto`, puis la même chose plus spécifique, puis encore
   plus spécifique avec `:last-child`. Aucune n'a rien changé. Le vide sous le
   bouton restait mesuré à 97 px.

   La cause n'était dans aucune feuille de style : chaque bouton portait
   `style="width:100%;text-align:center;margin-top:15px"` dans le balisage.
   Un attribut `style` bat TOUTE règle de sélecteur, quelle qu'en soit la
   spécificité — il n'est pas en haut de l'échelle, il est à côté. Aucune
   surenchère de classes ne pouvait aboutir, et j'ai perdu deux passes à
   monter une échelle qui ne menait nulle part.

   Il restait `!important`, qui aurait marché. Ç'aurait été soigner le
   symptôme : neuf boutons, trois propriétés purement présentationnelles
   écrites dans le contenu, qu'aucune règle ne pourra jamais adapter à un
   appareil. Ils portent maintenant une classe, et la règle vit ici.

   LEÇON — quand une règle CSS « ne prend pas » malgré une spécificité
   croissante, vérifier l'attribut `style` du balisage AVANT d'ajouter un
   sélecteur de plus. Le symptôme d'une règle écrasée par une autre règle et
   celui d'une règle écrasée par un style en ligne sont identiques ; le second
   ne se corrige jamais depuis la feuille de style.
   ══════════════════════════════════════════════════════════════════════════ */
.site .cardcta { width: 100%; text-align: center; margin-top: 15px; }

@media (max-width: 640px) {
  /* le bouton descend au bas de la carte, le vide passe au-dessus de lui */
  .site .grid4 > .card > .cardcta:last-child { margin-top: auto; }
}


/* ══════════════════════════════════════════════════════════════════════════
   48. LE RESTE, ET LE MÊME PLANCHER SUR TABLETTE

   Les trente dernières mentions sous 13 px, une par une : compteurs, libellés
   de champ, âges, notes de bas de carte, horodatages du Club. Elles sont
   nommées ici plutôt que rattrapées par une règle générale — parce qu'une
   règle générale du type `font-size: max(13px, 1em)` posée sur tous les
   éléments de texte écraserait aussi les titres, dont la taille propre est
   supérieure à celle de leur parent. Le remède serait pire.

   ══ ET LA TABLETTE ══
   Mesuré à 820 px : de 15 à 49 mentions sous 13 px par page, contre 1 à 13
   sur téléphone. Le palier tablette n'avait reçu qu'une poignée de règles.
   C'est une incohérence de raisonnement : une tablette se tient à la même
   distance qu'un téléphone et se touche du même doigt. Le plancher de
   lisibilité et le seuil de 44 px valent donc jusqu'à 1 080 px, sans
   exception — c'est le NOMBRE DE COLONNES qui distingue les deux appareils,
   pas la taille du texte.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 1080px) {
  .site.site .lg b,
  .site.site .row > div > span, .site.site .ag, .site.site .incl b,
  .site.site .nat p, .site.site .billbadge, .site.site .rgpd b,
  .site.site .acbadge, .site.site .acnote, .site.site .accessbar .txt span,
  .site.site .roomhead b, .site.site .roomsearch,
  .site.site .livebar b, .site.site .livebar span, .site.site .join,
  .site.site .fixit b, .site.site .fixit s, .site.site .fld2,
  .site.site .enonly b, .site.site .gain, .site.site .counter b,
  .site.site .report p, .site.site .rline span,
  .site.site .alab, .site.site .asep span, .site.site .aoubli a,
  .site.site .authcote .sur, .site.site .vl small,
  .site.site .lock, .site.site .day .slot span, .site.site .cal .slot span,
  .site.site .ladder > div, .site.site .grid2 > div > div { font-size: 13px; }

  /* les mêmes rôles que sur téléphone : la tablette n'est pas un petit
     ordinateur, c'est un grand téléphone */
  .site.site ul.ck li, .site.site .pm ul li, .site.site .card ul li,
  .site.site .rlist li { font-size: 14.5px; line-height: 1.5; }
  .site.site .eyebrow, .site.site .kick2, .site.site .bientot,
  .site.site .badge, .site.site .lb, .site.site .sub, .site.site .st,
  .site.site .nslot, .site.site .rn2, .site.site .rv, .site.site .acc,
  .site.site .clubkpi span, .site.site .chit span, .site.site .roomgrp,
  .site.site .room .rt span, .site.site .mini2, .site.site .tvchip,
  .site.site table.tb th, .site.site table.inv th,
  .site.site .ladder .st3 span, .site.site .ladder .res b,
  .site.site .tcard .foot span, .site.site .kpicard .sub,
  .site.site .tvhero .meta span, .site.site .tvfoot span { font-size: 13px; }
  .site.site footer.site .cols h5 { font-size: 13px; letter-spacing: .1em; }

  /* LE PIED DE PAGE ATTEINT ENFIN 44 PX.
     Le rembourrage posé à la passe précédente perdait lui aussi contre une
     règle plus spécifique : mesuré à 34 px au lieu des 46 attendus. Il fallait
     le même poids de sélecteur que le reste de cette section. */
  .site.site footer.site .cols a {
    display: block; font-size: 14.5px; padding-block: 11px; margin-block: -3px;
  }
  .site.site footer.site .bot a { display: inline-block; padding-block: 12px; }
  .site.site footer.site a.res { min-height: 44px; display: flex; align-items: center; }
  .site.site .btn, .site.site .hbtn { min-height: 46px;
    display: inline-flex; align-items: center; justify-content: center; }
  .site.site .ftab, .site.site .chip { min-height: 44px; }

  /* le calendrier des créneaux : 37 mentions sous 13 px à lui seul sur
     tablette, dont les heures elles-mêmes à 9 px */
  .site.site .cal .dn, .site.site .cal .slot,
  .site.site .day .dn, .site.site .day .slot { font-size: 13px; }
  .site.site .msg .bb, .site.site .msg b, .site.site .room b,
  .site.site .thread b { font-size: 14px; }
  .site.site footer.site .cols p, .site.site footer.site .bot a,
  .site.site footer.site .bot span, .site.site footer.site .bot em { font-size: 13.5px; }

  /* le bouton au bas de la carte vaut aussi sur tablette : c'est le même
     défilement horizontal, donc les mêmes hauteurs égalisées */
  .site.site .grid4 > .card { display: flex; flex-direction: column; }
  .site.site .grid4 > .card > .cardcta:last-child { margin-top: auto; }
}


/* ══════════════════════════════════════════════════════════════════════════
   49. LE PLUS PETIT ÉCRAN REPREND SES DROITS

   Le plancher de lisibilité de la section 45 a porté « Se connecter » et
   « S'inscrire » de 12 à 13,5 px, et leur a donné 44 px de haut. Bon pour le
   doigt, et l'audit l'a immédiatement sanctionné : 20 px de débordement
   horizontal sur les DIX-SEPT pages, à 320 px de large. La barre du haut ne
   rentrait plus.

   C'est le compromis inévitable d'une barre fixe : chaque pixel donné à un
   élément est pris à un autre. À 320 px il n'y a plus de marge à distribuer.
   On rend donc le pixel typographique — les deux libellés redescendent — mais
   PAS la hauteur de 44 px, qui ne coûte rien en largeur. Le doigt garde sa
   cible, l'œil perd un demi-point sur deux mots qu'il n'a pas besoin de lire.

   320 px n'est pas un cas d'école : c'est un iPhone SE, et c'est aussi tout
   téléphone récent dont l'utilisateur a agrandi la police du système.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 400px) {
  .site.site .topnav .authbtns a { font-size: 12px; }
  .site.site .topnav .authbtns .sgup,
  .site.site .topnav .authbtns .lgin { padding-inline: 9px; }
  .site.site .topnav .rt { gap: 6px; }
}


/* ══════════════════════════════════════════════════════════════════════════
   50. LA RANGÉE D'ONGLETS SUR TÉLÉPHONE : PLUS DE CADRE, DONC PLUS DE CADRE
       COUPÉ

   Les quatre catégories de la collection tiennent sur 527 px. Un téléphone en
   offre 350. La rangée défile donc — c'est le bon comportement — mais elle
   porte un CADRE en pilule, bordure et fond compris, et ce cadre est coupé
   net par le bord de l'écran. On ne lit pas « il y a la suite à droite », on
   lit « ce bloc est cassé ». Les deux personnes qui l'ont signalé l'ont
   entouré au feutre rouge, sur téléphone comme sur ordinateur.

   Le cadre est la promesse d'un contenant fermé. Dès que le contenu le
   dépasse, la promesse est fausse, et il vaut mieux la retirer que la tenir à
   moitié. Sur téléphone la rangée devient donc une simple file d'onglets qui
   glisse, sans boîte autour — la pastille active suffit à dire où l'on est —
   et un dégradé sur le bord droit dit qu'il y a la suite.

   Le masque est posé sur le CONTENEUR EXTÉRIEUR et non sur la rangée qui
   défile : appliqué à la rangée, il aurait défilé avec elle et se serait
   promené au milieu des onglets.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 640px) {
  .site.site #famtabs {
    background: none; border-color: transparent; box-shadow: none;
    padding-inline: 0;
    display: flex; max-width: 100%;
    scroll-padding-inline: 0;
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 34px), transparent);
            mask-image: linear-gradient(90deg, #000 calc(100% - 34px), transparent);
  }
  /* la pastille garde son fond : c'est elle qui porte maintenant la forme */
  .site.site #famtabs .ftpuce { box-shadow: 0 4px 14px -4px rgba(0, 0, 0, .5); }
  .site.site #famtabs::-webkit-scrollbar { display: none; }
}
