/* ════ SOMMAIRE ════
   Les numéros bougent à chaque édition. Pour les rafraîchir :
   python3 docs-projet/sommaire.py   (ou grep -n '═══' css/styles.css)
   
     126-149  LIEN D'ÉVITEMENT
     150-192  FOCUS CLAVIER
     193-300  BANDE D'ANNONCE
     301-357  TYPO PRIMITIVES
     358-413  REVEAL MOTION
     414-466  BEZEL (double biseau)
     467-599  COUTURE DES SECTIONS
     600-621  SURVOL DES CARTES MÉDIA
     622-681  BOUTONS
    682-1004  NAVIGATION
   1005-1695  PREMIER ÉCRAN
   1696-1820  TRANSITION EN GUIRLANDE
   1821-1888  FIGURES D'AUTORITÉ
   1889-1917  MARQUEE
   1918-2073  MENU PRESTATIONS
   2074-2157  HEAD SPA
   2158-2176  REGARD
   2177-2286  AVANT / APRÈS
   2287-2444  AVIS CLIENTS
   2445-2669  DANS L'INSTITUT
   2670-3441  02 · LES CRÉATIONS
   3442-3590  FAQ
   3591-3636  CONTACT
   3637-3681  FOOTER
   3682-3876  ENGAGEMENTS
   3877-4587  ENGAGEMENTS : LA PILE
   4588-4844  ENGAGEMENTS : L'ÉVENTAIL
   4845-4865  ÉCRANS TACTILES
   4866-4899  BARRE D'ACTIONS MOBILE
   4900-4958  RESPONSIVE
   4959-5023  TÉLÉPHONES
   5024-5237  PAGES D'ATTERRISSAGE : le strict complément
   5238-5317  RÉSEAUX SOCIAUX
   5318-5435  PAGES LÉGALES
   5436-5511  CONSENTEMENT
   5512-5535  CARTE EN ATTENTE
   ════════════════ */

/* ═══════════════════════════════════════════════════════════
   MAISON JOLIS NAILS : Editorial Luxury
   Palette : crème chaud · espresso · bronze doux
   ═══════════════════════════════════════════════════════════ */

:root {
  --cream: #f7f1e8;
  --cream-deep: #efe6d8;
  --ivory: #fdfaf4;
  --ink: #2a2018;
  --ink-soft: #5c5044;
  --taupe: #a8977f;
  --bronze: #a87f4c;
  --bronze-soft: #c4a678;
  /* Point clair des dégradés de sceau et d'icône. Décoratif : il ne porte
     jamais de texte. */
  --bronze-pale: #d8bd93;
  --espresso: #221a13;
  --espresso-2: #2e241a;
  --hairline: rgba(42, 32, 24, 0.09);
  /* Vigne : verts d'oliveraie, assez sourds pour tenir près du crème sans
     jamais virer au vert « web ». */
  --vine-stem: #6d7e4c;
  --vine-a: #7f9155;
  --vine-b: #9aad72;
  --vine-deep: #5c6c3e;
  /* Bronze assombri, réservé au premier écran. Le bronze de la charte
     (#a87f4c) plafonne à 3,17:1 sur le crème de la page : sur une photo, même
     voilée, il tombe sous le seuil. Mesuré à 1,6:1 sur « Jamais abîmés. »
     avant correction. Celui-ci tient 3,3:1 sur le fond voilé, et reste dans
     la même famille. */
  --bronze-deep: #835c31;
  /* Le taupe est un jeton décoratif : sur crème il plafonne à 2,53:1, très
     en dessous des 4,5:1 exigés pour du texte courant. Il reste en place
     pour les filets et les fonds ; dès qu'il porte des mots sur fond clair,
     c'est cette variante qui sert. Mesurée à 4,87:1. */
  --taupe-deep: #746654;
  --ease-lux: cubic-bezier(0.32, 0.72, 0, 1);
  /* Décélération très longue en fin de course : le mouvement s'arrête sans
     jamais « buter ». Réservée aux gestes qu'on veut voir se poser. */
  --ease-settle: cubic-bezier(0.16, 1, 0.3, 1);
  /* Bas de la barre flottante (22px d'écart + 68px de hauteur) + une respiration :
     tout point d'ancrage sans padding propre s'y adosse pour ne pas passer sous le menu. */
  --nav-clear: 124px;
  /* Hauteur de la bande d'annonce. Elle est reprise par le décalage du menu,
     le padding du body et la hauteur du premier écran, qui doit rester à
     exactement 100svh, sinon la garantie repasse sous la ligne de flottaison.
     `svh` et non `dvh` : voir le bloc PREMIER ÉCRAN. */
  --annbar-h: 32px;
  --r-xl: 2rem;
  --r-core: calc(2rem - 0.4rem);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  background: var(--cream);
  color: var(--ink);
  font-family: "Plus Jakarta Sans", sans-serif;
  font-weight: 400;
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  padding-top: var(--annbar-h);
}

::selection { background: var(--bronze); color: var(--ivory); }

/* La barre de défilement est la dernière surface du navigateur qu'on n'avait
   pas habillée. Elle traverse toute la page, elle appartient au système. */
* { scrollbar-color: rgba(42, 32, 24, 0.28) transparent; scrollbar-width: thin; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: rgba(42, 32, 24, 0.24);
  border-radius: 999px;
  border: 3px solid var(--cream);
}
::-webkit-scrollbar-thumb:hover { background: rgba(42, 32, 24, 0.4); }

/* ═══════════ LIEN D'ÉVITEMENT ═══════════
   Invisible tant qu'on ne navigue pas au clavier. À la première tabulation il
   se pose sous la bande d'annonce et permet de sauter la barre pour aller au
   contenu, au lieu de traverser le logo, le menu, le sélecteur et le bouton à
   chaque page. */
.skip-link {
  position: fixed;
  top: var(--annbar-h); left: 16px;
  z-index: 200;
  /* -200 % et non -140 % : à -140 %, 12 px du bas de la pilule dépassaient
     encore sous la bande d'annonce, au repos. Mesuré. */
  transform: translateY(-200%);
  padding: 12px 22px;
  border-radius: 999px;
  background: var(--ink);
  color: var(--ivory);
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  box-shadow: 0 18px 40px -18px rgba(42, 32, 24, 0.5);
  transition: transform 0.4s var(--ease-lux);
}
.skip-link:focus-visible { transform: translateY(12px); outline-offset: 4px; }

/* ═══════════ FOCUS CLAVIER ═══════════
   Anneau bronze visible uniquement à la navigation au clavier : indispensable
   pour se repérer sans souris, invisible pour les autres. */
:focus-visible {
  outline: 2px solid var(--bronze);
  outline-offset: 3px;
  border-radius: 4px;
}
.btn:focus-visible,
.lang:focus-visible,
.nav-logo:focus-visible,
.idx-link:focus-visible,
.verif:focus-visible {
  outline-offset: 4px;
  border-radius: 999px;
}
/* Sur le premier écran, l'anneau passe du bronze à l'encre. Mesuré sur la
   photo de fond, voile compris : le bronze de charte tombait à 2,14:1 autour
   du sélecteur de langue et 2,30:1 autour de « Réserver », sous les 3:1 que
   WCAG 1.4.11 exige d'un indicateur de focus. L'encre tient 8:1 sur la partie
   la plus chaude de l'image et 13:1 sur le crème de la barre reformée.
   C'est la règle du Bronze Muet appliquée au focus : le bronze qualifie, il
   ne signale pas. */
.nav :focus-visible,
.opening :focus-visible { outline-color: var(--ink); }
.faq-item summary:focus-visible {
  outline-offset: -3px;
  border-radius: 22px;
}
.ba-range:focus-visible { outline-offset: -4px; border-radius: var(--r-core); }
.row:focus-visible, .menu-cat:focus-visible { border-radius: 12px; }

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; }

/* Film grain */
.grain {
  position: fixed; inset: 0; z-index: 80; pointer-events: none;
  opacity: 0.035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ═══════════ BANDE D'ANNONCE ═══════════
   Offre datée, pas prix d'appel : registre sobre, aucune exclamation, aucun
   emoji, la typographie de la maison. Le seul accent est le chiffre.
   Le défilement va de la gauche vers la droite (le rail part de -50 % et
   remonte à 0), les deux groupes sont identiques pour que la boucle soit
   invisible. On n'anime que `transform`. */
.annbar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 101;
  height: var(--annbar-h);
  display: flex;
  align-items: center;
  overflow: hidden;
  background:
    radial-gradient(120% 180% at 50% 0%, rgba(168, 127, 76, 0.22), transparent 70%),
    linear-gradient(90deg, var(--espresso) 0%, var(--espresso-2) 50%, var(--espresso) 100%);
  border-bottom: 1px solid rgba(196, 166, 120, 0.22);
}
/* Reflet lent qui traverse la bande : c'est ce qui la rend « habitée » sans
   ajouter de mouvement au texte. Pseudo-élément, donc rien ne repeint la barre. */
.annbar::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  width: 38%;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, rgba(253, 250, 244, 0.09), transparent);
  animation: annbar-sheen 9s var(--ease-lux) infinite;
}
@keyframes annbar-sheen {
  0%       { transform: translateX(-120%); }
  55%, 100% { transform: translateX(360%); }
}

.annbar-track {
  display: flex;
  width: max-content;
  animation: annbar-scroll 46s linear infinite;
}
.annbar:hover .annbar-track { animation-play-state: paused; }
@keyframes annbar-scroll {
  from { transform: translateX(-50%); }
  to   { transform: translateX(0); }
}

.annbar-group { display: flex; }
.annbar-item {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding-right: 46px;
  white-space: nowrap;
  /* Remonté de 0,66 à 0,72 rem. La hauteur du bandeau est fixée par
     --annbar-h et ne suit pas : revérifier que le texte ne déborde pas si
     cette taille bouge encore. */
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: rgba(196, 166, 120, 0.9);
}
.annbar-hl {
  color: var(--ivory);
  font-weight: 600;
  letter-spacing: 0.14em;
}
/* La coupure forcée après « gratuitement. » compose le paragraphe sur deux
   lignes en grand écran. Sous 640 px les deux moitiés se replient de toute
   façon, et la garder coûtait une quatrième ligne : donc la garantie sous la
   barre d'actions fixe. On la neutralise, le texte est identique. */
/* Libellé court réservé au mobile. Déclaré AVANT la media query : à
   spécificité égale, c'est l'ordre source qui tranche, et le mettre après
   annulerait l'affichage mobile. */
.lbl-short { display: none; }

@media (max-width: 640px) {
  .lbl-more { display: none; }
  /* Même logique pour le chapô des tarifs : la coupure équilibre les deux
     lignes en grand écran, mais en colonne étroite elle laisse une ligne
     orpheline de deux mots au milieu du paragraphe. */
  .menu-head .lead br { display: none; }
  .lbl-full { display: none; }
  .lbl-short { display: inline; }
}

.annbar-sep {
  width: 4px; height: 4px;
  flex-shrink: 0;
  rotate: 45deg;
  background: var(--bronze);
  opacity: 0.75;
}

@media (max-width: 860px) {
  :root { --annbar-h: 30px; }
  .annbar-item {
    gap: 10px;
    padding-right: 32px;
    /* Remonté de 0,6 à 0,68 rem : sous 10 px, une capitale interlettrée
       devient un motif avant d'être un mot. La bande fait 30 px de haut ici,
       le texte 15 : la marge est suffisante. */
    font-size: 0.68rem;
    letter-spacing: 0.14em;
  }
  .annbar-track { animation-duration: 34s; }
}

/* ═══════════ TYPO PRIMITIVES ═══════════ */

.eyebrow {
  display: inline-block;
  font-size: 10px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--bronze-deep);
  border: 1px solid rgba(168, 127, 76, 0.35);
  border-radius: 999px;
  padding: 7px 16px 6px;
  margin-bottom: 28px;
  background: rgba(168, 127, 76, 0.06);
}
.eyebrow-light {
  color: var(--bronze-soft);
  border-color: rgba(196, 166, 120, 0.3);
  background: rgba(196, 166, 120, 0.07);
}
@keyframes pulse {
  0% { box-shadow: 0 0 0 0 rgba(125, 147, 103, 0.45); }
  70% { box-shadow: 0 0 0 8px rgba(125, 147, 103, 0); }
  100% { box-shadow: 0 0 0 0 rgba(125, 147, 103, 0); }
}

h1, h2, h3 {
  font-family: "Cormorant Garamond", serif;
  font-weight: 500;
  line-height: 1.02;
  letter-spacing: -0.005em;
  /* La ligature « ff » de Cormorant soude les deux f à des hauteurs inégales */
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0, "clig" 0, "dlig" 0;
}
h1 em, h2 em, .contact-core em {
  font-style: italic;
  font-weight: 400;
  color: var(--bronze-deep);
}

.h2 {
  font-size: clamp(2.7rem, 5.5vw, 4.7rem);
  margin-bottom: 28px;
}
.h2-light { color: var(--ivory); }
.h2-light em { color: var(--bronze-soft); }

.lead {
  font-size: 1.06rem;
  line-height: 1.75;
  color: var(--ink-soft);
  max-width: 46ch;
  font-weight: 300;
}
.lead-light { color: rgba(253, 250, 244, 0.62); }

/* ═══════════ REVEAL MOTION ═══════════ */

.reveal {
  opacity: 0;
  transform: translateY(48px);
  filter: blur(2px);
  transition:
    opacity 0.9s var(--ease-lux),
    transform 0.9s var(--ease-lux),
    filter 0.9s var(--ease-lux);
}
.reveal.in { opacity: 1; transform: translateY(0); filter: blur(0); }
/* ⚠️ `blur(0)` n'est PAS `none`. Un filtre, même nul, laisse à l'élément un
   contexte d'empilement et une opération de filtrage que le navigateur
   réapplique à chaque peinture, et il l'empêche d'être refondu dans le calque
   du document. Les 72 `.reveal` de l'accueil restaient dans cet état une fois
   apparus : mesuré au compositeur, ils portaient les plus gros calques de la
   page, jusqu'à 11 Mo pour un seul paragraphe, par agglomération des éléments
   qui les recouvrent.
   La classe est posée par js/main.js une fois la transition finie, jamais
   avant : `none` ne s'interpole pas avec `blur(2px)`, le flou d'entrée
   disparaîtrait d'un coup. */
.reveal.pose { filter: none; }
.d1 { transition-delay: 0.08s; }
.d2 { transition-delay: 0.16s; }
.d3 { transition-delay: 0.26s; }
.d4 { transition-delay: 0.36s; }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; filter: none; transition: none; }
  html { scroll-behavior: auto; }
  .marquee-track { animation: none !important; }
  /* La bande d'annonce doit rester lisible : on arrête le rail et le reflet,
     le texte reste en place et complet. */
  .annbar-track,
  .annbar::after { animation: none !important; }
  .annbar::after { opacity: 0; }
  /* On garde les changements de couleur (ils aident à comprendre l'état)
     mais on supprime tout déplacement. */
  .bezel:hover .media img,
.hs-card:hover,
.ba-card:hover,
.avis-card:hover,
.garantie:hover,
.garantie:hover .garantie-icon,
.faq-item:hover,
.idx-link:hover,
.row:hover,
.eng-item:hover,
.autorite-band span:hover{ transform: none; }
  .faq-a { transition: opacity 0.2s ease; }
  /* Le reflet du bouton d'or s'arrête hors du cadre : il ne doit pas rester
     figé en travers du libellé. */
  .btn-gold::after { animation: none !important; transform: translateX(-200%); }
}

/* ═══════════ BEZEL (double biseau) ═══════════ */

.bezel {
  background: rgba(42, 32, 24, 0.045);
  border: 1px solid var(--hairline);
  border-radius: var(--r-xl);
  padding: 0.4rem;
  box-shadow:
    0 1px 2px rgba(42, 32, 24, 0.03),
    0 24px 60px -24px rgba(42, 32, 24, 0.14);
}
.bezel-core {
  background: var(--ivory);
  border-radius: var(--r-core);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.7);
  overflow: hidden;
}
/* Le liseré doit se voir sur les QUATRE côtés.
   Ivoire à 10 %, il ne donnait que 1,52:1 contre l'espresso, et seul le bord
   HAUT se lisait, parce que le filet interne clair du cœur le doublait. En
   colonne unique, où deux cartes ne sont séparées que par leurs bords bas,
   elles semblaient fondues l'une dans l'autre. Le bronze à 40 % monte le
   liseré à 2,54:1, et le filet bas sombre du cœur rétablit la symétrie du
   biseau. Ne pas revenir à l'ivoire translucide : mesuré, il ne suffit pas. */
.bezel-dark {
  background: rgba(253, 250, 244, 0.05);
  border-color: rgba(196, 166, 120, 0.4);
  box-shadow: 0 30px 70px -30px rgba(0, 0, 0, 0.55);
}
.bezel-dark .bezel-core {
  background: var(--espresso-2);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.06),
    inset 0 -1px 0 rgba(0, 0, 0, 0.45);
}

/* Media placeholders : élégants même sans image */
.media {
  position: relative;
  background:
    radial-gradient(120% 90% at 15% 10%, rgba(196, 166, 120, 0.5), transparent 55%),
    radial-gradient(110% 100% at 90% 90%, rgba(168, 127, 76, 0.38), transparent 60%),
    radial-gradient(80% 80% at 70% 20%, rgba(253, 250, 244, 0.7), transparent 55%),
    linear-gradient(160deg, #eadfcd, #d9c6a8 55%, #cbb28c);
}
.media::after {
  content: "";
  position: absolute; inset: 0;
  background: radial-gradient(90% 70% at 50% 45%, transparent 55%, rgba(42, 32, 24, 0.1));
  pointer-events: none;
}
.media img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.media-tall { aspect-ratio: 3 / 4; }
.media-wide { aspect-ratio: 16 / 11; }

.tilt-l { transform: rotate(-1.5deg); }
.tilt-r { transform: rotate(1.5deg); }

/* ═══════════ COUTURE DES SECTIONS ═══════════
   Entre un bloc clair et un bloc sombre, la page n'avait qu'un trait. La
   couture prolonge la couleur du voisin clair DANS la section sombre sur
   44 px et lui donne le rayon : le bloc clair s'arrondit vers le sombre au
   lieu de s'arrêter net. « Dans l'institut » l'a inaugurée ; ce bloc la sort
   de sa section et la généralise à toutes les frontières clair/sombre qui
   n'avaient rien.

   Trois choses à ne pas perdre de vue :
   1. La bande est peinte SOUS le contenu (`z-index: -1`) et PAR-DESSUS le
      fond de la section. C'est `isolation: isolate` qui l'y tient : sans lui,
      la bande passe derrière la section et on ne voit plus rien.
   2. Hauteur = rayon. Au-delà, on lit une bande claire au lieu d'un coin.
   3. La couleur est celle du voisin, jamais une couleur choisie. Elle se
      déclare en `--seam-t` (voisin du dessus) et `--seam-b` (voisin du
      dessous) sur la section sombre. Sans jeton, la déclaration devient
      invalide et le fond retombe sur `transparent` : la couture ne se dessine
      pas, ce qui est le comportement voulu pour les frontières qui n'en
      veulent pas.

   Les frontières qui portent DÉJÀ une transition n'en prennent pas, elles
   font le travail : la guirlande de vigne sous le premier écran, la bande
   déroulante entre les maisons partenaires et les prestations, le filet
   bronze qui sépare les deux fonds sombres du pied de page court. Les
   frontières clair/clair non plus : leur filet à l'encre suffit.

   Où elle s'applique, une fois les jetons posés plus bas :
     accueil        avis → institut · institut → avant/après
                    avant/après → engagements · prestations → head spa
                    head spa → le regard · nous trouver → pied de page
     atterrissage   prestations → engagements · engagements → questions
                    nous trouver → rappel final
     pages légales  texte → rappel final                                    */

.institut,
.engagements,
.headspa,
.footer,
.menu,
.faq,
.contact,
.avis,
.lp-band {
  position: relative;
  isolation: isolate;
}
.institut::before,
.engagements::before,
.headspa::before,
.footer::before,
.menu::before,
.faq::before,
.contact::before,
.avis::before,
.lp-band::before,
.institut::after,
.engagements::after,
.headspa::after,
.footer::after,
.menu::after,
.faq::after,
.contact::after,
.avis::after,
.lp-band::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 44px;
  z-index: -1;
  pointer-events: none;
}
.institut::before,
.engagements::before,
.headspa::before,
.footer::before,
.menu::before,
.faq::before,
.contact::before,
.avis::before,
.lp-band::before {
  top: 0;
  border-radius: 0 0 44px 44px;
  background: var(--seam-t);
}
.institut::after,
.engagements::after,
.headspa::after,
.footer::after,
.menu::after,
.faq::after,
.contact::after,
.avis::after,
.lp-band::after {
  bottom: 0;
  border-radius: 44px 44px 0 0;
  background: var(--seam-b);
}

/* ── Les jetons, une ligne par frontière ──
   Le sélecteur nomme le voisin, pas la section : c'est lui qui donne la
   couleur, et c'est lui qui décide qu'il y a une couture. Les `.engagements`
   des pages d'atterrissage sont deux ; seul celui qui précède les questions
   prend une couture basse, le rappel final touchant le pied de page sombre. */
.avis + .institut { --seam-t: var(--ivory); }
.institut:has(+ .realisations) { --seam-b: var(--cream); }

.realisations + .engagements,
.contact + .engagements,
.lp-band + .engagements { --seam-t: var(--cream); }
.menu + .engagements { --seam-t: var(--ivory); }
.engagements:has(+ .faq) { --seam-b: var(--ivory); }

.menu + .headspa { --seam-t: var(--ivory); }
.headspa:has(+ .regard) { --seam-b: var(--cream-deep); }

/* Le pied de page n'est pas frère de « Nous trouver » : il est hors du
   <main>. D'où le sélecteur sur <main>, et non sur .contact. */
main + .footer { --seam-t: var(--cream); }

/* ── Les frontières clair/clair ──
   Trois fonds clairs cohabitent : l'ivoire, le crème du body et le crème
   profond. L'écart entre deux d'entre eux est de quelques pour cent : posé sur
   un trait, il se lit comme une salissure ; posé sur un coin, il se lit comme
   une page qui tourne. Même couture, même rayon, même mécanique : c'est
   l'écart de valeur qui est faible, pas le geste.
   Le marquee n'en prend pas : il EST la transition entre les maisons
   partenaires et les prestations, et ses deux filets à l'encre lui
   appartiennent. */
.regard + .faq { --seam-t: var(--cream-deep); }
.faq + .contact { --seam-t: var(--ivory); }
.lp-band + .menu { --seam-t: var(--cream); }

/* ── Les frontières propres aux pages d'atterrissage ──
   Ajoutées le 29/08 : les trois ou quatre premières sections de ces pages
   n'avaient aucune couture, et leurs frontières se lisaient comme des traits
   nets là où tout le reste du site s'arrondit. Même mécanique, même rayon.
   Le premier écran n'en prend pas : la guirlande fait déjà le travail.

   🔴 UNE SEULE couture par frontière. Posées le 29/08 en double (le coin haut
   de la section du bas ET le coin bas de celle du haut), elles se peignaient
   l'une sous l'autre et la frontière rendait deux arrondis empilés. C'est la
   section qui REÇOIT qui porte le coin, et elle prend la couleur de son
   voisin : c'est la règle du bloc ci-dessus, elle ne souffre pas d'exception.

   🔴 Le fond du ruban d'avis suit son VOISINAGE, arbitré le 30/08 :
   - entre deux sections claires, il passe en sombre (`.avis.on-dark`, posé par
     le générateur) : sans lui, quatre blocs clairs s'enchaînaient et la page
     s'aplatissait ;
   - s'il touche déjà une bande sombre, il reste en ivoire, qui est son plus
     beau rendu, et c'est alors le menu qui le suit qui passe au crème, sinon
     la frontière ivoire/ivoire n'existe pas et le filet du menu la remplace
     par un trait droit, seul de son espèce sur tout le site. */
.lp-band + .avis { --seam-t: var(--cream); }
.avis:not(.on-dark) + .menu { background: var(--cream); }
/* 🔴 Le menu qui vient de passer au crème change AUSSI la couture de la
   section qui le suit : elle prend la couleur de son voisin du dessus, et ce
   voisin n'est plus ivoire. Sans ces deux lignes, une bande ivoire arrondie
   se peignait sous un menu crème, et la frontière rendait deux arrondis de
   teintes différentes empilés. C'est le défaut vu sur head spa et pieds. */
.avis:not(.on-dark) + .menu + .engagements,
.avis:not(.on-dark) + .menu + .lp-band { --seam-t: var(--cream); }
.avis + .menu { --seam-t: var(--ivory); border-top: 0; }
.avis.on-dark + .menu { --seam-t: var(--espresso); }
.avis + .lp-band { --seam-t: var(--ivory); }
.avis.on-dark + .lp-band { --seam-t: var(--espresso); }
.menu + .lp-band { --seam-t: var(--ivory); }
.lp-band + .lp-band { --seam-t: var(--cream); }
.avis.on-dark { --seam-t: var(--cream); }

/* ── Le ruban d'avis en fond sombre ──
   Même aplat que la section head spa de l'accueil, jetons compris. Les cartes
   de témoignage gardent leur double biseau CLAIR : c'est le contraste qui les
   détache, et il n'y a rien à changer dans leur composition. Ne restent que
   l'en-tête et les deux voiles de bord, qui suivent le fond. */
.avis.on-dark {
  /* 🔴 Un seul halo, et il est ancré EN HAUT. Le fond de la section head spa
     en porte un second, ancré en bas à gauche : ici il tombait juste au-dessus
     de la couture, qui est un aplat d'espresso uni posé dans la section
     suivante. Le halo doré s'arrêtait donc net sur la ligne de frontière et
     dessinait une démarcation dorée en travers de l'arrondi. Le bas de la
     section doit être du même espresso que la couture qui la prolonge. */
  background:
    radial-gradient(60% 50% at 85% 0%, rgba(168, 127, 76, 0.14), transparent 60%),
    var(--espresso);
  color: var(--ivory);
}
/* 🔴 La couleur de la section ne franchit pas le biseau. Les cartes gardent
   leur double biseau CLAIR : sans cette ligne, la citation et le prénom
   héritaient de l'ivoire posé sur .avis.on-dark et devenaient invisibles sur
   leur propre fond ivoire (contraste mesuré à 1:1). Même piège que la carte
   d'étape sur bande sombre. */
.avis.on-dark .avis-card { color: var(--ink); }
/* Le cadre du double biseau, lui, doit changer de valeur. Il est fait d'encre
   à 4,5 % sur un liseré d'encre à 9 % : posé sur l'espresso, il disparaît, et
   la carte se retrouve sans épaisseur, comme découpée aux ciseaux. On reprend
   donc le geste de .bezel-dark, ivoire translucide et ombre profonde, avec le
   liseré bronze des cartes d'engagements : c'est le même cadre, dans les
   valeurs de son fond. */
.avis.on-dark .avis-card {
  background: rgba(253, 250, 244, 0.07);
  border-color: rgba(196, 166, 120, 0.34);
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.2),
    0 34px 70px -30px rgba(0, 0, 0, 0.62);
}
.avis.on-dark .avis-core { box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.8); }
@media (hover: hover) and (pointer: fine) {
  .avis.on-dark .avis-card:hover {
    border-color: rgba(196, 166, 120, 0.6);
    box-shadow:
      0 1px 2px rgba(0, 0, 0, 0.2),
      0 42px 84px -30px rgba(0, 0, 0, 0.7);
  }
}
.avis.on-dark .avis-fade-l { background: linear-gradient(90deg, var(--espresso), rgba(34, 26, 19, 0)); }
.avis.on-dark .avis-fade-r { background: linear-gradient(-90deg, var(--espresso), rgba(34, 26, 19, 0)); }
.avis.on-dark .score-note { color: rgba(253, 250, 244, 0.72); }
.avis.on-dark .verif {
  color: var(--bronze-soft);
  border-color: rgba(196, 166, 120, 0.34);
  background: rgba(196, 166, 120, 0.1);
}
.avis.on-dark .stars { color: var(--bronze-soft); }
/* La vigne pose elle-même la transition sous le premier écran : la bande qui
   la suit ne doit pas y ajouter un coin, il se peindrait sur les fleurs. */
.vine-rule + .lp-band::before { content: none; }

/* Les filets à l'encre de ces sections marquaient une frontière que la couture
   prend désormais en charge. Laissés en place, ils se retrouveraient peints EN
   TRAVERS de la bande claire, au lieu d'être noyés dans le fond voisin comme
   avant. Celui du menu ne part QUE sur les pages d'atterrissage : sur les
   accueils, le menu suit le marquee, qui garde son filet et sa frontière. */
.engagements + .faq,
.regard + .faq,
.headspa + .regard,
.lp-band + .menu { border-top: 0; }

/* ═══════════ SURVOL DES CARTES MÉDIA ═══════════ */

.regard-media .bezel,
.hs-card,
.ba-card{
  transition: transform 0.8s var(--ease-lux), box-shadow 0.8s var(--ease-lux);
}
.hs-card:hover,
.ba-card:hover{
  transform: translateY(-7px);
  box-shadow:
    0 1px 2px rgba(42, 32, 24, 0.03),
    0 42px 80px -30px rgba(42, 32, 24, 0.28);
}
.regard-media .bezel.tilt-r:hover { transform: rotate(1.5deg) translateY(-7px); }
.hs-card:hover { box-shadow: 0 44px 90px -32px rgba(0, 0, 0, 0.65); }

.media img { transition: transform 1.6s var(--ease-lux); }
.bezel:hover .media img { transform: scale(1.05); }
.ba-wrap img { transition: none; }
.ba-card:hover .ba-wrap img { transform: none; }

/* ═══════════ BOUTONS ═══════════ */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  border-radius: 999px;
  padding: 13px 15px 13px 26px;
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: transform 0.5s var(--ease-lux), background 0.5s var(--ease-lux),
    box-shadow 0.5s var(--ease-lux), border-color 0.5s var(--ease-lux);
}
.btn:active { transform: scale(0.98); }
/* Le padding ci-dessus (26 px à gauche, 15 px à droite) réserve la place de
   l'orbe. Un bouton qui n'en porte pas voit son libellé décalé de 11 px :
   on lui rend une assise symétrique. */
.btn:not(:has(.btn-orb)) { padding-right: 26px; }

.btn-orb {
  width: 30px; height: 30px;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.8rem;
  background: rgba(253, 250, 244, 0.16);
  transition: transform 0.5s var(--ease-lux);
}
.btn:hover .btn-orb { transform: translate(2px, -1px) scale(1.08); }

.btn-solid {
  background: var(--ink);
  color: var(--ivory);
  box-shadow: 0 18px 40px -18px rgba(42, 32, 24, 0.5);
}
.btn-solid:hover { background: var(--espresso); box-shadow: 0 22px 48px -18px rgba(42, 32, 24, 0.6); }

.btn-ghost {
  border: 1px solid rgba(42, 32, 24, 0.18);
  color: var(--ink);
  padding-right: 26px;
  background: transparent;
}
/* Le rembourrage droit symétrique ci-dessus vaut pour un bouton fantôme nu.
   Dès qu'un bouton fantôme porte l'orbe ↗ qui signale une sortie du site, il
   lui faut la même assise que ses voisins pleins, sinon les boutons ne
   s'accordent pas (orbe à 27 px du bord contre 15 px). */
.btn-ghost:has(.btn-orb) { padding-right: 15px; }
/* L'orbe est taillé pour un bouton plein : un disque d'ivoire à 16 % lisible
   sur l'espresso. Posé sur un bouton fantôme, il disparaît, et son glyphe en
   `currentColor` passe à l'encre. On lui rend le contraste par l'inverse, exactement
   comme la barre de navigation le fait déjà pour « Réserver » sur fond clair
   (.nav .btn-nav .btn-orb). Utile depuis que le téléphone de la section
   « Nous trouver » est passé en fantôme au profit de « Réserver en ligne ». */
.btn-ghost .btn-orb { background: rgba(42, 32, 24, 0.09); color: var(--ink); }
.btn-ghost:hover { border-color: rgba(42, 32, 24, 0.4); background: rgba(253, 250, 244, 0.6); }

/* ═══════════ NAVIGATION ═══════════ */

/* La barre prend toute la largeur du contenu de page, et son rembourrage
   gauche de 40 px est retranché de la gouttière : le symbole du logo tombe
   exactement sur la verticale du titre du premier écran, à n'importe quelle
   largeur de fenêtre. Une largeur maximale fixe décrochait au-delà de
   1600 px, la gouttière du premier écran continuant elle de s'ouvrir.
   41 px retranchés, pas 40 : le rembourrage gauche plus le filet de 1 px. */
.nav-wrap {
  position: fixed;
  top: var(--annbar-h); left: 0; right: 0;
  z-index: 100;
  display: flex;
  justify-content: center;
  padding: 22px max(16px, calc(clamp(24px, 7vw, 110px) - 41px)) 0;
  pointer-events: none;
}
/* ── Barre en trois zones ──
   La barre n'épouse plus son contenu : elle distribue logo à gauche, menu au
   centre, actions à droite. Les deux colonnes latérales sont en 1fr, donc le
   menu est centré sur la barre et non sur ce qui reste entre le logo et le
   bouton.

   La hauteur ne bouge pas (10 px de rembourrage vertical, 68 px au total).
   --nav-clear, le padding du body et la hauteur du premier écran en dépendent :
   les toucher ferait repasser la garantie sous la ligne de flottaison.

   ── Haut de page : plus de contenant ──
   Sur la photo, la barre de verre faisait boîte. Les éléments flottent donc à
   même l'image tant qu'on n'a pas défilé, et la pilule se reforme dès que le
   contenu clair passe dessous : sans elle, le menu deviendrait illisible sur
   les sections claires. Ces règles de transparence s'arrêtent au header :
   aucune section en dessous n'y touche, le verre est un effet de premier
   écran, pas un langage de page. */
.nav {
  pointer-events: auto;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  width: 100%;
  gap: 24px;
  padding: 10px 10px 10px 40px;
  border-radius: 999px;
  background: transparent;
  border: 1px solid transparent;
  box-shadow: none;
  transition: box-shadow 0.6s var(--ease-lux), background 0.6s var(--ease-lux);
}
.nav.scrolled {
  background: rgba(253, 250, 244, 0.85);
  border-color: rgba(42, 32, 24, 0.07);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  box-shadow: 0 16px 44px -16px rgba(42, 32, 24, 0.3);
}

.nav .nav-links {
  justify-self: center;
  gap: clamp(26px, 2.6vw, 44px);
}
/* Le sélecteur perd son fond plein au profit d'un verre à peine teinté :
   posé sur l'image, un aplat crème ferait pastille rapportée. */
.nav .lang {
  background: rgba(253, 250, 244, 0.22);
  border-color: rgba(42, 32, 24, 0.16);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  transition: background 0.6s var(--ease-lux), border-color 0.6s var(--ease-lux);
}
.nav.scrolled .lang {
  background: rgba(247, 241, 232, 0.5);
  border-color: rgba(42, 32, 24, 0.12);
}
/* « Réserver » du menu passe en fantôme tant qu'on est sur l'image, comme
   dans la référence, et redevient plein dès que la barre se reforme. C'est
   « Réserver en ligne », au centre du premier écran, qui porte l'action en
   encre pleine : deux pilules sombres en même temps se concurrenceraient. */
.nav .btn-nav {
  background: rgba(253, 250, 244, 0.2);
  border: 1px solid rgba(42, 32, 24, 0.22);
  color: var(--ink);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: none;
  transition: background 0.6s var(--ease-lux), color 0.6s var(--ease-lux),
              border-color 0.6s var(--ease-lux), box-shadow 0.6s var(--ease-lux);
}
.nav .btn-nav .btn-orb {
  background: rgba(42, 32, 24, 0.09);
  color: var(--ink);
}
.nav.scrolled .btn-nav {
  background: var(--ink);
  border-color: transparent;
  color: var(--ivory);
  box-shadow: 0 18px 40px -18px rgba(42, 32, 24, 0.5);
}
.nav.scrolled .btn-nav .btn-orb {
  background: rgba(253, 250, 244, 0.16);
  color: var(--ivory);
}

/* Sélecteur de langue et « Réserver » forment un bloc unique, calé à droite. */
.nav-actions {
  display: flex;
  align-items: center;
  justify-self: end;
  gap: 18px;
}
.nav-logo {
  font-family: "Cormorant Garamond", serif;
  font-style: italic;
  font-weight: 500;
  font-size: 1.45rem;
  letter-spacing: 0.01em;
  display: inline-flex;
  align-items: center;
  gap: 11px;
}
.nav-mark { height: 26px; width: auto; }
.nav-links { display: flex; gap: 26px; }
.nav-links a {
  white-space: nowrap;
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  position: relative;
  transition: color 0.4s var(--ease-lux);
}
.nav-links a::after {
  content: "";
  position: absolute;
  left: 0; bottom: -4px;
  width: 100%; height: 1px;
  background: var(--bronze);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.5s var(--ease-lux);
}
.nav-links a:hover { color: var(--ink); }
.nav-links a:hover::after { transform: scaleX(1); transform-origin: left; }

/* Le numéro dans la barre des pages générées. Il y est seul dans .nav-links,
   entre un logo, une bascule de langue en pastille et un bouton plein : sans
   contenant, c'est le seul élément sans forme et il flotte. On lui donne une
   pastille filaire : assez présente pour tenir la ligne, assez discrète pour
   ne pas concurrencer « Réserver » : et un orbe qui dit enfin qu'on peut
   cliquer dessus. Masqué sous 861 px comme tout .nav-links. */
.nav-tel {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 7px 16px 7px 8px;
  border-radius: 999px;
  border: 1px solid rgba(42, 32, 24, 0.13);
  background: rgba(42, 32, 24, 0.025);
  color: var(--ink);
  transition: border-color 0.5s var(--ease-lux), background 0.5s var(--ease-lux);
}
.nav-tel::after { display: none; }   /* ce n'est plus un lien de menu : pas de soulignement */
.nav-tel-orb {
  width: 24px; height: 24px;
  flex-shrink: 0;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(196, 166, 120, 0.2);
  color: var(--bronze);
  transition: background 0.5s var(--ease-lux);
}
.nav-tel-orb svg { width: 12px; height: 12px; }
@media (hover: hover) and (pointer: fine) {
  .nav-tel:hover { border-color: rgba(168, 127, 76, 0.42); background: rgba(196, 166, 120, 0.09); }
  .nav-tel:hover .nav-tel-orb { background: rgba(196, 166, 120, 0.34); }
}

.btn-nav { background: var(--ink); color: var(--ivory); padding: 10px 10px 10px 20px; font-size: 0.78rem; }
.btn-nav .btn-orb { width: 26px; height: 26px; }

/* ── Sélecteur de langue ──
   Un seul lien vers l'autre version : la pastille bascule où que l'on clique,
   comme un interrupteur. Les deux libellés ne sont que l'état affiché, pas
   deux cibles distinctes. La position de départ vient de data-active. */
.lang {
  position: relative;
  display: inline-flex;
  align-items: center;
  padding: 3px;
  border-radius: 999px;
  border: 1px solid rgba(42, 32, 24, 0.12);
  background: rgba(247, 241, 232, 0.5);
  flex: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.lang-thumb {
  position: absolute;
  top: 3px; bottom: 3px; left: 3px;
  width: 38px;
  border-radius: 999px;
  background: var(--ink);
  box-shadow: 0 8px 18px -10px rgba(42, 32, 24, 0.65);
  transition:
    transform 0.9s var(--ease-settle),
    width 0.9s var(--ease-settle),
    box-shadow 0.6s var(--ease-lux);
}
.lang[data-active="en"] .lang-thumb { transform: translateX(38px); }

/* Survol du bouton entier : la pastille s'étire vers l'autre langue sans la
   couvrir. Le geste est annoncé avant le clic, et se rétracte si l'on renonce. */
.lang:hover .lang-thumb {
  width: 53px;
  box-shadow: 0 10px 24px -10px rgba(42, 32, 24, 0.75);
}
.lang[data-active="en"]:hover .lang-thumb { transform: translateX(23px); }
/* Appui : la pastille se tasse légèrement, puis repart. */
.lang:active .lang-thumb { transition-duration: 0.18s; width: 44px; }
.lang[data-active="en"]:active .lang-thumb { transform: translateX(32px); }

/* Arrivée depuis l'autre version : la pastille glisse jusqu'à sa place.
   Sans « from » sortant, l'état final reste celui des règles ci-dessus,
   donc l'étirement au survol reprend la main dès l'animation terminée. */
.lang--switched[data-active="en"] .lang-thumb { animation: lang-to-en 1s var(--ease-settle); }
.lang--switched[data-active="fr"] .lang-thumb { animation: lang-to-fr 1s var(--ease-settle); }
@keyframes lang-to-en { from { transform: translateX(0); } }
@keyframes lang-to-fr { from { transform: translateX(38px); } }

.lang-opt {
  position: relative;
  z-index: 1;
  width: 38px;
  /* 10 px verticaux au lieu de 6 : le sélecteur passe de 37 à 45 px de haut,
     au-dessus du seuil tactile de 44. Il reste sous les 46 px du bouton
     « Réserver », donc la hauteur de la barre ne bouge pas. */
  padding: 10px 0;
  text-align: center;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  color: var(--taupe-deep);
  transition: color 0.7s var(--ease-settle);
}
.lang:hover .lang-opt { color: var(--ink); }
.lang-opt.is-on,
.lang:hover .lang-opt.is-on { color: var(--ivory); }
.lang--switched .lang-opt { animation: lang-tint 1s var(--ease-settle); }
@keyframes lang-tint { from { color: var(--taupe); } }

/* Burger */
.burger {
  display: none;
  /* 44 px : la cible tactile recommandée. C'est le seul accès au menu sur
     téléphone, il ne se rate pas. */
  width: 44px; height: 44px;
  border: none;
  background: var(--ink);
  border-radius: 999px;
  position: relative;
  cursor: pointer;
}
.burger span {
  position: absolute;
  left: 50%; top: 50%;
  width: 16px; height: 1.5px;
  background: var(--ivory);
  transition: transform 0.5s var(--ease-lux);
}
.burger span:nth-child(1) { transform: translate(-50%, calc(-50% - 3.5px)); }
.burger span:nth-child(2) { transform: translate(-50%, calc(-50% + 3.5px)); }
.burger.open span:nth-child(1) { transform: translate(-50%, -50%) rotate(45deg); }
.burger.open span:nth-child(2) { transform: translate(-50%, -50%) rotate(-45deg); }

/* Overlay mobile */
.overlay {
  position: fixed; inset: 0;
  z-index: 90;
  background: rgba(247, 241, 232, 0.88);
  backdrop-filter: blur(30px);
  -webkit-backdrop-filter: blur(30px);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.6s var(--ease-lux), visibility 0.6s;
  display: flex;
  align-items: center;
  justify-content: center;
}
.overlay.open { opacity: 1; visibility: visible; }
.overlay-inner { display: flex; flex-direction: column; align-items: center; gap: 8px; text-align: center; }
.ov-link {
  font-family: "Cormorant Garamond", serif;
  font-weight: 500;
  font-size: clamp(2.2rem, 8.5vw, 3.3rem);
  line-height: 1.25;
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.7s var(--ease-lux), transform 0.7s var(--ease-lux), color 0.4s;
}
.ov-link:hover { color: var(--bronze-deep); }
.overlay.open .ov-link { opacity: 1; transform: translateY(0); }
.overlay.open .ov-link:nth-child(1) { transition-delay: 0.1s; }
.overlay.open .ov-link:nth-child(2) { transition-delay: 0.16s; }
.overlay.open .ov-link:nth-child(3) { transition-delay: 0.22s; }
.overlay.open .ov-link:nth-child(4) { transition-delay: 0.28s; }
.overlay.open .ov-link:nth-child(5) { transition-delay: 0.34s; }
.overlay.open .ov-link:nth-child(6) { transition-delay: 0.4s; }
.ov-cta {
  margin-top: 28px;
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.7s var(--ease-lux) 0.42s, transform 0.7s var(--ease-lux) 0.42s;
}
.overlay.open .ov-cta { opacity: 1; transform: translateY(0); }
.ov-foot {
  margin-top: 34px;
  font-size: 10px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--taupe-deep);
}

/* ═══════════ PREMIER ÉCRAN ═══════════
   Le premier écran des neuf pages qui en ont un : les deux accueils et les
   sept pages d'atterrissage. Photo pleine page, texte groupé, un seul bouton,
   et une capsule de garantie. C'était d'abord le hero de l'accueil seul ; les
   sept pages ont basculé ici le 20 août 2026 et le bloc HERO en deux colonnes
   qui les servait, 217 lignes, a été retiré avec ses classes orphelines.

   Trois valeurs seulement sont propres à une page, et aucune ne se recopie :
   le ratio du H1 (jetons `--h1-*`, mesurés sur la fonte réelle), le cadrage
   de la photo et les contrastes du voile.

   Contrainte de mesure : 8 visiteurs sur 10 arrivent en mobile. Sur 375 × 812
   l'ensemble doit tenir sans défilement (mesuré 782 px). Chaque valeur en
   dessous du palier 860 px est calculée pour ça, y compris les 84 px de
   rembourrage bas qui laissent passer la barre d'actions fixe. */

.opening {
  /* ── Les deux jetons du H1 ──
     La règle des Deux Lignes dépend des lettres du titre, pas de la page :
     ces deux valeurs sont donc portées par la section et remesurées à chaque
     réécriture du H1. `--h1-ratio` plafonne la taille sur téléphone (largeur
     de la plus longue ligne divisée par la taille de police, mesurée sur la
     fonte réelle, plus 2 % de marge). `--h1-vw` plafonne celle d'ordinateur,
     où la colonne de texte ne fait que 58 % de la largeur.
     Les valeurs ci-dessous sont celles des deux accueils ; les sept pages
     d'atterrissage posent les leurs plus bas, par `data-lp`. */
  --h1-ratio: 8;
  --h1-vw: 5.6vw;
  /* Le palier « fenêtre courte » (moins de 760 px de haut) grossit le titre
     pour compenser les respirations qu'il retire. Il a donc son propre
     plafond, plus haut que celui de la pleine hauteur : le mesurer à part. */
  --h1-vw-court: 6.2vw;
  position: relative;
  /* `svh`, pas `dvh`. Le « d » de dvh veut dire dynamique : la valeur suit en
     direct la barre d'adresse du navigateur mobile, qui se rétracte dès qu'on
     défile. La section grandissait alors sous les yeux du visiteur et la photo
     avait l'air de se dérouler, comme si un bandeau replié s'ouvrait. `svh`
     est la hauteur barres déployées : elle ne bouge jamais, donc défiler fait
     descendre la page et rien d'autre.
     Le repli en `vh` sert aux navigateurs qui ignorent `svh` ; il est déclaré
     avant, la seconde règle l'écrase partout où elle est comprise. */
  min-height: calc(100vh - var(--annbar-h));
  min-height: calc(100svh - var(--annbar-h));
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Le texte et la garantie forment UN bloc, centré verticalement dans la
     colonne gauche. La garantie n'est plus plaquée en bas de l'écran : c'est
     ce qui l'envoyait dans la bande des mains, mesurée à partir de 33 % de
     la largeur (profil de netteté de la photo, les doigts sont nets, le lin
     est flou). Bloc en haut à gauche, photo en bas à droite. */
  justify-content: center;
  overflow: hidden;
}

/* ── Les ratios de H1, page par page ──
   Mesurés au Range sur le Cormorant Garamond 500, dans la variante réelle de
   chaque ligne, italique comprise, à l'interlettrage du titre (-0.015em). La
   valeur retenue est la plus large des deux lignes, plus 2 % de marge pour
   les arrondis de rendu. Le témoin de la méthode : « Vos ongles, sublimés. »
   rend 7,839, la valeur documentée de l'accueil.

   Ces titres sont des titres SEO. Ils ne se réécrivent pas pour arranger la
   mise en page : c'est le ratio qui s'adapte au titre, jamais l'inverse.

   Ordre : ces règles suivent `[lang="en"] .opening`, qui a la même
   spécificité. Les déplacer au-dessus rendrait le ratio anglais gagnant sur
   les trois pages anglaises. */
[lang="en"] .opening { --h1-ratio: 5.7; }

.opening[data-lp="gainage"]    { --h1-ratio: 8.2; }   /* « Le gainage des ongles » 7,964 */
.opening[data-lp="headspa"]    { --h1-ratio: 8.2; }   /* « où vous ne décidez rien. » 8,005 */
.opening[data-lp="onglerie"]   { --h1-ratio: 8.7; --h1-vw-court: 5.9vw; }  /* « La pose d'ongles à Aix, » 8,426 (remesuré le 29/08, l'ancien titre rendait 8,433 : jetons inchangés) */
.opening[data-lp="cils"]       { --h1-ratio: 7.1; }   /* « L'extension de cils, » 6,912 */
.opening[data-lp="pieds"]      { --h1-ratio: 7.7; }   /* « La beauté des pieds, » 7,459, mesuré le 29/08 */
.opening[data-lp="en-pieds"]   { --h1-ratio: 7.2; }   /* « A pedicure in Aix, » 6,942, mesuré le 30/08 */
.opening[data-lp="en-biab"]    { --h1-ratio: 8.2; }   /* « your nails just stronger. » 8,022 */
.opening[data-lp="en-headspa"] { --h1-ratio: 9.7; --h1-vw: 5.3vw; --h1-vw-court: 5.3vw; }  /* « Japanese head spa in Aix, » 9,435 */
/* Seul titre à réclamer aussi un plafond d'ordinateur : à 5,6vw il cassait
   en trois lignes de 870 à 1490 px, mesuré tous les 10 px. La colonne de
   texte vaut 51 % de la largeur dans cette plage (58 % moins 7vw de
   rembourrage), d'où 51 / 9,435 = 5,40, ramené à 5,3 pour la marge. */
.opening[data-lp="en-gelmani"] { --h1-ratio: 8.1; }   /* « Gel manicure in Aix, » 7,876 */
.opening[data-lp="en-lashes"]  { --h1-ratio: 10; --h1-vw: 5.1vw; --h1-vw-court: 5.1vw; }  /* « Eyelash extensions in Aix, » 9,710 */
/* Le titre le plus long du site : « eyelash extensions » est le terme que les
   anglophones cherchent, on ne l'abrège pas pour arranger la mise en page.
   Conséquence assumée et mesurée : 27,6 px à 320 px de large, contre 34,5 px
   pour l'accueil français. C'est le plancher du site. */

/* ── Les trois jetons du cadrage portrait ──
   Sur téléphone, le texte est ancré EN HAUT (rembourrage de 110 px) et la
   photo était ancrée EN BAS. Les deux s'éloignaient donc l'un de l'autre à
   mesure que l'écran grandissait : mesuré, le vide entre la carte de garantie
   et les mains passait de 12 px sur un écran de 812 px de haut à 56 px sur un
   écran de 932. Dans le même mouvement, le bas du sujet passait derrière la
   barre d'actions fixe (77 px) et sous la vigne.

   Le sujet est maintenant ancré AU TEXTE, pas au bas de l'écran. Trois jetons
   par photo suffisent, posés par page comme le ratio du H1 :

   `--texte-bas`  bas du bloc de texte, mesuré dans le DOM à 375 px de large,
                  **animation d'apparition neutralisée**. Le piège : `.reveal`
                  pose un `transform: translateY`, que `getBoundingClientRect`
                  inclut. Mesurer sans le neutraliser gonfle la valeur de 48 px
                  et décolle le sujet d'autant. Quand deux pages partagent une
                  photo (une FR, une EN), on garde la plus haute des deux
                  valeurs : le sujet ne passe sous le texte d'aucune des deux.
                  Relevé de 320 à 430 px de large : la valeur varie de −46 à
                  +14 px autour de celle de 375, ce que l'aération absorbe.
   `--sujet-haut` début du sujet dans le fichier, en fraction de sa hauteur.
   `--sujet-h`    hauteur du sujet dans le fichier, en fraction.

   Les bornes du sujet viennent d'une détection de teinte (peau, ongles,
   cheveux) ligne par ligne sur les cinq fichiers portrait, pas d'un coup
   d'œil. Elles sont dans DESIGN.md. */
.opening                          { --texte-bas: 427px; --sujet-haut: 0.65; --sujet-h: 0.26; }
.opening[data-lp="gainage"],
.opening[data-lp="en-biab"]       { --texte-bas: 437px; --sujet-haut: 0.57; --sujet-h: 0.29; }
.opening[data-lp="headspa"],
.opening[data-lp="en-headspa"]    { --texte-bas: 437px; --sujet-haut: 0.69; --sujet-h: 0.28; }
.opening[data-lp="onglerie"],
.opening[data-lp="en-gelmani"]    { --texte-bas: 432px; --sujet-haut: 0.59; --sujet-h: 0.32; }
.opening[data-lp="cils"],
.opening[data-lp="en-lashes"]     { --texte-bas: 430px; --sujet-haut: 0.64; --sujet-h: 0.31; }

/* Emplacement réservé du fond, 2880 × 1620 px, ratio 16:9. */
.opening-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  /* L'aplat reste sous l'image : tant que les fichiers ne sont pas livrés,
     ou s'ils manquent, c'est lui qu'on voit, et la page ne bouge pas. */
  background: var(--cream-deep);
}
.opening-bg picture,
.opening-bg img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center bottom;
}
/* Le voile est posé avant la photo, pas après : il fait partie de la mesure
   de contraste du texte en encre. Sans lui, une photo claire en haut et
   sombre en bas ferait sauter la lisibilité au moment de la substitution. */
/* ── Plus aucun flou sur la photo du premier écran ──
   Une couche `.opening-blur` en `backdrop-filter`, masquée par un dégradé,
   floutait le fond du bord gauche jusqu'à 54 % de la largeur (53 % de la
   hauteur sur téléphone). Elle est retirée le 22/08.

   Deux mesures ont tranché. D'abord le sujet : le profil d'arêtes des cinq
   photos, calculé fenêtre par fenêtre, montre que le détail remonte AVANT la
   fin de la rampe sur `lp-headspa-bg` et `lp-gainage-bg`. Le sujet passait
   donc bien sous le flou, contrairement à ce qui était écrit ici. Ensuite le
   contraste : recomposité hors ligne (photo en `cover`, flou gaussien, voile
   crème), le rapport de contraste le plus défavorable sous le texte perd au
   maximum **0,25 point** quand on retire la couche, et **zéro** sur
   téléphone. Le flou ne servait donc pas la lisibilité, c'est le voile qui
   la tient seul.

   Conséquence : ne pas réintroduire de `filter`, de `backdrop-filter` ni de
   couche floutée sur `.opening-bg`, `.opening` ou par-dessus. La photo est
   servie nette, exactement telle qu'elle est encodée. */

.opening-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  /* ── Le voile suit le texte, il ne couvre plus la photo ──
     Le voile est l'outil de contraste du premier écran, pas une ambiance.
     Jusqu'au 22/08 c'était un dégradé vertical pleine largeur, à 50 % au
     milieu : il voilait le sujet autant que le texte. Mesuré, il retirait
     **64 % du micro-contraste** du sujet, ce que l'oeil lit exactement comme
     du flou. C'était la vraie cause du « header flou », pas un `blur`.

     Il est maintenant directionnel, et se compose de trois couches qui ne se
     recouvrent pas. Alpha résultant = 1 − ∏(1 − alpha de chaque couche).

     1. La colonne de texte, à l'horizontale. Le texte le plus à droite du
        site est le H1 de l'accueil, mesuré dans le DOM à 53 % de la largeur.
        La rampe tient 0,82 jusqu'à 42 %, tombe à 0,42 à 56 %, et vaut zéro
        à 66 %. Le sujet commence au plus tôt à 68 % : il n'est plus touché.
     2. Un bandeau haut, léger, sur toute la largeur. Il ne sert qu'à la
        barre de navigation, dont les liens s'étendent jusqu'à 65 % de la
        largeur à 9,7 % de la hauteur, sans contenant propre.
     3. Un raccord bas de 8 %, qui pose la photo sur le crème de la page.

     Mesuré sur les cinq photos, en 1280 × 693 et 390 × 750, sur les zones de
     texte relevées dans le DOM :

     | | pire contraste ord. | micro-contraste du sujet |
     |---|---|---|
     | ancien voile vertical | 4,23:1 | 1,68 |
     | celui-ci | **4,60:1** | **3,07** |

     La force horizontale est descendue de 0,86 à 0,82 le 22/08, pour laisser
     voir le lin derrière le texte. Elle ne peut pas descendre plus bas : le
     surtitre en bronze `#835c31` tombe à 4,47:1 dès 0,76, sous le seuil AA.
     Sur téléphone le surtitre ne sort pas, d'où un voile bien plus léger là-bas.
     Pour alléger davantage en paysage il faudrait foncer le bronze du surtitre
     à `#714c24`, qui rendrait 5,43:1 avec un voile à 0,62.

     Il est donc meilleur sur les deux tableaux à la fois : le texte passe le
     seuil AA de 4,5:1 partout, ce que l'ancien ratait sur le surtitre, et le
     sujet récupère 83 % de piqué. Toute retouche de ces valeurs se remesure,
     elle ne s'ajuste pas à l'oeil. */
  background:
    linear-gradient(180deg,
      rgba(247, 241, 232, 0) 92%,
      rgba(247, 241, 232, 0.62) 100%),
    linear-gradient(180deg,
      rgba(247, 241, 232, 0.36) 0%,
      rgba(247, 241, 232, 0.30) 12%,
      rgba(247, 241, 232, 0) 21%),
    linear-gradient(90deg,
      rgba(247, 241, 232, 0.82) 0%,
      rgba(247, 241, 232, 0.78) 42%,
      rgba(247, 241, 232, 0.40) 56%,
      rgba(247, 241, 232, 0) 66%);
}

/* ── Le texte et les mains ne partagent jamais la même bande ──
   Superposer le texte au sujet de la photo abîme les deux : on ne lit ni
   l'un ni l'autre. Un masque géométrique autour des doigts serait la version
   pauvre de la solution. Ici, la composition sépare : sur ordinateur le
   texte tient la colonne gauche et les mains gardent la droite, sur
   téléphone le texte tient le haut et les mains le bas. Le voile n'a plus à
   compenser quoi que ce soit, la photo respire. */
.opening-inner {
  position: relative;
  z-index: 2;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  width: 100%;
  /* 54 % : la colonne s'arrête avant les mains, jamais dessus. `.opening`
     centre ses enfants, il faut donc dire explicitement que celui-ci se cale
     à gauche, sinon la colonne se recentre et le gain est perdu. */
  max-width: 58%;
  align-self: flex-start;
  /* Le rembourrage haut n'est plus qu'un garde-fou contre la barre flottante
     (--nav-clear vaut 124 px) : c'est le centrage du groupe qui place le
     bloc, pas lui. */
  padding: 30px 0 0 clamp(24px, 7vw, 110px);
}
.opening-eyebrow {
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bronze-deep);
  margin-bottom: 22px;
}
.opening-eyebrow strong { font-weight: 600; }
.opening-title {
  font-family: "Cormorant Garamond", serif;
  font-weight: 500;
  /* La colonne fait 54 % de la largeur : à 7,6vw, « Vos ongles, sublimés. »
     cassait en deux et le titre passait à trois lignes. Deux lignes exactement,
     c'est la règle du H1 de cette maison. */
  font-size: clamp(2.8rem, var(--h1-vw), 5rem);
  line-height: 1.02;
  letter-spacing: -0.015em;
}
.opening-title .line { display: block; }
.opening-title em { font-style: italic; color: var(--bronze-deep); }
/* Deux lignes, la seconde en gras : la première dit ce qu'on fait, la seconde
   lève l'objection. C'est le seul texte de service du premier écran. */
/* Le Plus Jakarta Sans en 400 avec un interlettrage large donne une ligne
   filiforme, qui se lit comme une mention légale plutôt que comme l'offre.
   Le 500 lui rend du corps, l'interlettrage resserré lui rend de la densité,
   et l'encre douce passe à l'encre pleine. */
.opening-sub {
  margin-top: 30px;
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1.7;
  color: var(--ink);
}
.opening-cta { margin-top: 34px; padding: 15px 15px 15px 30px; }
.opening-cta .btn-orb { width: 30px; height: 30px; }

/* ── L'argument unique ──
   Une capsule de verre, centrée, qui porte le renversement de risque et rien
   d'autre. La transparence est l'effet, pas la décoration : la photo de fond
   doit passer au travers, c'est ce qui pose la capsule DANS l'image au lieu
   de la poser DESSUS. D'où le flou large, la saturation poussée et la lèvre
   claire en haut, qui donne l'épaisseur du verre. */
.opening-proof {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 58%;
  align-self: flex-start;
  margin-top: 38px;
  justify-content: flex-start;
  /* Le dégagement sous la capsule se règle dans les requêtes de hauteur plus
     bas. La guirlande monte 123 px au-dessus de la jonction sur les colonnes
     que la capsule occupe : sans cette garde, une fleur vient s'asseoir sur sa
     dernière ligne de texte. */
  padding: 0 0 0 clamp(24px, 7vw, 110px);
  display: flex;
}
.warranty {
  display: inline-flex;
  align-items: center;
  gap: 17px;
  /* 52ch, la largeur d'origine. Elle n'a plus à être rognée : la photo de
     fond place désormais les mains dans le tiers droit, la carte a toute la
     gauche pour elle. */
  max-width: 52ch;
  padding: 15px 24px 15px 18px;
  border-radius: 22px;   /* le rayon « carte » de DESIGN.md */
  /* La carte d'origine, forme et couleurs comprises. Le verre avait un défaut
     de fond : une surface transparente se fond dans la photo, or c'est le seul
     argument du premier écran. Il lui faut le contraste maximal de la page. */
  background:
    radial-gradient(130% 170% at 0% 50%, rgba(196, 166, 120, 0.22), transparent 62%),
    var(--espresso-2);
  box-shadow:
    0 1px 2px rgba(42, 32, 24, 0.05),
    0 26px 52px -30px rgba(34, 26, 19, 0.62);
  color: var(--ivory);
  transition:
    transform 0.6s var(--ease-lux),
    box-shadow 0.6s var(--ease-lux);
}
/* Le geste d'origine : la carte se soulève, le sceau respire. Elle n'est plus
   cliquable, l'animation reste un signe de vie et pas une promesse de lien. */
@media (hover: hover) and (pointer: fine) {
  .warranty:hover {
    transform: translateY(-3px);
    box-shadow:
      0 1px 2px rgba(42, 32, 24, 0.05),
      0 34px 62px -30px rgba(34, 26, 19, 0.7);
  }
  .warranty:hover .warranty-seal {
    transform: scale(1.06);
    box-shadow: 0 0 0 8px rgba(196, 166, 120, 0.18);
  }
}
.warranty-seal {
  flex: none;
  width: 42px;
  height: 42px;
  border-radius: 999px;
  display: grid;
  place-items: center;
  color: var(--espresso);
  background: linear-gradient(145deg, var(--bronze-pale), var(--bronze));
  box-shadow: 0 0 0 5px rgba(196, 166, 120, 0.14);
  transition: transform 0.6s var(--ease-lux), box-shadow 0.6s var(--ease-lux);
}
.warranty-seal svg { width: 19px; height: 19px; }
.warranty-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; text-align: left; }
.warranty-k {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--bronze-soft);
}
.warranty-t {
  font-family: "Cormorant Garamond", serif;
  font-size: 1.34rem;
  font-weight: 600;
  line-height: 1.12;
  color: var(--ivory);
}
.warranty-d {
  font-size: 0.79rem;
  font-weight: 300;
  line-height: 1.5;
  color: rgba(253, 250, 244, 0.72);
}
/* La capsule n'est plus un lien : ni orbe, ni état de survol. C'est une
   affirmation posée sous le bouton, pas une porte de plus. */

/* ── Ordinateur portable ──
   Le premier écran doit tenir sur une fenêtre de 800 px de haut, barre
   d'annonce comprise. On rend les pixels sur les respirations, jamais sur la
   taille du titre ni sur la garantie. */
@media (min-width: 861px) and (max-height: 880px) {
  .opening-inner { padding-top: 126px; padding-bottom: 26px; }
  .opening-sub { margin-top: 20px; }
  .opening-cta { margin-top: 26px; }
  /* 134 px, mesuré dans le DOM : la capsule se retrouve à 134 px de la
     jonction, soit 11 px au-dessus de la fleur la plus haute de ses colonnes.
     À 88 px elle n'était qu'à 108 px, la fleur la traversait. */
  .opening-proof { padding-bottom: 134px; }
}
@media (min-width: 861px) and (max-height: 760px) {
  .opening-title { font-size: clamp(2.7rem, var(--h1-vw-court), 4.6rem); }
  /* Le rembourrage de 74 px a été retiré : la garde sous la capsule ne dépend
     plus de la hauteur de fenêtre, c'est la guirlande qui la fixe. À 74 px la
     capsule retombait à 91 px de la jonction et la fleur la traversait. Les
     134 px hérités tiennent ici sans rien coûter au premier écran, mesuré à
     1440 sur 760 : la capsule reste à 109 px au-dessus de la ligne de
     flottaison et le titre à 75 px sous la barre. */
  .warranty { padding: 13px 20px 14px 15px; gap: 14px; }
  .warranty-seal { width: 38px; height: 38px; }
  .warranty-t { font-size: 1.2rem; }
  .warranty-d { font-size: 0.75rem; }
}

/* ── Mobile ──
   Le premier écran ne fait plus exactement un écran : on lui retire 88 px
   pour que le haut de la vigne dépasse au-dessus de la barre d'actions fixe.
   Voir la vigne commencer, c'est savoir qu'il y a une suite. Un écran qui
   s'arrête pile à la ligne de flottaison ne le dit pas, et c'est là que le
   rebond se joue : 8 visiteurs sur 10 arrivent d'un téléphone. */
@media (max-width: 860px) {
  /* En portrait, la séparation est verticale et le partage est franc : tout
     le texte est groupé en haut, d'un seul tenant, et le bas de l'écran
     appartient entièrement à la photo. C'est ce qui laisse les mains
     entières, au lieu de faire descendre la carte de garantie dessus. */
  .opening {
    /* Le premier écran fait de nouveau un écran plein. Il était volontairement
       88 px plus court pour que le haut de la vigne dépasse au-dessus de la
       barre d'actions : sur un téléphone dont la barre d'adresse est déployée,
       ces 88 px étaient pris à la photo, qui n'avait plus la place de montrer
       les mains. La vigne se découvre maintenant au défilement, comme le reste
       de la page. */
    min-height: calc(100vh - var(--annbar-h));
    min-height: calc(100svh - var(--annbar-h));
    justify-content: flex-start;
  }
  /* 110 px de rembourrage haut : la barre flottante s'arrête à 112 px du haut
     de la page (30 de bande d'annonce, 22 d'écart, 60 de hauteur). À 84 px le
     titre la touchait presque, le surtitre retiré ne servant plus de tampon.
     Il reste 28 px de respiration, et chacun de ces pixels est pris à la
     photo : ne pas monter plus haut sans revérifier le dégagement des ongles. */
  .opening-inner {
    max-width: 100%;
    align-items: center;
    text-align: center;
    padding: 110px 22px 0;
  }
  /* Le surtitre sort du premier écran sur téléphone. Il tenait sur deux lignes
     et coûtait 59 px pris à la photo, pour redire ce que le logo au-dessus et
     la ligne des prestations en dessous disent déjà. Il reste dans le balisage
     et sur ordinateur : c'est lui qui situe la maison et la ville. */
  .opening-eyebrow { display: none; }
  /* ── Le sujet se pose sous le texte, plus au fond de l'écran ──
     La photo n'est plus calée en bas. Sa hauteur et sa position se calculent
     à partir des trois jetons de la page.

       couverture = largeur du hero × 2,1667   (ratio du fichier portrait)
       hauteur    = borne(couverture, place disponible / hauteur du sujet)
       haut       = min(sous le texte, centré dans la place disponible)

     `couverture` se calcule sur la LARGEUR, pas sur la hauteur : c'est la
     hauteur minimale pour que la photo couvre encore le cadre. Elle vaut
     104 % de la hauteur du hero sur un iPhone 812, mais **127 %** sur un
     667. La poser en pourcentage de hauteur laissait deux bandes crème sur
     les côtés des écrans courts, mesurées à 18 % de la largeur. D'où les
     unités de conteneur : `100cqw` est la largeur du hero.

     La place disponible va du bas du bloc de texte au haut de la barre
     d'actions fixe. Le dégagement du bas vaut 88 px : 77 px de barre, plus
     11 px pour que le sujet ne la touche pas. La vigne commence à 95,7 % de
     la hauteur, elle reste donc toujours sous le sujet.

     Le plafond de zoom, 114 % de la couverture, borne le rognage latéral à
     7 % de chaque côté : au-delà, les doigts d'`opening-bg` sortent du cadre.
     Il borne aussi l'agrandissement à ×1,12 sur un écran à trois pixels
     physiques, ce qui reste sous le seuil du visible.

     Le `min()` de la position est le repli des écrans courts. Sur un 375×667,
     le bloc de texte occupe 69 % du hero et le sujet en réclame 37 % : il n'y
     a pas la place, c'est géométrique. L'ancrage sous le texte ferait alors
     descendre tout le débord derrière la barre d'actions. Le second terme
     centre le sujet dans la place disponible, ce qui répartit le débord
     moitié en haut, moitié en bas. Sur les écrans qui ont la place, c'est le
     premier terme qui gagne, et le sujet se pose juste sous le texte.

     Repli : sans unités de conteneur (navigateurs d'avant 2023), tout le bloc
     est ignoré et la règle de base reprend la main, `cover` calé en bas. */
  @supports (height: 1cqw) {
    .opening-bg { container-type: size; }
    .opening-bg img {
      --couverture: calc(100cqw * 2.1667);
      --place: calc(100cqh - var(--texte-bas) - var(--aeration) - 88px);
      --h: min(calc(var(--couverture) * 1.14),
               max(var(--couverture), calc(var(--place) / var(--sujet-h))));
      /* La largeur reste à 100 % : `object-fit: cover` sur une boîte plus
         haute que le ratio du fichier rogne les côtés, et `object-position`
         les rogne à parts égales. C'est exactement le recadrage voulu, sans
         avoir à décaler l'image à la main. */
      height: var(--h);
      top: min(
        calc(var(--texte-bas) + var(--aeration) - var(--sujet-haut) * var(--h)),
        calc((var(--texte-bas) + var(--aeration) + 100cqh - 88px) / 2
             - (var(--sujet-haut) + var(--sujet-h) / 2) * var(--h))
      );
      bottom: auto;
      object-position: center;
    }
  }
  /* L'aération entre le bloc de texte et le sujet. 26 px : en dessous les
     doigts touchent la carte de garantie, au-dessus le vide redevient
     visible. Elle absorbe aussi l'écart de `--texte-bas` d'une largeur
     d'écran à l'autre, au plus 14 px vers le haut entre 375 et 430 px. */
  .opening { --aeration: 26px; }
  /* ── Le voile en portrait : vertical, et éteint avant le sujet ──
     Le partage est vertical : tout le texte translucide est en haut, le
     sujet en bas. Relevé dans le DOM sur 375 × 812, le texte le plus bas
     (le sous-titre) s'arrête à 38,5 % de la hauteur ; le bouton et la carte
     de garantie sont opaques et n'ont besoin d'aucun voile. La rampe tient
     donc 0,80 jusqu'à 32 %, tombe à 0,44 à 42 % et vaut zéro à 54 %.

     Aucun raccord bas ici : la barre d'actions fixe couvre déjà le bas de
     l'écran, et le raccord d'ordinateur voilerait le visage ou les mains.

     La force est passée de 0,86 à **0,48** le 22/08 : le lin doit se lire à
     travers le texte, c'est ce qui fait respirer le premier écran. Le plancher
     mesuré est 0,42 (pire contraste 4,91:1) ; 0,48 garde 20 % de marge.

     Mesuré sur les cinq photos et quatre tailles d'écran (320 × 640, 375 × 637,
     375 × 812, 430 × 932), sur toute la bande de texte et non sur les seules
     lignes : pire contraste **5,42:1**, seuil AA à 4,5:1. */
  .opening-scrim {
    background: linear-gradient(180deg,
      rgba(247, 241, 232, 0.48) 0%,
      rgba(247, 241, 232, 0.43) 32%,
      rgba(247, 241, 232, 0.23) 42%,
      rgba(247, 241, 232, 0) 54%);
  }
  /* ── Le H1 ne passe jamais à trois lignes ──
     La taille n'est plus une part de la largeur d'écran : elle est plafonnée
     par la largeur que réclame la plus longue des deux lignes. Mesuré sur le
     Cormorant Garamond 500 avec l'interlettrage du titre, une ligne fait
     7,84 fois la taille de police en français (« Vos ongles, sublimés. ») et
     5,57 fois en anglais (« Beautiful nails. »). On divise par un peu plus,
     8 et 5,7, ce qui garde 2 % de marge sur les arrondis de rendu.
     44 px retranchés de la largeur : les deux rembourrages de 22 px.
     Toute réécriture du H1 doit refaire cette mesure, le ratio dépend du
     texte. */
  .opening-title {
    font-size: min(3.4rem, calc((100vw - 44px) / var(--h1-ratio)));
    line-height: 1.04;
  }
  .opening-sub { margin-top: 18px; font-size: 0.75rem; letter-spacing: 0.08em; line-height: 1.7; }
  .opening-cta { margin-top: 20px; padding: 15px 15px 15px 28px; }
  /* La carte suit le bouton au lieu d'être plaquée en bas de l'écran : les
     cinq éléments se lisent comme un seul bloc, et la photo garde toute la
     moitié basse pour elle. */
  .opening-proof {
    max-width: 100%;
    justify-content: center;
    /* Chaque pixel repris ici est un pixel rendu aux ongles : sur un téléphone
       dont la barre d'adresse est déployée, la carte s'arrête à une vingtaine
       de pixels du début de la manucure. */
    margin-top: 18px;
    padding: 0 16px;
  }
  /* Sur 375 px, chaque ligne de carte se paie en main cachée. On garde le
     libellé et la promesse, on retire l'accroche et la seconde phrase. */
  /* La carte se resserre à 330 px : elle se lit d'un bloc au lieu de barrer
     tout l'écran, et rend de la largeur à la photo de chaque côté. Le texte
     reste aligné à gauche, sur l'aplomb du sceau. */
  .warranty {
    max-width: min(100%, 330px);
    gap: 12px;
    padding: 13px 18px 14px 13px;
    border-radius: 22px;
  }
  .warranty-t,
  .w-more { display: none; }
  .warranty-seal { width: 36px; height: 36px; box-shadow: 0 0 0 4px rgba(196, 166, 120, 0.14); }
}

/* ── Téléphones courts : seules les marges cèdent ──
   Le recalage de la photo par `object-position` vivait ici, page par page.
   Il est retiré le 22/08 : le cadrage portrait est désormais calculé à partir
   des jetons `--texte-bas`, `--sujet-haut` et `--sujet-h`, qui valent à toutes
   les hauteurs d'écran. Poser en plus un `object-position` n'aurait aucun
   effet, la boîte de l'image ayant exactement le ratio du fichier.

   Le rembourrage haut reste à 110 px partout : ce n'est pas une respiration,
   c'est le dégagement de la barre de navigation, qui s'arrête à 86 px dans la
   section. L'avoir descendu à 78, 58 puis 46 px faisait passer le titre
   derrière la barre, de 8 à 40 px. Mesuré, et visible à l'oeil sur un
   iPhone 17. Seules les marges entre les éléments cèdent quelques pixels, et
   seulement sur les écrans les plus courts : chaque pixel repris au texte est
   un pixel rendu au sujet, puisque le sujet suit désormais le texte. */
@media (max-width: 860px) and (max-height: 800px) {
  .opening-sub { margin-top: 14px; }
  .opening-cta { margin-top: 16px; }
  .opening-proof { margin-top: 14px; }
}
@media (max-width: 860px) and (max-height: 660px) {
  .opening-sub { margin-top: 10px; }
  .opening-cta { margin-top: 12px; padding: 13px 14px 13px 26px; }
  .opening-proof { margin-top: 10px; }
  .warranty { padding: 11px 16px 12px 11px; gap: 11px; }
  .warranty-seal { width: 33px; height: 33px; }
}

/* ── Téléphone couché ──
   Moins de 520 px de hauteur : la garantie passait sous la ligne de
   flottaison, or c'est le seul argument du premier écran. On reprend les
   respirations, jamais la taille du titre ni la carte elle-même. */
@media (max-width: 860px) and (max-height: 520px) {
  .opening-inner { padding-top: 84px; }
  .opening-sub { margin-top: 12px; }
  .opening-cta { margin-top: 14px; }
  .opening-proof { margin-top: 12px; }
  .warranty-seal svg { width: 16px; height: 16px; }
  .warranty-k { font-size: 0.68rem; letter-spacing: 0.18em; }
  .warranty-d { font-size: 0.95rem; font-weight: 400; line-height: 1.35; color: rgba(253, 250, 244, 0.92); }
}

/* ── La plante ──
   Ces règles servaient l'arche du hero et servent maintenant la seule vigne
   restante, celle de la transition. Elles vivaient dans le bloc HERO : à sa
   suppression le 20 août 2026, elles ont été rapatriées ici, où elles sont
   désormais chez elles. Les quatre règles de survol qui les accompagnaient
   (`.hero-visual:hover .vine-*`, le souffle au survol de l'arche) sont
   parties avec le hero : la vigne de transition ne se survole pas.

   Le tracé est généré en pixels réels par main.js, jamais dans un viewBox
   fixe : les feuilles gardent la même taille quelle que soit la largeur. */
.vine {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;   /* les feuilles débordent du cadre, comme une vraie vigne */
  pointer-events: none;
  z-index: 2;
}
/* La bordure sur laquelle les lianes s'enroulent : à peine dessinée, elle
   n'existe que pour donner un support visuel au tressage. */
.vine-border {
  fill: none;
  stroke: var(--vine-stem);
  stroke-width: 1;
  opacity: 0.3;
  transition: opacity 0.9s var(--ease-lux);
}
.vine-strand {
  fill: none;
  stroke: var(--vine-stem);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.7;
  transition: opacity 0.9s var(--ease-lux);
}
.vine-leaf .leaf { fill: var(--vine-a); }
.vine-leaf .leaf-b { fill: var(--vine-b); }
.vine-leaf .leaf-c { fill: var(--vine-deep); }
.vine-leaf .leaf-petiole {
  fill: none;
  stroke: var(--vine-stem);
  stroke-width: 1.1;
  stroke-linecap: round;
}
.vine-leaf .leaf-rib {
  fill: none;
  stroke: rgba(253, 250, 244, 0.36);
  stroke-width: 0.5;
  stroke-linecap: round;
}
/* Les feuilles tournées vers l'intérieur passaient devant la photo de l'arche :
   on les allège pour qu'elles se lisent comme un second plan. La règle de la
   transition, plus bas, les remonte, faute de photo derrière. */
.vine-leaf.is-inner { opacity: 0.62; }
.vine-curl {
  fill: none;
  stroke: var(--vine-stem);
  stroke-width: 1.1;
  stroke-linecap: round;
  opacity: 0.66;
}
.vine-leaf, .vine-curl { transition: opacity 0.9s var(--ease-lux); }
/* Souffle : le retard de chaque feuille suit sa position sur le tracé, donc
   l'ondulation fait le tour du cadre au lieu de secouer tout d'un bloc. Le
   déclencheur au survol est parti avec l'arche ; l'ossature reste, prête à
   resservir, et `main.js` continue de poser le --d de chaque feuille. */
.vine-sway { transform-box: fill-box; transform-origin: 0% 50%; }
@keyframes vine-breeze {
  0%, 100% { transform: rotate(0deg); }
  28%      { transform: rotate(7deg) scale(1.04); }
  62%      { transform: rotate(-4deg) scale(0.99); }
}

/* ═══════════ TRANSITION EN GUIRLANDE ═══════════
   La bande n'est plus un tracé calculé, c'est la photo de guirlande. Trois
   décisions, chacune prise après une mesure.

   1. Aucune répétition. La guirlande est une chaîne continue d'un bord à
   l'autre, donc toute jonction se voit : le miroir posait un axe de symétrie
   en pleine bande, le fondu croisé superposait deux régions de la photo et
   dédoublait des fleurs, la coupe franche tranchait une feuille en ligne
   droite (le feuillage reste opaque jusqu'aux colonnes de bord, où qu'on
   coupe). Posée une seule fois, il n'y a plus de jonction du tout.

   2. La bande prend le rapport de la photo (2752 sur 369) plutôt qu'une
   hauteur fixe : la photo la remplit donc entière, sans déformation et sans
   rognage, à toutes les largeurs, et aucune fleur n'est coupée par un bord de
   bande. Les marges sont en pourcentage, qui se résout sur la largeur, la
   bande suit donc la largeur sans une ligne de JS et sans requête média.

   3. Elle ne se pose PAS à cheval au milieu. Une bande de 286 px centrée sur
   la jonction remontait trop haut dans le premier écran et son fond recouvrait
   la capsule de garantie. Elle trempe de 9,77 % de la largeur dans la section du
   dessus, soit 141 px à 1440 px. Ce n'est pas une valeur choisie : c'est le point
   le plus bas de la frontière du voile, donc de la ligne de tige. Le posant là,
   le voile est opaque partout à la hauteur de la jonction et le lin descend
   toucher la tige sur toute la largeur.
   Contrepartie mesurée : la guirlande monte 123 px au-dessus de la jonction sur
   les colonnes de la capsule de garantie du premier écran. Celle-ci doit donc
   remonter à 134 px de la jonction, contre 88 aujourd'hui (`bottom` de
   .opening-proof), sinon une fleur vient s'asseoir sur sa dernière ligne de
   texte.

   4. Le lin du premier écran ne s'arrête pas net : un voile l'éteint en
   épousant la ligne de tige. Cette ligne est mesurée sur la photo, pas
   dessinée : pour chaque colonne, le centre de gravité des pixels opaques,
   lissé sur 70 px. La frontière du voile EST cette ligne, à 4 px près en
   moyenne, et non un plancher posé à sa moyenne : avec un plancher, le lin
   s'arrêtait au-dessus de la tige partout où elle descend, et l'écart se
   voyait. Le voile s'éteint vers le haut sur 45 lignes source, soit 23 px à
   l'écran à 1440 px, son bord brouillé par trois octaves de bruit déterministe
   (festons de 320 px, ondulations de 110, grain de 36) et un grain sur
   l'opacité, et le fondu est borné sur les 82 colonnes qui passent derrière la
   capsule de garantie pour n'y rien peindre. Un dégradé droit, essayé d'abord,
   se lisait comme une arête ; le feuillage seul ne croise la jonction que sur
   30 % de la largeur, il ne pouvait donc pas la tenir.
   La couleur du voile est cuite dans le fichier, une variante par fond de
   section. C'est volontaire : mask-image en url() n'est pas honoré partout, et
   un voile non masqué couvre toute la bande, donc coupe le lin au carré. Deux
   fichiers sont fournis, ivoire et crème.

   5. Le générateur n'intervient plus sur .vine-rule : plus rien n'est
   recalculé au redimensionnement, la bande est identique par construction et
   les 2943 nœuds SVG qu'elle coûtait tombent à zéro.

   Le `border-top` de la section qui suit doit être retiré, sinon il réapparaît
   en filet sous la guirlande, juste là où le voile vient d'éteindre la
   jonction. */
.vine-rule {
  position: relative;
  z-index: 5;
  width: 100%;
  aspect-ratio: 2752 / 369;
  /* 9,77 % de trempage : la jonction tombe sur le point le plus bas de la ligne
     de tige. En bas, la demi-hauteur exacte (6,704 % = 50 x 369 / 2752). */
  margin-top: -9.77%;
  margin-bottom: -6.704%;
  pointer-events: none;
}

/* Deux calques, dans cet ordre : le voile, puis la guirlande. Un enfant en
   z-index négatif se peindrait AU-DESSUS du fond de son parent et effacerait
   les fleurs ; deux pseudo-éléments règlent l'ordre sans z-index. */
/* WebP SANS PERTE, aux dimensions EXACTES des PNG (2752 x 369). 1 368 Ko de
   PNG deviennent 634 Ko, sans qu'un seul pixel visible ne change : vérifié
   pixel par pixel le 26/08/2026, zéro écart sur les pixels d'alpha > 0.
   ⚠️ NE PAS redimensionner et NE PAS ajouter de requête média. Une première
   tentative servait du 750 px en mobile et du 1440 px en desktop : sur un
   écran retina (densité 2 ou 3) la source était AGRANDIE, et le rendu flou.
   La première déclaration est le repli des navigateurs sans image-set(). */
.vine-rule::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("../images/vine-veil-ivory.png");
  background-image: image-set(
    url("../images/vine-veil-ivory.webp") type("image/webp"),
    url("../images/vine-veil-ivory.png") type("image/png"));
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

/* Une bande posée au-dessus d'une section crème prend l'autre voile. */
.vine-rule.on-cream::before {
  background-image: url("../images/vine-veil-cream.png");
  background-image: image-set(
    url("../images/vine-veil-cream.webp") type("image/webp"),
    url("../images/vine-veil-cream.png") type("image/png"));
}

.vine-rule::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("../images/vine-garland-clean.png");
  background-image: image-set(
    url("../images/vine-garland-clean.webp") type("image/webp"),
    url("../images/vine-garland-clean.png") type("image/png"));
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

/* Pour une bande posée au-dessus d'une section crème, ajouter la classe
   on-cream sur le div .vine-rule du HTML. C'est la seule modification HTML de
   toute l'opération, et seulement sur les bandes concernées. */

/* Aucune requête média : la bande suit la largeur toute seule. Mesuré : 193,1
   px de haut à 1440 px de large, 50,3 px à 375 px ; débord de 141 et 36,6 px
   dans la section du dessus, de 96,5 et 25,1 px dans celle du dessous. Sur
   téléphone la guirlande est deux fois plus fine que l'ancienne vigne (92 px),
   elle ne peut donc pas déborder sur le texte voisin. */

/* La section du dessous : la bande y mord 96,5 px à 1440 px, contre 46 px pour
   l'ancienne vigne. Son padding haut passe de 112 à 150 px, ce qui laisse
   53 px entre le bas de la bande et la pastille des avis (mesuré). Sur
   téléphone, les 74 px de padding actuels suffisent, la bande n'y mordant que
   25 px. */

/* ═══════════ FIGURES D'AUTORITÉ ═══════════ */

/* Cette bande suit immédiatement « Nos engagements », qui est déjà sombre et
   porte son propre halo haut. Lui en donner un second créait une deuxième
   source de lumière à la couture des deux sections : ça se lisait comme une
   bavure, pas comme un éclairage. Les deux blocs ne forment donc plus qu'un
   seul champ sombre continu, séparés par un simple filet. La bande de marques
   est une mention de bas de page, pas une section : elle en prend le ton. */
.autorite {
  padding: clamp(40px, 6vh, 62px) clamp(24px, 7vw, 110px) clamp(56px, 8vh, 88px);
  text-align: center;
  background: var(--espresso);
  /* La frontière avec .engagements tombe sur une position fractionnaire
     (3977,20 px). Les deux sections arrondissent chacune de leur côté et
     laissent passer un pixel physique du fond crème du body : un filet clair
     en travers de deux sections sombres. Cette ombre peint 1 px d'espresso
     au-dessus de la section et bouche la couture, sans rien décaler. */
  box-shadow: 0 -1px 0 var(--espresso);
}
.autorite-label {
  position: relative;
  font-size: 10px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  font-weight: 600;
  color: rgba(196, 166, 120, 0.72);
  padding-top: clamp(34px, 5vh, 54px);
  margin-bottom: 28px;
}
/* Filet centré, dégradé aux extrémités : il marque la séparation sans poser une
   ligne dure en travers du champ sombre. */
.autorite-label::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: min(280px, 42vw);
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(196, 166, 120, 0.34) 22%,
    rgba(196, 166, 120, 0.34) 78%,
    transparent
  );
}
.autorite-band {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  gap: 14px clamp(16px, 3vw, 46px);
  max-width: 1180px;
  margin: 0 auto;
}
.autorite-band span {
  font-family: "Cormorant Garamond", serif;
  font-weight: 500;
  font-size: clamp(0.78rem, 1.3vw, 1.15rem);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(253, 250, 244, 0.42);
  white-space: nowrap;
  transition: color 0.5s var(--ease-lux), transform 0.5s var(--ease-lux);
}
.autorite-band span:hover { color: var(--ivory); transform: translateY(-2px); }

/* ═══════════ MARQUEE ═══════════ */

.marquee {
  overflow: hidden;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  padding: 20px 0;
  background: var(--cream-deep);
}
.marquee-track {
  display: flex;
  align-items: center;
  gap: 38px;
  width: max-content;
  animation: marquee 36s linear infinite;
}
/* Même règle que pour le ruban des avis. */
.hors-vue .marquee-track { animation-play-state: paused; }
.marquee-track span {
  font-family: "Cormorant Garamond", serif;
  font-size: 1.2rem;
  font-style: italic;
  font-weight: 500;
  color: var(--ink-soft);
  white-space: nowrap;
}
.marquee-track i { font-style: normal; color: var(--bronze-deep); font-size: 0.7rem; }
@keyframes marquee { to { transform: translateX(-50%); } }

/* ═══════════ MENU PRESTATIONS ═══════════ */

.menu { padding: clamp(100px, 14vh, 170px) clamp(24px, 7vw, 110px); background: var(--ivory); border-top: 1px solid var(--hairline); }
.menu-head { max-width: 1320px; margin: 0 auto 70px; }

/* Garantie « La semaine tranquille » */
.garantie {
  max-width: 1320px;
  margin: 0 auto 56px;
  transition: transform 0.7s var(--ease-lux), box-shadow 0.7s var(--ease-lux);
}
.garantie:hover {
  transform: translateY(-5px);
  box-shadow:
    0 1px 2px rgba(42, 32, 24, 0.03),
    0 42px 84px -32px rgba(34, 26, 19, 0.5);
}
.garantie .garantie-icon { transition: transform 0.7s var(--ease-lux); }
.garantie:hover .garantie-icon { transform: scale(1.14) rotate(8deg); }
.garantie-core {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: clamp(24px, 3.2vw, 36px) clamp(24px, 3.4vw, 44px);
  background:
    radial-gradient(60% 120% at 0% 50%, rgba(196, 166, 120, 0.16), transparent 60%),
    var(--espresso-2);
}
.garantie-icon {
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  color: var(--bronze-soft);
}
/* 🔴 Le nom du dispositif est ce que la visiteuse doit lire en premier dans
   cette carte : c'est lui qui transfère le risque. En surtitre bronze de
   0,66 rem, il se lisait après le titre et après le texte. Il devient une
   pastille de métal, la même que le bouton doré, et prend le premier regard.
   Encre sur bronze : contraste mesuré à 8,8:1. */
.garantie-kicker {
  display: inline-block;
  padding: 6px 14px;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--bronze-pale), var(--bronze-soft) 55%, var(--bronze));
  color: var(--espresso);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 700;
  margin-bottom: 14px;
  box-shadow: 0 6px 18px -8px rgba(168, 127, 76, 0.7);
}
.garantie-title {
  font-family: "Cormorant Garamond", serif;
  font-weight: 600;
  font-size: 1.45rem;
  color: var(--ivory);
  margin-bottom: 5px;
}
.garantie-text {
  font-size: 0.9rem;
  font-weight: 300;
  color: rgba(253, 250, 244, 0.65);
  line-height: 1.6;
}

.menu-grid {
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: clamp(40px, 5vw, 90px);
  max-width: 1320px;
  margin: 0 auto;
  align-items: start;
}

.menu-index {
  position: sticky;
  top: 120px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.idx-link {
  min-height: 44px;
  display: flex;
  align-items: center;
  font-size: 0.86rem;
  font-weight: 500;
  color: var(--taupe-deep);
  padding: 9px 16px;
  border-radius: 999px;
  transition: color 0.45s var(--ease-lux), background 0.45s var(--ease-lux), transform 0.45s var(--ease-lux);
}
.idx-link:hover { color: var(--ink); transform: translateX(4px); }
.idx-link.active { color: var(--ink); background: var(--cream); }

.menu-list { display: flex; flex-direction: column; gap: 26px; }

/* Les catégories n'ont pas de padding haut : sans cette marge d'ancrage, un lien
   « #m-… » les collerait au bord haut et la barre flottante en couperait le titre. */
.menu-cat { scroll-margin-top: var(--nav-clear); }

.cat-core { padding: clamp(26px, 3.4vw, 44px); }
.cat-title {
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  margin-bottom: 22px;
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
}
.cat-note {
  font-family: "Plus Jakarta Sans", sans-serif;
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--bronze-deep);
}

.rows { display: flex; flex-direction: column; }
.row {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 14px 0;
  border-top: 1px solid var(--hairline);
  transition: transform 0.5s var(--ease-lux);
}
.row:hover { transform: translateX(6px); }
.r-name { font-size: 0.93rem; font-weight: 400; max-width: 58ch; }
.r-name small {
  display: block;
  font-size: 0.78rem;
  font-style: italic;
  color: var(--taupe-deep);
  font-weight: 300;
  margin-top: 2px;
}
.r-dots {
  flex: 1;
  border-bottom: 1px dotted rgba(42, 32, 24, 0.22);
  transform: translateY(-4px);
  min-width: 20px;
}
.r-time { font-size: 0.78rem; color: var(--taupe-deep); white-space: nowrap; font-weight: 300; }
.r-price {
  font-family: "Cormorant Garamond", serif;
  font-weight: 600;
  font-size: 1.2rem;
  white-space: nowrap;
  min-width: 46px;
  text-align: right;
}
.cat-legal { margin-top: 16px; font-size: 0.76rem; font-style: italic; color: var(--taupe-deep); }

.rows-2col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 48px;
}
/* Un forfait n'est pas une zone de plus : il traverse les deux colonnes pour
   se lire comme un ensemble, et il ouvre la liste. Seul de sa catégorie à
   porter une durée : c'est justement ce qui le distingue des lignes à l'unité. */
.rows-2col .row-forfait { grid-column: 1 / -1; border-top: 0; }

/* ═══════════ HEAD SPA ═══════════ */

.headspa {
  position: relative;
  background:
    radial-gradient(60% 50% at 85% 0%, rgba(168, 127, 76, 0.14), transparent 60%),
    radial-gradient(50% 45% at 5% 100%, rgba(196, 166, 120, 0.1), transparent 60%),
    var(--espresso);
  color: var(--ivory);
  padding: clamp(110px, 15vh, 180px) clamp(24px, 7vw, 110px);
}
.hs-head { max-width: 1320px; margin: 0 auto 70px; }
.hs-head .lead { max-width: 58ch; }
.hs-head .lead + .lead { margin-top: 20px; }
.hs-mark {
  position: absolute;
  top: clamp(60px, 9vh, 110px);
  right: 8%;
  width: clamp(70px, 8vw, 120px);
  opacity: 0.14;
  pointer-events: none;
}
.hs-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(24px, 3vw, 44px);
  max-width: 1320px;
  margin: 0 auto;
}
.hs-card { display: flex; flex-direction: column; }
.hs-card > .bezel-core { flex: 1; }
.hs-core { display: flex; flex-direction: column; }
.hs-media { aspect-ratio: 16 / 9; }
.hs-media { background:
    radial-gradient(120% 90% at 20% 15%, rgba(196, 166, 120, 0.4), transparent 55%),
    radial-gradient(100% 90% at 85% 90%, rgba(168, 127, 76, 0.3), transparent 60%),
    linear-gradient(160deg, #4a3a28, #33271a 60%, #2a2015);
}
.hs-body { padding: clamp(24px, 2.6vw, 38px); }
.hs-row { display: flex; align-items: baseline; justify-content: space-between; gap: 18px; margin-bottom: 14px; }
.hs-title { font-size: clamp(1.5rem, 2.2vw, 1.9rem); color: var(--ivory); }
.hs-price {
  font-family: "Cormorant Garamond", serif;
  font-weight: 600;
  font-size: 1.7rem;
  color: var(--bronze-soft);
  white-space: nowrap;
}
.hs-price span { font-size: 0.85rem; color: rgba(253, 250, 244, 0.45); font-family: "Plus Jakarta Sans", sans-serif; font-weight: 300; }
.hs-desc { font-size: 0.9rem; font-weight: 300; line-height: 1.8; color: rgba(253, 250, 244, 0.6); }

.hs-intro {
  font-size: 0.68rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--bronze-soft);
  margin-bottom: 18px;
}
.hs-steps {
  counter-reset: step;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.hs-steps li {
  counter-increment: step;
  display: flex;
  gap: 15px;
  font-size: 0.9rem;
  font-weight: 300;
  line-height: 1.5;
  color: rgba(253, 250, 244, 0.72);
}
.hs-steps li::before {
  content: counter(step, decimal-leading-zero);
  font-family: "Cormorant Garamond", serif;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--bronze-soft);
  flex-shrink: 0;
  min-width: 22px;
}

/* ═══════════ REGARD ═══════════ */

.regard {
  padding: clamp(100px, 14vh, 170px) clamp(24px, 7vw, 110px);
  background:
    radial-gradient(50% 60% at 100% 0%, rgba(168, 127, 76, 0.1), transparent 60%),
    var(--cream-deep);
  border-top: 1px solid var(--hairline);
}
.regard-grid {
  display: grid;
  grid-template-columns: 1fr 1.05fr;
  gap: clamp(48px, 7vw, 110px);
  align-items: center;
  max-width: 1320px;
  margin: 0 auto;
}
.regard-text .btn { margin-top: 36px; }

/* ═══════════ AVANT / APRÈS ═══════════ */

.realisations {
  padding: clamp(100px, 14vh, 170px) clamp(24px, 7vw, 110px);
  background: var(--cream);
}
.ba-head { max-width: 1320px; margin: 0 auto 64px; }

.ba-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 2.6vw, 34px);
  max-width: 1320px;
  margin: 0 auto;
}

.ba-wrap {
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  touch-action: pan-y;
}
.ba-layer { position: absolute; inset: 0; }
.ba-layer img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.ba-after { clip-path: inset(0 0 0 var(--pos)); }
/* Le calque avant est découpé à droite du curseur : son étiquette disparaît avec lui */
.ba-layer:not(.ba-after) { clip-path: inset(0 calc(100% - var(--pos)) 0 0); }

/* Placeholders différenciés tant que les photos manquent */
.ba-ph-before {
  background:
    radial-gradient(100% 80% at 20% 15%, rgba(200, 192, 180, 0.8), transparent 60%),
    linear-gradient(155deg, #d3cabd, #b3a795 60%, #9e9080);
}
.ba-ph-after {
  background:
    radial-gradient(110% 90% at 25% 12%, rgba(230, 210, 180, 0.85), transparent 60%),
    linear-gradient(155deg, #e9dcc6, #d3ba92 60%, #c0a274);
}

.ba-tag {
  position: absolute;
  top: 14px; left: 14px;
  z-index: 2;
  padding: 6px 13px 5px;
  border-radius: 999px;
  background: rgba(34, 26, 19, 0.55);
  color: var(--ivory);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.ba-tag-r { left: auto; right: 14px; }

.ba-handle {
  position: absolute;
  top: 0; bottom: 0;
  left: var(--pos);
  width: 2px;
  margin-left: -1px;
  background: rgba(253, 250, 244, 0.95);
  box-shadow: 0 0 14px rgba(34, 26, 19, 0.35);
  z-index: 3;
  pointer-events: none;
}
.ba-knob {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 42px; height: 42px;
  border-radius: 50%;
  background: var(--ivory);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.95rem;
  box-shadow: 0 6px 22px -6px rgba(34, 26, 19, 0.45);
  transition: transform 0.5s var(--ease-lux);
}
.ba-wrap:hover .ba-knob { transform: translate(-50%, -50%) scale(1.08); }

.ba-range {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  margin: 0;
  opacity: 0;
  cursor: ew-resize;
  z-index: 4;
  -webkit-appearance: none;
  appearance: none;
}

.ba-cap {
  padding: 18px 22px;
  font-size: 0.84rem;
  font-weight: 500;
  letter-spacing: 0.03em;
  color: var(--ink-soft);
  border-top: 1px solid var(--hairline);
}

/* ═══════════ AVIS CLIENTS ═══════════ */

.avis {
  padding: clamp(100px, 14vh, 170px) 0;
  background: var(--ivory);
  overflow: hidden;
}
/* Le filet haut est retiré : la guirlande reprend elle-même les deux fonds et
   le voile éteint la jonction, un trait horizontal reparaîtrait juste dessous.
   Le rembourrage haut monte à 150 px sur ordinateur parce que la bande mord
   96,5 px dans la section, contre 46 px pour l'ancienne vigne. Sur téléphone
   elle n'en mord que 25, la valeur en vh y suffit donc et n'est pas touchée.
   Le sélecteur vise la section qui SUIT une bande : sans elle il n'y a rien à
   compenser, et les pages légales garderaient un blanc de 38 px de trop. */
@media (min-width: 861px) {
  .vine-rule + .avis { padding-top: max(150px, clamp(100px, 14vh, 170px)); }
}
.avis-head {
  max-width: 1320px;
  margin: 0 auto 64px;
  padding: 0 clamp(24px, 7vw, 110px);
}
.avis-score {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-top: 26px;
}
.avis-score .stars { font-size: 1.25rem; }
.avis-score { flex-wrap: wrap; }

.verif {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 14px 6px 11px;
  border-radius: 999px;
  border: 1px solid rgba(168, 127, 76, 0.3);
  background: rgba(168, 127, 76, 0.07);
  color: var(--bronze-deep);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: border-color 0.5s var(--ease-lux), background 0.5s var(--ease-lux);
}
.verif:hover { border-color: rgba(168, 127, 76, 0.5); background: rgba(168, 127, 76, 0.11); }
.verif svg { width: 14px; height: 14px; flex-shrink: 0; }
.stars {
  color: var(--bronze-deep);
  letter-spacing: 0.22em;
  font-size: 0.95rem;
}
.star-part {
  letter-spacing: 0;
  background: linear-gradient(90deg, var(--bronze) 88%, rgba(168, 127, 76, 0.16) 88%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
.score-note {
  font-size: 0.8rem;
  color: var(--taupe-deep);
  font-weight: 300;
  letter-spacing: 0.04em;
}

.avis-marquee { position: relative; }
.avis-fade {
  position: absolute;
  /* 🔴 Le voile déborde de sa piste, et ce n'est pas un confort : les cartes
     portent une ombre portée de 60 px décalée de 24 vers le bas
     (`0 24px 60px -24px`), qui descend donc bien plus bas que le ruban. À
     `bottom: 0`, le voile la recouvrait sur sa largeur puis s'arrêtait net :
     l'ombre reparaissait d'un coup et dessinait un trait horizontal net sous
     chaque extrémité du ruban, visible sur les accueils comme sur les pages
     d'atterrissage. Mesuré : 50 px d'ombre au-delà du voile. On l'étend au
     débord réel de l'ombre, haut compris. */
  top: -16px; bottom: -64px;
  width: clamp(40px, 8vw, 160px);
  z-index: 2;
  pointer-events: none;
}
.avis-fade-l { left: 0; background: linear-gradient(90deg, var(--ivory), transparent); }
.avis-fade-r { right: 0; background: linear-gradient(-90deg, var(--ivory), transparent); }

.avis-track {
  display: flex;
  gap: 26px;
  width: max-content;
  padding: 10px 0;
  animation: avis-scroll 55s linear infinite;
  /* 🔴 La durée seule ne fixe pas une vitesse : elle fixe le temps mis à
     parcourir la moitié de la piste, et cette moitié vaut le nombre de
     témoignages. À 55 s fixes, les huit avis des accueils défilaient deux fois
     plus vite que les quatre d'une page d'atterrissage, et près de trois fois
     plus vite que les trois d'une autre. La durée devient donc proportionnelle
     au nombre de cartes, et c'est la VITESSE qui est constante sur tout le
     site : 16 s par témoignage, soit environ 27 px/s.
     `--avis-n` est écrit par le générateur sur la piste ; sa valeur par défaut
     est celle des deux accueils, qui n'ont donc rien à déclarer. */
  animation-duration: calc(var(--avis-n, 8) * 16s);
}
/* 🔴 La pause au survol est réservée à la souris. Au doigt, un tap déclenche
   un :hover qui RESTE actif jusqu'au tap suivant : le ruban s'arrêtait dès
   qu'on le touchait et ne repartait qu'au défilement suivant. Même raison que
   le bloc ÉCRANS TACTILES, qui neutralise déjà les déplacements au survol. */
@media (hover: hover) and (pointer: fine) {
  .avis-marquee:hover .avis-track { animation-play-state: paused; }
}
/* Hors du champ, le ruban s'arrête. Voir la note « Les rubans décoratifs » de
   js/main.js : c'est un calque de 5639 px de large que le compositeur faisait
   avancer à chaque image, y compris à trois écrans de distance. La reprise se
   fait 200 px avant l'entrée en vue, et une boucle qui repart d'où elle s'est
   arrêtée ne se voit pas. La règle de survol reste au-dessus : les deux posent
   le même état, elles ne peuvent pas se contredire. */
.hors-vue .avis-track { animation-play-state: paused; }
@keyframes avis-scroll { to { transform: translateX(calc(-50% - 13px)); } }

.avis-card {
  width: clamp(300px, 30vw, 400px);
  flex-shrink: 0;
  transition: transform 0.6s var(--ease-lux);
}
.avis-card:hover { transform: translateY(-6px); }
.avis-core {
  padding: 30px 32px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  height: 100%;
}
.avis-core .stars { font-size: 0.8rem; }
.avis-core blockquote {
  font-family: "Cormorant Garamond", serif;
  font-size: 1.22rem;
  font-weight: 500;
  line-height: 1.45;
  font-style: italic;
}
/* Le pied de carte doit garder exactement la même hauteur d'un avis à l'autre,
   sinon un nom long ou un libellé de prestation qui passe sur deux lignes fait
   remonter la photo par rapport aux cartes voisines. Deux garde-fous : le nom
   ne se coupe jamais, et le libellé reste plus court que l'avatar même sur deux
   lignes. La hauteur du pied est donc toujours celle de l'avatar. */
.avis-core figcaption {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 34px;
  border-top: 1px solid var(--hairline);
  padding-top: 14px;
  margin-top: auto;
}
.avis-who { display: inline-flex; align-items: center; gap: 10px; flex-shrink: 0; }
.avis-avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  object-fit: cover;
  border: 1px solid var(--hairline);
  flex-shrink: 0;
}
.avis-name { font-size: 0.85rem; font-weight: 600; letter-spacing: 0.03em; white-space: nowrap; }
.avis-presta {
  font-size: 0.72rem;
  line-height: 1.3;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--bronze-deep);
  font-weight: 600;
  text-align: right;
}

@media (prefers-reduced-motion: reduce) {
  .avis-track { animation: none; flex-wrap: wrap; width: auto; padding: 10px clamp(24px, 7vw, 110px); }
  .avis-card[aria-hidden="true"] { display: none; }
}

/* ═══════════ DANS L'INSTITUT ═══════════
   Bande sombre glissée entre les avis et l'avant/après. Elle reprend
   l'espresso des engagements, seule autre respiration sombre de la page :
   inventer un troisième registre aurait fait trois pages dans une.

   Les deux colonnes sont calées par le BAS (align-items: flex-end). Leurs
   cadres n'ont pas le même format, 3:4 et 4:5 : les aligner par le haut
   laissait un décrochement en bas de section, exactement là où le regard sort
   du bloc pour aller au bouton. Le décalage des bandes 01 / 02 est le prix
   payé, et il tient lieu de rythme.

   Les deux coutures claires ne décorent pas : elles prolongent l'ivoire des
   avis et le crème de l'avant/après DANS la section, ce qui arrondit les deux
   blocs clairs vers le sombre. Posé sur `.avis`, un border-radius aurait rogné
   le marquee, qui déborde déjà de sa boîte. La mécanique et les jetons de
   couleur vivent maintenant dans le bloc COUTURE DES SECTIONS, avec les six
   autres frontières de la page : ne rien redéfinir ici. */

.institut {
  padding: clamp(104px, 15vh, 190px) clamp(24px, 7vw, 110px);
  background:
    radial-gradient(85% 60% at 50% 6%, rgba(168, 127, 76, 0.17), transparent 64%),
    var(--espresso);
  overflow: hidden;
}

.inst-head {
  max-width: 1320px;
  margin: 0 auto clamp(48px, 7vh, 92px);
  text-align: center;
}
/* .lead porte un max-width de 46ch : sans marges auto il se calait à gauche
   dans un bloc centré, même remède qu'en tête des engagements. */
.inst-head .lead { max-width: 58ch; margin-inline: auto; }

/* ── L'empilement vertical ──
   `.inst-grid` était un flex en wrap, calé par le bas, avec deux bases de 320
   et 380 px. Les deux cadres ne partagent plus de ligne : le calage par le bas
   et le décalage volontaire des bandes 01 / 02 n'ont plus d'objet, et le
   téléphone, qui empilait déjà, y gagne une colonne pleine largeur. */
.inst-stack {
  display: flex;
  flex-direction: column;
  gap: clamp(56px, 8vh, 96px);
  max-width: 1240px;
  margin: 0 auto;
}
.inst-col {
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-width: 0;
}

/* ── 01 · La visite garde son cadre, et se centre ──
   Le 9:16 reste à 392 px. Recadrer une vidéo portrait en pleine largeur
   coûterait le sujet : c'est la galerie qui prend la largeur utile, pas la
   visite.

   Les deux notes latérales occupent la place ainsi libérée. Elles ne portent
   rien d'unique, les engagements 01, 03 et 07 le disent en entier : dès qu'il
   n'y a plus de place pour elles, sous 1121 px, elles sortent sans perte. */
.inst-visite-row {
  position: relative;
  display: flex;
  justify-content: center;
}
/* Les deux notes se posent au quart et aux trois quarts de la hauteur du
   cadre, jamais à sa moitié : c'est ce décalage qui fait tourner le regard
   autour de la vidéo au lieu de le poser sur une ligne. Les hauteurs sont
   celles de la maquette, 24 % et 74 %.
   Aucun filet entre la note et le cadre : la note tient par son alignement
   sur la marge, la ligne ne faisait que remplir le vide. */
/* ⚠️ Les notes sont posées en ABSOLU, et c'est le seul moyen d'atteindre le
   quart et les trois quarts de la HAUTEUR du cadre. Une marge en pourcentage
   se résout sur la LARGEUR du bloc conteneur, jamais sur sa hauteur : essayé,
   les deux notes se retrouvaient à 7 % et 35 %, empilées en haut. `top`, lui,
   se résout bien sur la hauteur. */
.inst-aside {
  position: absolute;
  display: flex;
  align-items: center;
  /* `translate` et non `transform` : les notes portent `.reveal`, qui anime
     `transform`. Les deux se composeraient mal sur la même propriété, et le
     centrage a gagné (il est déclaré plus bas dans la feuille) au prix du
     rideau d'apparition. La propriété indépendante les laisse cohabiter. */
  translate: 0 -50%;
}
.inst-aside-l {
  left: 0;
  right: calc(50% + 232px);
  top: 24%;
}
.inst-aside-r {
  right: 0;
  left: calc(50% + 232px);
  top: 74%;
  text-align: right;
}
.inst-aside-copy { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.inst-aside-r .inst-aside-copy { align-items: flex-end; }
.inst-aside-t {
  font-family: "Cormorant Garamond", serif;
  font-weight: 500;
  font-size: clamp(1.45rem, 1.9vw, 1.9rem);
  line-height: 1.15;
  letter-spacing: -0.005em;
  color: var(--ivory);
}
.inst-aside-t em { font-style: italic; font-weight: 400; color: var(--bronze-soft); }
.inst-aside-d,
.inst-aside-list {
  font-size: 0.82rem;
  font-weight: 300;
  line-height: 1.6;
  color: rgba(253, 250, 244, 0.6);
}
.inst-aside-list { display: flex; flex-direction: column; gap: 9px; }

/* ── La bande numérotée ──
   Chiffre italique, filet, intitulé. Le filet relie les deux extrémités, ce
   qui en fait un seul objet et non deux signes posés côte à côte. Même
   grammaire que la bande des engagements, à l'envers près : ici le numéro
   ouvre la ligne, parce qu'il y en a deux et qu'ils se lisent dans l'ordre. */
.inst-band {
  display: flex;
  align-items: center;
  gap: 16px;
}
.inst-n {
  font-family: "Cormorant Garamond", serif;
  font-style: italic;
  font-size: 1.25rem;
  line-height: 1;
  color: var(--bronze-soft);
}
.inst-rule { flex: 1; height: 1px; background: rgba(196, 166, 120, 0.24); }
.inst-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(253, 250, 244, 0.55);
}

.inst-fig {
  display: flex;
  flex-direction: column;
  gap: 20px;
  margin: 0;
  width: 392px;
  max-width: 100%;
}
.inst-core { position: relative; }
.inst-core-visite { aspect-ratio: 9 / 16; }
/* Même traitement que `.media img` : la vidéo remplit le cadre et se recadre,
   elle ne dicte pas sa hauteur. */
.inst-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Voile bas : il assoit la photo sur l'espresso au lieu de la laisser flotter,
   et prépare le pied de cadre s'il reçoit un jour une étiquette. */
.inst-veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(34, 26, 19, 0.12) 0%,
    rgba(34, 26, 19, 0) 44%,
    rgba(34, 26, 19, 0.55) 100%
  );
}

/* ── Le bouton de son posé sur la vidéo ──
   Le voile de lisibilité se pose là où est le texte, jamais sur toute la
   surface : c'est le corollaire tiré du premier écran, et il s'applique ici
   à la lettre. La pastille porte donc son propre fond espresso à 0,72.

   Mesuré sur l'image la plus claire que puisse servir la vidéo (les fauteuils
   blancs, ~250) : ivoire plein sur la pastille tient 6,5:1, et 16,8:1 sur une
   image sombre. À 0,55, valeur de la maquette, on tombait à 3,8:1. Pas de
   `backdrop-filter` : le premier écran a démontré que le flou ne tient pas la
   lisibilité, seul le voile la tient. */
.inst-sound {
  position: absolute;
  top: 14px;
  right: 14px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 15px;
  border-radius: 999px;
  border: 1px solid rgba(253, 250, 244, 0.24);
  background: rgba(34, 26, 19, 0.72);
  font-family: inherit;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ivory);
  cursor: pointer;
  transition: background 0.5s var(--ease-lux), border-color 0.5s var(--ease-lux);
}
.inst-sound:hover {
  background: rgba(34, 26, 19, 0.85);
  border-color: rgba(196, 166, 120, 0.6);
}
/* Le point est le seul état visible du bouton, et il double le mot : éteint,
   il est ivoire sourd ; allumé, il passe au bronze. Le bronze qualifie l'état,
   il ne le déclenche pas, ce que le Bronze Muet autorise. */
.inst-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: rgba(253, 250, 244, 0.34);
  transition: background 0.5s var(--ease-lux);
}
.inst-sound[aria-pressed="true"] .inst-dot { background: var(--bronze-soft); }

/* ═══════════ 02 · LES CRÉATIONS ═══════════
   Le cadre unique « Le détail » devient une galerie en profondeur : cinq
   cartes montées sur un anneau, la centrale nette, les latérales reculées et
   inclinées. Le nail art n'est plus une pièce à conviction, c'en est cinq.

   Aucun verre dépoli. La charte arrête les transparences à la barre de
   navigation, et la pile des engagements a démontré qu'un fond translucide
   laisse lire le texte du dessous. La profondeur vient donc du double biseau,
   pas du flou : cartes opaques en espresso 2, liseré ivoire à 10 %, lèvre
   claire en haut, ombre large à étalement négatif. Le seul verre est la
   pilule flottante, cousine de la barre de navigation, et rien ne se lit à
   travers elle.

   Le flou des cartes latérales n'est pas un effet de verre, c'est une
   distance. Il porte la profondeur, jamais la lisibilité d'un texte, ce qui
   reste dans la règle du premier écran : un voile tient le texte, un flou ne
   le tient pas. Chaque légende a donc son voile local en espresso, comme le
   bouton de son de la vidéo juste au-dessus.

   Le bronze ne porte aucune action ici : il numérote, il souligne la
   technique, il qualifie le point actif. La seule surface d'or de la section
   reste le bouton de réservation. */

.gal {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  /* 22 px et non 28 : la bande, la scène, la pilule et les points doivent
     tenir sous la ligne de flottaison d'un portable, sinon la pagination se
     lit à moitié et personne ne devine qu'il y a cinq créations. */
  gap: 18px;
  /* Géométrie de la scène. Les quatre nombres sans unité sont relus par
     js/main.js à chaque redimensionnement : le CSS reste la seule source des
     points de bascule, le script ne connaît aucun seuil.
     La scène fait 60 px de plus que la carte, pas 130 : le reste était du
     vide, et c'est ce vide qui poussait les points hors de l'écran. */
  --gal-w: 400px;
  --gal-h: 500px;
  --gal-step: 300;
  --gal-depth: 190;
  --gal-fan: 22;
  --gal-stage: 530px;
  --gal-persp: 1600px;
}
.gal-scene { position: relative; }
/* Halo de section, à l'aplomb du halo bronze existant. Sa teinte est celle de
   la carte au centre, interpolée en continu par le script. Aucun `filter` :
   un radial-gradient est déjà doux, et un flou de 70 px sur un calque de
   cette taille coûtait une surface intermédiaire par image. */
.gal-ambient {
  position: absolute;
  inset: -14% -10%;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(58% 52% at 50% 32%, rgba(216, 189, 147, 0.17), transparent 66%);
}
/* La perspective se déclare sur la scène, pas sur les cartes : c'est déjà la
   règle de la bascule du cadre 02 aujourd'hui. `pan-y` rend le défilement
   vertical de la page au doigt, le geste horizontal reste à la galerie. */
.gal-stage {
  position: relative;
  z-index: 1;
  height: var(--gal-stage);
  perspective: var(--gal-persp);
  perspective-origin: 50% 46%;
  /* Pas de `transform-style: preserve-3d`. La `perspective` du parent
     s'applique aux transformations 3D des enfants sans lui ; il ne sert qu'au
     tri en profondeur et aux petits-enfants. Or chaque carte porte un
     `filter` et une opacité inférieure à 1, deux propriétés de groupement qui
     l'aplatissent de toute façon : le contexte 3D était maintenu à chaque
     image pour rien. Le tri se fait au `z-index`, écrit par la boucle
     (`100 − |d| × 10`), donc de façon déterministe. */
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
  border-radius: var(--r-xl);
}
/* L'anneau de focus sort à 12 px : plus près, il passait sous les cartes
   latérales, qui débordent la scène. */
.gal-stage:focus-visible { outline-offset: 12px; border-radius: var(--r-xl); }

/* Aucune transition sur le transform : la boucle de js/main.js l'écrit à
   chaque image, une transition CSS se battrait avec elle et le drag
   décrocherait du doigt. */
.gal-card {
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--gal-w);
  height: var(--gal-h);
  margin: calc(var(--gal-h) / -2) 0 0 calc(var(--gal-w) / -2);
  cursor: pointer;
  /* `will-change` n'est PAS permanent. Cinq cartes promues en continu, chacune
     avec un filtre et une ombre de 70 px de flou, c'est cinq surfaces que le
     compositeur transporte à chaque image du défilement, y compris quand la
     galerie ne bouge pas. C'est la règle « une carte invisible reste un
     calque » appliquée à l'envers : ici les cartes sont visibles mais
     immobiles. L'attribut est posé par js/main.js au réveil de la boucle et
     retiré à son repos.
     Pas de `backface-visibility` non plus : la rotation ne dépasse jamais
     44°, aucune face arrière n'est exposée, et l'indice coûtait une
     promotion de plus. */
}
.gal[data-moving] .gal-card { will-change: transform, opacity; }
.gal-card[data-active] { cursor: grab; }
.gal[data-dragging] .gal-card[data-active] { cursor: grabbing; }
.gal-core { width: 100%; height: 100%; }
/* `.media::after` pose une vignette sur toutes les images du site. Ici le
   voile de la carte fait déjà ce travail, et c'est un dégradé de plus à
   peindre par carte, sous deux `backdrop-filter` fixes sur téléphone. */
.gal-core::after { content: none; }

/* Le survol des cartes en biseau agrandit l'image partout ailleurs sur le
   site. Ici c'est la boucle qui écrit le transform de l'image, parallaxe
   comprise : la règle commune est neutralisée et la durée ramenée à celle du
   geste, celui de la lueur qui suivait le curseur sur le cadre 02. */
.gal-core img { transform: scale(1.06); transition: transform 0.6s var(--ease-lux); }
@media (hover: hover) and (pointer: fine) {
  .gal-card:hover .gal-core img { transform: scale(1.06); }
}
.gal[data-dragging] .gal-core img { transition: none; }

/* ── Aucune photo ne s'attrape à la souris ──
   Un `<img>` est nativement transportable : un clic-glisser en détache un
   fantôme translucide qui suit le curseur, et le navigateur se met à proposer
   de le déposer ailleurs. Sur un carrousel qu'on fait justement défiler au
   glisser, les deux gestes se superposent, et le fantôme est le seul des deux
   qu'on remarque. Même chose sur le comparateur avant/après, qui se manipule
   aussi en tirant.

   Trois verrous, parce qu'aucun ne suffit seul : `-webkit-user-drag` couvre
   Safari et Chrome, l'attribut `draggable="false"` du balisage couvre Firefox,
   qui ne connaît pas la propriété, et js/main.js refuse `dragstart` en dernier
   ressort, ce qui prend aussi la vidéo et tout ce qu'on ajoutera plus tard. */
.gal-core img,
.gal-core picture,
.gal-thumb img,
.ba-layer img {
  -webkit-user-drag: none;
  user-select: none;
  -webkit-user-select: none;
}

/* `.media::after` pose sa vignette après tous les enfants : sans plan
   explicite, elle passerait par-dessus la légende. */
/* ── La lueur qui suit le curseur ──
   Reprise à l'identique de l'ancien cadre « 02 · Le détail » : même dégradé,
   même `soft-light`, même rôle. Le mode de fusion n'est pas décoratif. Un
   calque blanc en `normal` délaverait le nude des ongles, qui est précisément
   ce qu'on donne à juger ; en `soft-light` il éclaire la surface sans toucher
   à ce qui est posé dessus. Le dégradé est peint par js/main.js, dans la même
   boucle que la bascule. */
.gal-glare {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  opacity: 0;
  mix-blend-mode: soft-light;
}
.gal-veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(34, 26, 19, 0.1) 0%,
    rgba(34, 26, 19, 0) 44%,
    rgba(34, 26, 19, 0.42) 100%
  );
  transition: opacity 0.8s var(--ease-settle);
}

/* Le libellé court reste monté sur les cinq cartes et c'est son opacité qui
   se fond quand la carte prend le centre : monter et démonter le bloc le
   faisait disparaître d'un coup au passage. */
.gal-side {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 3;
  padding: 22px;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  gap: 6px;
  /* Le voile du libellé est mesuré, pas estimé. Sur le pixel le plus clair
     des cinq photos, l'ivoire plein du nom tombait à 3,08:1 et le libellé à
     0,6 d'alpha à 3,05:1, sous les 4,5:1 exigés d'un petit texte. Trois
     arrêts au lieu de deux, 0,94 sous les mots, et l'alpha du libellé porté à
     0,72 : mesuré à 5,7:1 au pire pixel, le nom à 5,3:1. Le haut du dégradé
     reste transparent, la photo n'est pas coupée en deux. */
  background: linear-gradient(
    0deg,
    rgba(34, 26, 19, 0.94) 0%,
    rgba(34, 26, 19, 0.78) 62%,
    rgba(34, 26, 19, 0) 100%
  );
  transition: opacity 0.8s var(--ease-settle), transform 0.8s var(--ease-settle);
}
.gal-card[data-active] .gal-side { opacity: 0; transform: translateY(10px); }
.gal-side-t {
  font-family: "Cormorant Garamond", serif;
  font-weight: 500;
  font-size: 1.35rem;
  line-height: 1.05;
  letter-spacing: -0.005em;
  color: var(--ivory);
}
.gal-side-s {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(253, 250, 244, 0.72);
}

/* ── Ordinateur : la légende est au survol ──
   Au repos la carte active est une photo nue, seul son voile bas subsiste. À
   l'entrée du pointeur le voile monte et les deux blocs arrivent en cascade.
   À la sortie tout repart ensemble, d'où les retards portés par l'état ouvert
   seulement. Le nail art reste donc visible tant qu'on ne demande pas la
   légende, ce qui est exactement ce qu'on donne à juger. */
.gal-over {
  position: absolute;
  inset: 0;
  z-index: 4;
  padding: 30px 28px 28px;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 14px;
  background: linear-gradient(
    0deg,
    rgba(34, 26, 19, 0.94) 8%,
    rgba(34, 26, 19, 0.72) 46%,
    rgba(34, 26, 19, 0.18) 78%,
    rgba(34, 26, 19, 0.1)
  );
  opacity: 0;
  transition: opacity 0.62s var(--ease-settle);
}
.gal-over-t,
.gal-over-s {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.62s var(--ease-settle), transform 0.62s var(--ease-settle);
}
.gal-over-t {
  font-family: "Cormorant Garamond", serif;
  font-weight: 500;
  font-size: clamp(2rem, 2.6vw, 2.9rem);
  line-height: 1.02;
  letter-spacing: -0.005em;
  color: var(--ivory);
}
.gal-over-s {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-left: 14px;
  border-left: 1px solid rgba(196, 166, 120, 0.5);
}
.gal-card[data-legend] .gal-veil { opacity: 0; }
.gal-card[data-legend] .gal-over { opacity: 1; }
.gal-card[data-legend] .gal-over-t,
.gal-card[data-legend] .gal-over-s { opacity: 1; transform: translateY(0); }
.gal-card[data-legend] .gal-over-t { transition-delay: 0.12s; }
.gal-card[data-legend] .gal-over-s { transition-delay: 0.18s; }

/* Le nom est le héros, le titre devient sa signature : bronze pour la
   personne, italique sourd pour la fonction, filet bronze à gauche. */
.gal-over-by,
.gal-cap-by {
  font-size: 0.92rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--bronze-soft);
}
.gal-over-q,
.gal-cap-q {
  font-style: italic;
  font-size: 0.88rem;
  font-weight: 300;
  line-height: 1.4;
  color: rgba(253, 250, 244, 0.68);
}

/* ── Téléphone : la légende passe sous la scène ──
   Pas de survol au doigt et pas de place pour un pavé de texte sur une carte
   de 236 px : la légende sort de la photo et se pose entre la scène et la
   pilule, comme la légende sous l'ancien cadre 02. La photo reste
   entière, la cascade rejoue à chaque changement de carte. */
.gal-cap { display: none; }
.gal-cap-t {
  font-family: "Cormorant Garamond", serif;
  font-weight: 500;
  font-size: 2.1rem;
  line-height: 1.02;
  letter-spacing: -0.005em;
  color: var(--ivory);
}
.gal-cap-s {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-left: 13px;
  border-left: 1px solid rgba(196, 166, 120, 0.5);
}
/* ── La passation de légende ──
   Une création rend le centre à la suivante ; la légende doit faire le même
   geste, pas clignoter. Elle sortait d'un coup (`transition: none`) pour
   rentrer en fondu, et c'est exactement ce qui se lisait comme un à-coup.

   Elle sort donc vers le HAUT, vite et d'un bloc, et revient du BAS,
   lentement et en cascade. Sortir plus vite qu'on entre est la règle : une
   sortie longue se lit comme de la latence, une entrée longue se lit comme de
   l'attention. Les 260 ms de sortie sont calées sur le ressort de l'anneau,
   qui bascule d'index vers 300 ms : le texte est déjà parti quand il change. */
.gal-cap-t,
.gal-cap-by,
.gal-cap-q {
  transition: opacity 0.76s var(--ease-settle), transform 0.76s var(--ease-settle);
}
.gal-cap-by { transition-delay: 0.09s; }
.gal-cap-q { transition-delay: 0.15s; }
.gal-cap[data-out] .gal-cap-t,
.gal-cap[data-out] .gal-cap-by,
.gal-cap[data-out] .gal-cap-q {
  opacity: 0;
  transform: translateY(-7px);
  transition:
    opacity 0.26s var(--ease-lux),
    transform 0.26s var(--ease-lux);
  transition-delay: 0s;
}
/* Point de départ de l'entrée : sous la ligne, jamais au-dessus. */
.gal-cap[data-in] .gal-cap-t,
.gal-cap[data-in] .gal-cap-by,
.gal-cap[data-in] .gal-cap-q {
  opacity: 0;
  transform: translateY(14px);
  transition: none;
}

/* ── La pilule flottante ──
   Le seul verre autorisé de la section, et il est cousin de la barre de
   navigation : même flou de 20 px, même liseré ivoire, même ombre. Elle
   chevauche le bas de la scène de 18 px, ce qui la pose SUR la galerie au
   lieu de la mettre à la suite. */
.gal-pill-row {
  position: relative;
  z-index: 5;
  display: flex;
  justify-content: center;
  margin-top: -14px;
}
/* ⚠️ AUCUN `backdrop-filter` ICI, ET C'EST DÉFINITIF. Trois fois que ce dépôt
   tranche dans le même sens : couche `.opening-blur` retirée du premier écran
   le 22/08, cartes de l'éventail le 23/08, et la pilule le 26/08.

   Le flou de fond a d'abord été retiré au doigt, pour le coût : il relit son
   fond à chaque image, et ce fond contient cinq cartes filtrées sous une
   perspective. Il est maintenant retiré partout, pour une seconde raison, plus
   visible encore. Un `backdrop-filter` échantillonne le fond TEL QU'IL EST
   COMPOSÉ. Or les cartes ne sont promues en calques que pendant que la boucle
   tourne (`[data-moving]`), donc le fond change de découpage en cours de
   route, et la couture d'un calque traverse la pilule : l'arête basse de la
   carte centrale tombe à 26 px sous son bord haut, celle du halo à 60 px.
   D'où le trait clair qui apparaissait par intermittence, au défilement et au
   retour de la bascule, jamais au repos.

   L'épaisseur vient donc du liseré clair, de la lèvre de lumière et de
   l'ombre, exactement comme sur les cartes de l'éventail. Ne pas remettre de
   flou pour « faire premium » : c'est ce flou qui salissait le bouton. */
.gal-pill {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 12px 16px;
  max-width: 100%;
  border-radius: 999px;
  border: 1px solid rgba(253, 250, 244, 0.14);
  /* Fond plein, plus une lèvre de lumière sur les 42 % du haut. Une capsule
     d'un seul ton se lit comme un bloc découpé ; le dégradé lui rend une
     courbure, sans rien flouter. Même grammaire que la lèvre `inset` des
     cadres en biseau, en plus haut. */
  background:
    linear-gradient(180deg, rgba(253, 250, 244, 0.06), rgba(253, 250, 244, 0) 42%),
    #2b211a;
  box-shadow:
    0 30px 70px -30px rgba(0, 0, 0, 0.55),
    inset 0 1px 1px rgba(255, 255, 255, 0.09);
}

/* ── La flèche ──
   Le chevron est dessiné, pas composé : `‹` et `›` sont des glyphes de fonte,
   ils ne partagent ni la graisse ni les extrémités arrondies des icônes de la
   maison, et ils ne s'animent pas indépendamment de leur boîte. Même
   convention que `.eng-ico` : viewBox de 24, `currentColor`, bouts ronds.

   La pression tient en trois gestes qui partent ensemble et se relâchent
   ensemble : le bouton s'enfonce, le chevron prend la direction du voyage, et
   un halo bronze éclôt puis se dissipe. C'est le même halo que l'anneau de
   focus du site, joué une fois. Le retour se fait sur `--ease-settle`, donc
   long et sans rebond : c'est là que la pression se sent. */
.gal-arrow {
  position: relative;
  width: 46px;
  height: 46px;
  flex: none;
  border-radius: 999px;
  border: 1px solid rgba(253, 250, 244, 0.22);
  /* Un ton au-dessus de la pilule : sans cet écart les trois objets se
     fondaient en une seule barre. */
  background: rgba(253, 250, 244, 0.055);
  color: var(--ivory);
  display: grid;
  place-items: center;
  cursor: pointer;
  transition:
    background 0.7s var(--ease-settle),
    border-color 0.7s var(--ease-settle),
    color 0.7s var(--ease-settle),
    transform 0.75s var(--ease-settle),
    box-shadow 0.7s var(--ease-settle);
}
.gal-chev {
  width: 21px;
  height: 21px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.25;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 0.75s var(--ease-settle);
}
/* Le halo n'existe que le temps de la pression : hors animation il est à
   opacité nulle et ne coûte rien. */
.gal-arrow::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: 999px;
  border: 1px solid var(--bronze-soft);
  opacity: 0;
  pointer-events: none;
}
@keyframes gal-bloom {
  from { opacity: 0.5; transform: scale(0.82); }
  to { opacity: 0; transform: scale(1.5); }
}
.gal-arrow[data-press]::after { animation: gal-bloom 0.62s var(--ease-lux); }
.gal-arrow[data-press] {
  background: rgba(196, 166, 120, 0.18);
  border-color: rgba(196, 166, 120, 0.55);
  color: var(--bronze-pale);
  transform: scale(0.9);
  transition-duration: 0.13s;
}
/* Le chevron part dans le sens du voyage : c'est le seul lien visible entre
   le bouton et l'anneau qui tourne derrière. */
.gal-arrow-prev[data-press] .gal-chev { transform: translateX(-3px); transition-duration: 0.13s; }
.gal-arrow-next[data-press] .gal-chev { transform: translateX(3px); transition-duration: 0.13s; }
/* Sans souris, un survol se déclenche au doigt et RESTE actif jusqu'au tap
   suivant : la flèche restait allumée après la pression. Le survol est donc
   réservé au pointeur fin, et il ne reste que `:active`, qui s'éteint tout
   seul à la levée du doigt, sur les 750 ms de `--ease-settle`. */
@media (hover: hover) and (pointer: fine) {
  .gal-arrow:hover {
    background: rgba(253, 250, 244, 0.13);
    border-color: rgba(196, 166, 120, 0.5);
    box-shadow: 0 0 26px 0 rgba(196, 166, 120, 0.14);
  }
}
.gal-arrow,
.gal-dot { -webkit-tap-highlight-color: transparent; }

.gal-brand { display: flex; align-items: center; gap: 12px; padding: 0 4px; min-width: 0; }
.gal-thumb {
  width: 48px;
  height: 48px;
  flex: none;
  border-radius: 999px;
  border: 1px solid rgba(196, 166, 120, 0.35);
  background: var(--cream-deep);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.7);
}
.gal-thumb img { width: 50%; height: 50%; object-fit: contain; opacity: 0.86; }
.gal-brand-copy { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.gal-brand-n {
  font-family: "Cormorant Garamond", serif;
  font-weight: 500;
  font-size: 1.2rem;
  line-height: 1.05;
  letter-spacing: -0.005em;
  color: var(--ivory);
  white-space: nowrap;
}
.gal-brand-s {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(253, 250, 244, 0.55);
  white-space: nowrap;
}

/* Le compteur passe à côté des points, hors de la photo. */
.gal-dots {
  position: relative;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin-top: 10px;
}
.gal-dots-row { display: flex; align-items: center; gap: 9px; }
.gal-dot {
  width: 6px;
  height: 6px;
  padding: 0;
  border: none;
  border-radius: 999px;
  /* 0,26 d'alpha ne tenait que 2,33:1 sur l'espresso, sous les 3:1 exigés
     d'un élément d'interface. 0,34 le porte à 3,03:1. */
  background: rgba(253, 250, 244, 0.34);
  cursor: pointer;
  transition:
    width 0.78s var(--ease-settle),
    background 0.78s var(--ease-settle),
    transform 0.5s var(--ease-settle);
}
.gal-dot[aria-current="true"] { width: 24px; background: var(--bronze-soft); }
.gal-dot[data-press] { transform: scaleY(1.7); transition-duration: 0.13s; }
/* Le compteur change de chiffre : il se fond, il ne bascule pas. */
.gal-count { transition: opacity 0.42s var(--ease-lux); }
.gal-dots[data-out] .gal-count { opacity: 0; transition-duration: 0.2s; }
.gal-dot:focus-visible { outline-offset: 5px; }
.gal-count {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.18em;
  color: rgba(253, 250, 244, 0.5);
}

/* Sur un portable, c'est la HAUTEUR qui manque, pas la largeur : la carte
   descend d'un cran pour que la bande, la scène, la pilule et les points
   tiennent sous la ligne de flottaison. Sans quoi la pagination se lit à
   moitié et personne ne devine qu'il y a cinq créations. Même vocabulaire de
   points de bascule que le premier écran. */
@media (min-width: 861px) and (max-height: 900px) {
  .gal {
    --gal-w: 344px;
    --gal-h: 430px;
    --gal-step: 262;
    --gal-depth: 168;
    --gal-stage: 458px;
  }
}

@media (max-width: 1120px) {
    .inst-aside { display: none; }
  .gal {
    --gal-w: 336px;
    --gal-h: 420px;
    --gal-step: 248;
    --gal-depth: 158;
    --gal-fan: 20;
    --gal-stage: 472px;
    --gal-persp: 1350px;
  }
}

/* ── Téléphone : la galerie entière tient dans un écran ──
   Première version : scène de 400 px pour une carte de 295, plus trois
   gouttières de 28 et deux marges de 26. Il fallait deux écrans pour aller de
   la photo au bouton suivant, avec un trou de 106 px entre la carte et sa
   légende. La scène est ramenée au strict nécessaire (la carte plus une
   respiration), les gouttières à 16 et la légende resserrée : l'ensemble
   bande, scène, légende, pilule et points tient sous 600 px. */
@media (max-width: 860px) {
  .gal {
    --gal-w: 236px;
    --gal-h: 295px;
    --gal-step: 166;
    --gal-depth: 118;
    --gal-fan: 16;
    --gal-stage: 320px;
    --gal-persp: 1100px;
    gap: 16px;
  }
  .gal-over { display: none; }
  .gal-cap {
    position: relative;
    z-index: 4;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 0 2px;
  }
  .gal-cap-t { font-size: 1.75rem; }
  .gal-cap-by { font-size: 0.86rem; }
  .gal-cap-q { font-size: 0.82rem; }
  .gal-pill-row { margin-top: 2px; }
  .gal-pill { gap: 10px; padding: 10px 14px; }
  .gal-arrow { width: 42px; height: 42px; font-size: 20px; }
  .gal-thumb { width: 40px; height: 40px; }
  .gal-brand { gap: 10px; padding: 0 2px; }
  .gal-brand-n { font-size: 1.1rem; }
  .gal-brand-s { font-size: 9px; letter-spacing: 0.16em; }
  .gal-dots { margin-top: 4px; }
  .gal-side { padding: 16px; }
  .gal-side-t { font-size: 1.1rem; }
  /* La visite et la galerie ne sont plus deux étages d'immeuble. */
  .inst-stack { gap: 56px; }

  /* ⚠️ Aucun flou sur les cartes, et une ombre courte.
     Deux `backdrop-filter` FIXES sont en permanence à l'écran sur téléphone,
     la barre de navigation (20 px) et la barre d'actions (18 px). Chacune
     relit son fond à chaque image. Quand ce fond est une scène en perspective
     portant quatre calques filtrés et cinq ombres de 70 px de flou, le coût
     ne s'additionne pas, il se multiplie : c'est ce qui écroulait le
     défilement, et aucune mesure du fil principal ne pouvait le montrer,
     parce que tout se passe à la rastérisation.
     Le flou de distance est donc retiré (js/main.js n'écrit plus de `filter`
     sous 861 px) et remplacé par ce que le compositeur donne gratuitement :
     l'échelle et l'opacité, dont la chute est accentuée en compensation. Sur
     390 px les cartes latérales sont de toute façon des tranches de 40 px,
     le flou n'y disait presque rien. */
  .gal-card { box-shadow: 0 14px 26px -14px rgba(0, 0, 0, 0.55); }
}

/* Règles ciblées, jamais un `0.01ms` global : la profondeur reste, elle
   devient statique. Le snap est immédiat, l'entrée n'est plus décalée, la
   parallaxe et la cascade ne s'arment pas, et la légende de la carte centrale
   reste affichée en permanence au lieu de dépendre d'un survol. */
@media (prefers-reduced-motion: reduce) {
  .gal-veil,
  .gal-side,
  .gal-over,
  .gal-over-t,
  .gal-over-s,
  .gal-cap-t,
  .gal-cap-by,
  .gal-cap-q,
  .gal-count,
  .gal-core img,
  .gal-arrow,
  .gal-chev,
  .gal-dot { transition: none; }
  /* La pression n'est pas décorative, c'est la confirmation d'une action :
     elle reste, réduite à un changement de couleur sans déplacement ni halo.
     js/main.js ne pose d'ailleurs pas `data-press` dans ce mode. */
  .gal-arrow::after { animation: none; opacity: 0; }
  .gal-card[data-active] .gal-veil { opacity: 0; }
  .gal-card[data-active] .gal-over { opacity: 1; }
  .gal-card[data-active] .gal-over-t,
  .gal-card[data-active] .gal-over-s { opacity: 1; transform: none; }
}

.inst-foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: clamp(24px, 3vw, 34px);
  max-width: 660px;
  margin: clamp(52px, 8vh, 104px) auto 0;
  padding-top: clamp(30px, 4vh, 50px);
  border-top: 1px solid rgba(196, 166, 120, 0.2);
}
.inst-foot-copy { display: flex; flex-direction: column; gap: 8px; }
.inst-foot-lead {
  font-family: "Cormorant Garamond", serif;
  font-weight: 500;
  font-size: clamp(1.35rem, 2.4vw, 1.9rem);
  line-height: 1.25;
  letter-spacing: -0.005em;
  color: var(--ivory);
}
.inst-foot-sub {
  font-size: 0.92rem;
  font-weight: 300;
  line-height: 1.7;
  color: rgba(253, 250, 244, 0.68);
}

/* ── Bouton d'or ──
   Unique exception au Bronze Muet, et elle est datée du 22/08/2026. La règle
   dit qu'un bouton bronze est une erreur ; elle a été écrite pour du bronze
   qui PORTE du texte sur du crème, où il plafonne à 3,22:1. Ici le bronze est
   la surface et l'espresso le texte, ce qui inverse complètement la mesure.

   Deux états ont été essayés avant, tous deux jugés trop discrets sur place :
   le plein encre, illisible sur l'espresso, puis le plein ivoire, correct mais
   noyé : la section est entourée de photos claires, la barre d'actions mobile
   pose trois pilules blanches juste dessous, et un pavé blanc de plus ne se
   distingue de rien. L'or est la seule couleur de la charte qui ne soit ni
   dans les photos ni dans le mobilier de l'interface.

   Le dégradé va du pâle en haut à gauche au bronze plein en bas à droite, dans
   ce sens précisément : le libellé commence en haut à gauche, sur la partie la
   plus claire. Mesuré, texte espresso sur les trois arrêts : 9,48:1 sur
   `--bronze-pale`, 7,41:1 sur `--bronze-soft`, 4,74:1 sur `--bronze`. Le point
   le plus défavorable du bouton passe donc AA, et il tombe sous l'orbe, pas
   sous les mots. Le bord tient les mêmes rapports contre l'espresso.

   Il est aussi plus grand que les autres boutons du site : c'est le seul de sa
   section, il ferme le bloc, il n'a personne avec qui s'accorder. */
.btn-gold {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: 19px 19px 19px 34px;
  font-size: 0.98rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  background: linear-gradient(
    135deg,
    var(--bronze-pale) 0%,
    var(--bronze-soft) 46%,
    var(--bronze) 100%
  );
  color: var(--espresso);
  /* Le halo n'est pas une ombre portée : c'est la même lumière que le radial
     bronze en haut de section, ramenée autour du bouton. Il fait de la pilule
     une source et non un objet posé. */
  box-shadow:
    0 0 0 5px rgba(196, 166, 120, 0.11),
    0 22px 52px -16px rgba(168, 127, 76, 0.6),
    inset 0 1px 0 rgba(255, 255, 255, 0.34);
}
.btn-gold .btn-orb { background: rgba(34, 26, 19, 0.16); }
.btn-gold:hover {
  box-shadow:
    0 0 0 7px rgba(196, 166, 120, 0.17),
    0 28px 64px -16px rgba(168, 127, 76, 0.75),
    inset 0 1px 0 rgba(255, 255, 255, 0.42);
}
/* Le reflet qui traverse le métal. Même grammaire que celui du cadre 02, en
   plus court et plus espacé : il passe une fois toutes les six secondes, assez
   pour accrocher l'œil au défilement, trop peu pour clignoter. */
@keyframes inst-gold-sheen {
  0%, 62% { transform: translateX(-140%) skewX(-18deg); }
  100% { transform: translateX(240%) skewX(-18deg); }
}
.btn-gold::after {
  content: "";
  position: absolute;
  z-index: -1;
  top: -30%;
  bottom: -30%;
  width: 36%;
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.34) 50%,
    rgba(255, 255, 255, 0) 100%
  );
  animation: inst-gold-sheen 6s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}

/* ═══════════ FAQ ═══════════ */

.faq {
  padding: clamp(100px, 14vh, 170px) clamp(24px, 7vw, 110px);
  background: var(--ivory);
  border-top: 1px solid var(--hairline);
}
.faq-head { max-width: 860px; margin: 0 auto 60px; }
.faq-list {
  max-width: 860px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.faq-item {
  background-color: var(--cream);
  border: 1px solid var(--hairline);
  border-radius: 22px;
  box-shadow: 0 1px 2px rgba(42, 32, 24, 0.02);
  /* Durée commune à toutes les transitions de la FAQ : elles doivent se terminer
     exactement ensemble. */
  transition: transform 0.6s var(--ease-lux), box-shadow 0.6s var(--ease-lux),
    border-color 0.6s var(--ease-lux), background-color 0.6s var(--ease-lux);
}
.faq-item:hover {
  transform: translateY(-2px);
  border-color: rgba(168, 127, 76, 0.35);
  box-shadow: 0 18px 40px -22px rgba(42, 32, 24, 0.2);
}
.faq-item[open] { border-color: rgba(168, 127, 76, 0.4); background-color: var(--ivory); }

.faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 24px 28px;
  cursor: pointer;
  list-style: none;
}
.faq-item summary::-webkit-details-marker { display: none; }

.faq-q {
  font-family: "Cormorant Garamond", serif;
  font-weight: 600;
  font-size: clamp(1.15rem, 1.9vw, 1.4rem);
  line-height: 1.25;
}
.faq-x {
  position: relative;
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 1px solid rgba(168, 127, 76, 0.35);
  color: var(--bronze-deep);
  transition: transform 0.6s var(--ease-lux), background-color 0.6s var(--ease-lux),
    color 0.6s var(--ease-lux), border-color 0.6s var(--ease-lux);
}
/* Croix dessinée en CSS : centrage exact et extrémités arrondies,
   contrairement au glyphe « + » dont les métriques sont irrégulières. */
.faq-x::before,
.faq-x::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  background: currentColor;
  border-radius: 999px;
}
.faq-x::before {
  width: 13px;
  height: 2.2px;
  transform: translate(-50%, -50%);
}
.faq-x::after {
  width: 2.2px;
  height: 13px;
  transform: translate(-50%, -50%);
}
.faq-item[open] .faq-x {
  transform: rotate(45deg);
  background-color: var(--bronze-deep);
  border-color: var(--bronze-deep);
  color: var(--ivory);
}

.faq-a {
  overflow: hidden;
  opacity: 0;
  transition: height 0.6s var(--ease-lux), opacity 0.6s var(--ease-lux);
}
.faq-item[open] .faq-a { opacity: 1; }

/* Fermeture : l'attribut « open » ne tombe qu'à la fin du repli. Cette classe
   rend la couleur, la bordure et l'icône à leur état initial DÈS le premier
   instant, pour que tout se termine au même moment. Placée après les règles
   [open] afin de les emporter à spécificité égale. */
.faq-item.is-closing {
  background-color: var(--cream);
  border-color: var(--hairline);
}
.faq-item.is-closing .faq-a { opacity: 0; }
.faq-item.is-closing .faq-x {
  transform: rotate(0deg);
  background-color: transparent;
  border-color: rgba(168, 127, 76, 0.35);
  color: var(--bronze-deep);
}
.faq-a p {
  padding: 0 28px 26px;
  font-size: 0.94rem;
  font-weight: 300;
  line-height: 1.8;
  color: var(--ink-soft);
  max-width: 62ch;
}
.faq-a a {
  color: var(--bronze-deep);
  border-bottom: 1px solid rgba(168, 127, 76, 0.4);
  transition: color 0.4s, border-color 0.4s;
}
.faq-a a:hover { color: var(--ink); border-color: var(--ink); }
/* Le rembourrage du résumé reste constant : toute variation ferait « rebondir »
   la carte en fin de fermeture, une fois le panneau déjà replié. */

.faq-more {
  max-width: 860px;
  margin: 40px auto 0;
  font-size: 0.9rem;
  font-weight: 300;
  color: var(--ink-soft);
}
.faq-more a {
  color: var(--bronze-deep);
  border-bottom: 1px solid rgba(168, 127, 76, 0.4);
  transition: color 0.4s, border-color 0.4s;
}
.faq-more a:hover { color: var(--ink); border-color: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  .lang--switched .lang-thumb, .lang--switched .lang-opt { animation: none; }
  .lang:hover .lang-thumb, .lang:active .lang-thumb { width: 38px; }
  .lang[data-active="en"]:hover .lang-thumb,
  .lang[data-active="en"]:active .lang-thumb { transform: translateX(38px); }
}

/* ═══════════ CONTACT ═══════════ */

.contact { padding: clamp(100px, 14vh, 170px) clamp(24px, 7vw, 110px); }
.contact > .bezel { max-width: 1320px; margin: 0 auto; }
.contact-core {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: clamp(40px, 5vw, 80px);
  padding: clamp(36px, 5vw, 80px);
  background:
    radial-gradient(70% 60% at 90% 10%, rgba(196, 166, 120, 0.16), transparent 60%),
    var(--ivory);
}
/* ══ Le monogramme en filigrane ══
   Même geste que `.hs-mark` sur la section head spa : le symbole posé en
   grand, très pâle, dans le vide de la composition. Il signe la carte sans
   rien disputer au texte.

   🔴 La couleur est CUITE DANS LE FICHIER (`symbol-bronze.png`, généré depuis
   `symbol-dark.png` en conservant l'alpha), et non obtenue par `mask-image` :
   le masque en `url()` n'est pas honoré partout, et là où il échoue le
   pseudo-élément se peindrait en RECTANGLE bronze plein au milieu de la
   carte. C'est la même raison qui a fait cuire les deux voiles de la
   guirlande dans leurs fichiers. Trois variantes du symbole existent
   désormais : dark (nav), cream (head spa), bronze (ici).

   Placement, mesuré et non estimé :
   · En desktop, le coin BAS DROIT. Le cas le plus serré est une page
     d'atterrissage, où il ne reste que 98 px entre le bas de la grille du NAP
     et le bord de la carte : la taille et la marge basse tiennent dans ce
     budget, l'accueil (134 px, cellule vide sous « Langues ») est plus large.
   · En mobile, le coin HAUT DROIT, où la colonne est seule. Le texte du haut
     s'arrête à 137 px sur toutes les pages, dans les deux langues ; le
     filigrane commence après 169 px, au pire cas de 320 px de large. */
.contact-core { position: relative; }
.contact-core::after {
  content: "";
  position: absolute;
  /* 🔴 Les deux marges des côtés PROCHES du bord sont ÉGALES, et c'est ce qui
     se voit : le symbole doit paraître posé à distance constante du coin, pas
     poussé dans un angle. Elles se lisent directement comme des marges
     visuelles parce que le fichier est rogné au glyphe (1 à 2 px de vide, pas
     davantage) ; avec une image marginée, il faudrait retrancher son vide.
     Un premier essai donnait 37,8 px à droite pour 18,7 px en bas : l'écart
     de 19 px se voyait immédiatement. */
  right: clamp(26px, 2.2vw, 32px);
  bottom: clamp(26px, 2.2vw, 32px);
  width: clamp(50px, 4vw, 58px);
  aspect-ratio: 200 / 194;
  background: url("../images/logos/symbol-bronze.png") center / contain no-repeat;
  /* 0,33 place le filigrane à 1,42:1 contre l'ivoire, tout près des 1,53:1
     de `.hs-mark`, la référence maison. Mesuré, pas jugé à l'œil. */
  opacity: 0.33;
  pointer-events: none;
}

.contact-cta { margin-top: 40px; }
.contact-mail { margin-top: 14px; display: inline-flex; }
.contact-right {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 36px 40px;
  align-content: center;
}
.c-label {
  font-size: 9px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--bronze-deep);
  margin-bottom: 10px;
}
.c-value { font-family: "Cormorant Garamond", serif; font-weight: 500; font-size: 1.3rem; line-height: 1.4; }

.contact > .contact-map {
  max-width: 1320px;
  margin: clamp(40px, 6vh, 64px) auto 0;
}
.map-core { aspect-ratio: 21 / 9; }
.map-core iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  filter: grayscale(0.35) sepia(0.14) saturate(0.9);
  transition: filter 0.6s var(--ease-lux);
}
.contact-map:hover .map-core iframe { filter: none; }

/* ═══════════ FOOTER ═══════════ */

.footer {
  padding: 80px 24px 56px;
  text-align: center;
  background:
    radial-gradient(50% 70% at 50% 0%, rgba(168, 127, 76, 0.12), transparent 65%),
    var(--espresso);
  color: rgba(253, 250, 244, 0.6);
}
.f-logo-img {
  width: min(300px, 72vw);
  margin: 0 auto 44px;
}
.f-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  font-size: 0.78rem;
  color: rgba(253, 250, 244, 0.55);
  font-weight: 300;
}
.f-meta a { border-bottom: 1px solid rgba(253, 250, 244, 0.25); transition: border-color 0.4s; }
.f-meta a:hover { border-color: var(--bronze-soft); }
.f-credit {
  display: inline-block;
  margin-top: 34px;
  padding: 11px 22px;
  border-radius: 999px;
  border: 1px solid rgba(196, 166, 120, 0.28);
  background: rgba(196, 166, 120, 0.07);
  font-size: 0.78rem;
  font-weight: 400;
  letter-spacing: 0.05em;
  color: rgba(253, 250, 244, 0.68);
}
.f-credit a {
  color: var(--bronze-soft);
  font-weight: 500;
  border-bottom: 1px solid rgba(196, 166, 120, 0.45);
  transition: color 0.4s, border-color 0.4s;
}
.f-credit a:hover { color: var(--ivory); border-color: var(--ivory); }

/* ═══════════ ENGAGEMENTS ═══════════
   Bande de réassurance placée après le premier écran : elle répond, dans l'ordre,
   aux quatre freins les plus bloquants relevés en étude de marché (abîmer,
   douleur de la dépose, surprise à la caisse, hygiène). */

.engagements {
  padding: clamp(70px, 10vh, 120px) clamp(24px, 7vw, 110px);
  background:
    radial-gradient(60% 90% at 50% 0%, rgba(168, 127, 76, 0.16), transparent 68%),
    var(--espresso);
}
.eng-head {
  max-width: 900px;
  margin: 0 auto clamp(44px, 6vh, 66px);
  text-align: center;
}
/* .lead porte un max-width de 46ch : sans marges auto il se calait à gauche
   dans un bloc centré. */
.eng-head .lead { max-width: 60ch; margin-inline: auto; }
/* Gouttière resserrée depuis que les tuiles sont devenues des cartes : le
   rembourrage interne fait déjà la moitié du travail de séparation, et une
   gouttière large en plus mangeait la largeur de ligne du texte. */
.eng-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 26px clamp(16px, 1.7vw, 26px);
  max-width: 1320px;
  margin: 0 auto;
}
/* ── Les quatre cartes ──
   Elles étaient posées à plat sous un simple filet : quatre colonnes de texte
   sans objet à survoler. Chacune est maintenant une surface propre, en verre
   bronze très dilué sur l'espresso, avec un liseré intérieur clair en haut qui
   lui donne son épaisseur. */
.eng-item {
  position: relative;
  isolation: isolate;
  padding: 26px 24px 30px;
  border-radius: 22px;
  border: 1px solid rgba(196, 166, 120, 0.17);
  background:
    linear-gradient(168deg, rgba(253, 250, 244, 0.055), rgba(253, 250, 244, 0.016) 62%);
  box-shadow: inset 0 1px 0 rgba(253, 250, 244, 0.07);
  transition:
    transform 0.5s var(--ease-lux),
    border-color 0.5s var(--ease-lux);
}
/* Le halo qui s'allume : une seule source, ancrée en haut de la carte, comme
   si la lumière de la section descendait dessus. On anime son opacité et non
   un dégradé, qui repeindrait la carte à chaque image. */
.eng-item::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background:
    radial-gradient(125% 85% at 50% 0%, rgba(196, 166, 120, 0.2), transparent 62%);
  opacity: 0;
  transition: opacity 0.6s var(--ease-lux);
}
/* L'ombre portée est un calque séparé, pour la même raison : une box-shadow
   qui se transitionne se repeint, une opacité non. */
.eng-item::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -2;
  border-radius: inherit;
  box-shadow: 0 34px 60px -34px rgba(0, 0, 0, 0.75);
  opacity: 0;
  transition: opacity 0.5s var(--ease-lux);
}
@media (hover: hover) and (pointer: fine) {
  .eng-item:hover {
    transform: translateY(-6px);
    border-color: rgba(196, 166, 120, 0.44);
  }
  .eng-item:hover::before,
  .eng-item:hover::after { opacity: 1; }
}
/* ⚠️ Ne pas réintroduire de subgrid ici. Deux tentatives, deux échecs :
   - à trois lignes (pictogramme / titre / texte), une gouttière de grid
     s'installait entre le pictogramme et le titre ;
   - à deux lignes, les paragraphes s'alignaient bien d'une carte à l'autre,
     mais les cartes dont le titre tient sur une seule ligne héritaient du
     blanc d'une deuxième ligne entre leur titre et leur texte.
   Dans les deux cas, le blanc atterrissait à l'intérieur d'une carte, là où il
   casse la lecture. Depuis que chaque carte est un objet à part entière, aligner
   les paragraphes d'une carte à l'autre n'apporte plus rien : ce qui compte est
   le rythme intérieur, identique partout : bande, 16 px, titre, 18 px, texte.
   Les hauteurs de cartes, elles, restent égales par le `stretch` du grid. */

/* Pictogrammes : premier niveau de lecture. Le regard attrape un signe avant
   d'attraper un mot, ce qui évite d'aborder la section comme quatre pavés de
   texte. Tracé fin bronze, jamais de plein, pour rester dans la ligne du reste
   du site. */
/* La bande d'en-tête : pictogramme à gauche, filet, numéro à droite. Le filet
   relie physiquement les deux, ce qui en fait un seul objet et non deux signes
   posés l'un à côté de l'autre. La bande n'est séparée de son titre que de
   16 px : c'est ce qui fait que le regard, attiré par le signe, tombe
   immédiatement sur le mot. */
.eng-band {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 16px;
}
.eng-rule {
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, rgba(196, 166, 120, 0.32), rgba(196, 166, 120, 0.08));
  opacity: 0.72;
  transition: opacity 0.5s var(--ease-lux);
}
.eng-ico {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--bronze-soft);
  background: rgba(196, 166, 120, 0.09);
  border: 1px solid rgba(196, 166, 120, 0.24);
  transition:
    color 0.5s var(--ease-lux),
    background 0.5s var(--ease-lux),
    border-color 0.5s var(--ease-lux),
    box-shadow 0.5s var(--ease-lux);
}
.eng-ico svg {
  width: 19px;
  height: 19px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.25;
  stroke-linecap: round;
  stroke-linejoin: round;
}
@media (hover: hover) and (pointer: fine) {
  .eng-item:hover .eng-ico {
    color: var(--espresso);
    background: linear-gradient(145deg, var(--bronze-pale), var(--bronze));
    border-color: transparent;
    box-shadow: 0 0 0 5px rgba(196, 166, 120, 0.13);
  }
  .eng-item:hover .eng-n {
    color: var(--bronze-soft);
    opacity: 1;
  }
  .eng-item:hover .eng-rule { opacity: 1; }
}
/* Le numéro est le fil conducteur de la section : c'est lui qui dit qu'il y a
   quatre engagements et dans quel ordre les lire. Il est donc traité comme un
   chiffre de chapitre (Cormorant italique, deux fois la taille du texte
   courant) et non comme une légende. */
.eng-n {
  font-family: "Cormorant Garamond", serif;
  font-style: italic;
  font-weight: 500;
  font-size: 1.65rem;
  line-height: 1;
  letter-spacing: 0.01em;
  color: var(--bronze-soft);
  opacity: 0.78;
  transition: color 0.5s var(--ease-lux), opacity 0.5s var(--ease-lux);
}
/* ⚠️ La famille est explicite : ce titre est un <h3>, et la règle globale
   h1,h2,h3 le passerait en Cormorant. À 1 rem, le Cormorant est maigre et
   flottant : le titre de carte doit rester dans la sans du site. */
.eng-t {
  font-family: "Plus Jakarta Sans", sans-serif;
  font-weight: 600;
  font-size: 1.02rem;
  line-height: 1.4;
  letter-spacing: -0.005em;
  color: var(--ivory);
  margin-bottom: 18px;
}
.eng-d {
  font-size: 0.86rem;
  font-weight: 300;
  line-height: 1.74;
  color: rgba(253, 250, 244, 0.66);
}
/* Troisième niveau de lecture : dans chaque tuile, la preuve concrète est
   remontée en clair. On peut ne lire que ces quatre phrases et avoir compris
   la section. */
.eng-d strong {
  font-weight: 500;
  color: rgba(253, 250, 244, 0.95);
}


/* ═══════════ ENGAGEMENTS : LA PILE ═══════════
   Les huit engagements ne se lisent plus en damier de huit pavés, mais une
   carte à la fois. La section se déroule sur un rail haut ; la scène s'y
   accroche le temps qu'on le parcourt, et chaque carte se redresse, se lit,
   puis s'envole pour découvrir la suivante. La dernière ne part pas : elle
   tient l'écran pendant que le rail se termine.

   ⚠️ Ce balisage n'existe dans AUCUN fichier HTML. C'est js/main.js qui monte
   le rail (.eng-stack) et la scène (.eng-pin) autour de .eng-grid, et
   seulement si les conditions sont réunies : mouvement réduit non demandé,
   position:sticky comprise, deux cartes au moins. Sans script, sans sticky,
   ou si la visiteuse a demandé moins de mouvement, rien n'est monté et la
   grille reste la grille : c'est le repli, pas une avarie.

   C'est aussi la seule raison pour laquelle tout ce bloc est préfixé par
   .eng-stack : hors de la pile, il ne doit jamais s'appliquer. Les dix pages
   qui portent .eng-grid héritent donc de la pile sans qu'on touche à leur
   balisage, et la version anglaise suit la française par construction. */

/* Quand la pile est montée, la section n'a plus besoin de son rembourrage bas :
   la scène fait 100svh et la dernière carte est centrée dedans, donc il reste
   déjà une demi-scène de vide sous elle. Les deux cumulés ouvraient 450 px de
   blanc entre la dernière carte et la bande des maisons partenaires (mesuré
   sur un écran de 800 px). La classe est posée par js/main.js, jamais dans le
   balisage : sans pile, pas de classe, et la section garde son rembourrage. */
.engagements.has-stack {
  padding-bottom: clamp(12px, 2vh, 28px);
  /* ⚠️ IL Y AVAIT ICI UN `overflow-x: clip`, ET IL NE FAUT PAS LE REMETTRE.
     C'est lui qui faisait trembler la pile sur iPhone. Un contexte
     d'empilement (celui que pose la couture de la section) ET une coupe sur le
     même ancêtre sortent ensemble le `position: sticky` de la scène du fil
     rapide de Safari : la scène est alors repositionnée sur le fil principal,
     avec une image ou deux de retard, pendant que le défilement, lui, suit le
     doigt. Mesuré image par image sur un enregistrement : la pilule fixe ne
     bougeait pas d'un centième de pixel quand le filet d'avancement, statique
     dans la scène, sautait de 1,9 px par image avec 76 changements de sens sur
     131. Retirer l'un OU l'autre suffit, vérifié sur l'appareil.
     Il est retiré sans rien mettre à la place parce qu'il ne servait à rien :
     `body` porte déjà `overflow-x: hidden`, qui interdit tout défilement
     horizontal. Contrôlé de 360 à 440 px de large, la carte la plus ouverte de
     l'éventail laisse encore 8 px de marge au bord de l'écran. */
}
/* La scène pose déjà une demi-hauteur d'écran au-dessus de la première carte :
   l'écart d'origine sous l'en-tête s'y ajoutait pour rien. */
.engagements.has-stack .eng-head { margin-bottom: clamp(20px, 2.5vh, 30px); }

.eng-stack {
  /* Course de défilement consommée par carte. C'est le seul bouton de réglage
     de la longueur de la section, et celui de sa SENSIBILITÉ : plus le pas est
     long, plus il faut de doigt pour faire passer une carte, et plus on garde
     la main sur le rythme. Le script en donne 38 % à la bascule et 62 % à la
     lecture. --eng-t (le nombre de pas) est écrit par le script.
     ⚠️ Monté de 42 à 66vh le 24 août 2026 : à 42, la bascule ne consommait que
     16vh de défilement, soit un geste de pouce, et les cartes défilaient plus
     vite qu'on ne les lisait. À 66, la bascule en demande 25 et la lecture 41.
     Le prix est la longueur de la section, qui est le prix normal de ce genre
     de scène. */
  --eng-step: 66vh;
  --eng-t: 1;
  /* Angle entre deux cartes de l'éventail, en degrés, lu par le script. Le
     pivot étant posé sous la carte, un degré ne fait pas tourner la carte sur
     place : il la promène sur un arc. C'est ce qui fait le colimaçon, et c'est
     pour ça que la valeur vit ici, dans la feuille de style, et pas dans le
     script : c'est un réglage de dessin. */
  --eng-fan: 5;
  /* Recentrer le PAQUET décentrait la carte, et c'est la carte qu'on regarde.
     L'idée était de compenser le balayage de l'éventail en décalant la colonne
     vers la gauche. À l'usage, on ne voyait qu'une carte hors d'axe et un filet
     d'avancement hors d'axe avec elle. La carte du dessus est pleine, les
     autres ne sont que des tranches en biais : c'est elle qui tient l'axe, et
     l'éventail s'ouvre à côté. Gardé à 0 plutôt que supprimé, pour que le
     repentir reste lisible. */
  --eng-lean: 0px;
  /* Hauteur de la scène, et son accroche. Sur écran large la scène occupe tout
     l'écran ; sur téléphone elle se resserre sur la bande utile, entre la
     pilule de navigation et la barre d'actions (voir plus bas). Le rail se
     calcule dessus pour que la course par carte ne bouge pas : sans ça, une
     scène plus courte allongerait mécaniquement le défilement. */
  --eng-pin-top: 0px;
  --eng-pin-h: 88vh;
  /* L'éventail descend autant qu'il s'ouvre : à cinq degrés par rang, 76 px
     sous la carte du dessus (mesuré). La scène centre la colonne, pas le
     paquet ; sans cette compensation, le paquet pend sous le milieu de l'écran
     et vient buter sur le filet d'avancement. */
  --eng-drop: 68px;
  position: relative;
  height: calc(var(--eng-pin-h) + var(--eng-t) * var(--eng-step));
}
@supports (height: 100svh) {
  .eng-stack { --eng-pin-h: 88svh; }
}

/* La scène. Le rembourrage haut dégage la bande d'annonce et la pilule de
   navigation, toutes deux fixes : sans lui, la carte passe sous le verre clair
   de la barre sur les écrans courts. */
.eng-stack .eng-pin {
  position: sticky;
  top: var(--eng-pin-top);
  height: var(--eng-pin-h);
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* La scène déborde du rembourrage de la section, des deux côtés. C'est
     l'éventail qui l'exige : sur un téléphone, la carte fait déjà toute la
     largeur utile, et les cartes du dessous en balaient encore quarante de
     plus. Sans ce plein cadre, elles seraient rognées par le bord de la
     section au lieu de s'ouvrir. */
  margin-inline: calc(-1 * clamp(24px, 7vw, 110px));
  padding-right: calc(2 * var(--eng-lean));
  /* Ces deux valeurs ne sont pas des marges de confort : le groupe est centré
     dans la scène, donc elles ne se voient que sur les écrans courts, où elles
     sont un plancher. En haut, le dégagement de la pilule de navigation, dont
     le bas tombe à 116 px du bord (mesuré). En bas, le strict nécessaire pour
     que le filet d'avancement ne touche pas le bord. */
  /* 104 px et non 56 : ce rembourrage n'est pas une marge, c'est le garde-fou
     de la barre de navigation. La carte est centrée dans la scène, donc sa
     position dépend de sa hauteur ; sur les pages d'atterrissage, dont les
     engagements sont plus longs, elle remontait jusqu'à 15 px du bas de la
     pilule (mesuré) et serait passée dessous sur un écran plus court. Le
     rembourrage haut est le plancher qui l'en empêche : le bas de la pilule
     tombe à 124 px, la carte ne peut plus monter au-dessus de 136. */
  padding-top: calc(var(--annbar-h) + 104px);
  padding-bottom: clamp(14px, 2.4vh, 26px);
  /* La carte qui s'envole traversait la pilule de navigation, qui est fixe et
     claire : on la voyait passer derrière le verre, et ça se lisait comme un
     défaut. Le masque la dissout avant qu'elle y arrive.

     ⚠️ Un MASQUE, et pas une coupe doublée d'un voile peint. La différence
     n'est pas cosmétique : un masque efface le contenu et laisse voir le fond
     tel qu'il est, tandis qu'un voile PEINT par-dessus. Essayé en voile le
     24 août, pour économiser une surface intermédiaire : il repeignait
     l'espresso à plat sur les vingt premiers pixels de la scène, donc
     par-dessus le halo bronze de la section, et ouvrait une bande sombre en
     haut du cadre avec un bord net en bas. C'est la « barre » qu'on voyait.
     Mesuré : 30 de luminance dans la bande contre 37 tout autour.
     Le voile avait été posé sur l'hypothèse que le masque coûtait cher au
     défilement. L'hypothèse était fausse : le tremblement venait du contexte
     d'empilement et de la coupe réunis sur la section, ce qui est corrigé
     ailleurs. Le masque revient donc, et avec lui le rendu d'origine.
     Déclaré deux fois, Safari ne comprend le masque que préfixé. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 128px);
  mask-image: linear-gradient(to bottom, transparent 0, #000 128px);
  /* Le masque se répète par défaut : au-dessus de la scène, la carte qui part
     retrouverait une bande opaque au lieu de disparaître. */
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* La grille cesse d'être une grille de colonnes pour devenir une grille d'une
   seule cellule : toutes les cartes occupent 1 / 1, donc elles se superposent
   et la pile prend d'elle-même la hauteur de la carte la plus longue. Aucune
   hauteur fixe à maintenir, aucun texte qui déborde à 320 px de large. */
.eng-stack .eng-grid {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: auto;
  gap: 0;
  /* La largeur est portée par le conteneur, pas par la colonne. Une colonne
     étroite centrée dans un conteneur pleine largeur revient au même à l'œil,
     mais pas au script : il mesure la position du curseur dans cette boîte
     pour en tirer l'inclinaison, et il lui faut la boîte de la carte, pas
     celle de la scène. Mesuré, l'écart était de 1238 px contre 440. */
  /* 340 px, et c'est la colonne qui règle tout le reste. Elle donne le format
     portrait sans avoir à grossir le texte (elle l'oblige à descendre en
     lignes plutôt qu'à s'étaler) et elle remplit la carte par la même
     occasion : à 370 px, le texte laissait 113 px de marge en haut comme en
     bas, mesuré, soit une carte à moitié vide. À 330 la ligne fait 33 signes,
     ce qui reste dans la mesure de lecture. C'est aussi la largeur du
     composant d'origine, à vingt pixels près. */
  width: min(330px, 84vw);
  max-width: none;
  margin: 0 auto var(--eng-drop);
}

/* ── La carte ──
   Même verre bronze que la tuile d'origine, mais assumé : elle est seule à
   l'écran, donc elle a droit à une opacité de fond (les cartes se recouvrent,
   un fond translucide laisserait lire celle du dessous), à un flou d'arrière-
   plan et à une ombre portée large qui la décolle de l'espresso.

   Le pivot est posé sous la carte, pas en son centre : une carte qu'on
   soulève d'un paquet tourne autour d'un point bas, et c'est ce qui donne
   l'éventail aux cartes encore en attente. */
.eng-stack .eng-item {
  grid-area: 1 / 1;
  /* Le contenu n'est ni centré ni calé en haut : il est réparti. La bande
     (pictogramme, filet, numéro) tient le haut de la carte, le titre et son
     texte tiennent le bas, et tout le mou tombe entre les deux. C'est ce qui
     fait qu'une carte portrait paraît pleine : sa structure touche ses deux
     bords. Centré, le bloc flottait au milieu de 240 px de vide partagé en
     deux, et la carte se lisait comme à moitié remplie.

     Toutes les cartes prennent la hauteur de la plus longue (c'est le propre
     d'une cellule de grille partagée) et les huit engagements ne font pas la
     même longueur : le mou varie donc d'une carte à l'autre, mais il varie
     dans le blanc du milieu, là où personne ne le compte, et non sous la
     dernière ligne, là où il se lirait comme une carte inachevée. La bande ne
     bouge plus du tout ; le titre, lui, bouge, mais il bouge
     pendant que la précédente s'envole : le saut ne se voit pas.
     La tuile d'origine est un bloc, pas une boîte flexible : sans les trois
     lignes qui suivent, rien de tout cela ne s'applique. */
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Format portrait, et c'est le point : une carte plus large que haute se lit
     comme une bande de texte, pas comme une carte. À 400 px de large, le
     plancher la porte à 520, soit un rapport de 0,77, celui d'une carte à
     jouer. Il gouverne partout, y compris sur les accueils où le texte le plus
     long ne remplit que 360 px : le reste devient de la marge, répartie en
     haut et en bas par le centrage. */
  /* Le plancher se cale sur la hauteur naturelle du contenu, pas sur une part
     d'écran. Mesuré : 382 px au large, 336 px sur téléphone pour l'engagement
     le plus long. Le plancher ajoute une trentaine de pixels, soit quinze de
     marge en haut et autant en bas, par-dessus le rembourrage. Il n'est plus
     là pour remplir l'écran, seulement pour que les cartes courtes ne se
     tassent pas sous les longues.

     ⚠️ Ne PAS s'en servir pour resserrer le vide autour de la carte. C'était
     son rôle précédent (63svh) et ça se payait dedans : 139 px de vide entre
     le bord de la carte et son texte, mesuré. Le vide autour se règle sur la
     hauteur de la scène (--eng-pin-h), pas ici. */
  min-height: clamp(360px, 50vh, 430px);
  min-height: clamp(360px, 50svh, 430px);
  /* 34 px et non 42 : à 440 px de large, un rembourrage de 42 px laissait
     84 px de marbre latéral pour 356 px de texte, soit un cinquième de la
     carte en vide. Le bas garde 2 px de plus que le haut, comme partout sur
     le site : une marge basse strictement égale se lit toujours trop courte. */
  padding: clamp(24px, 2.5vw, 32px) clamp(24px, 2.6vw, 34px) clamp(26px, 2.7vw, 34px);
  border-radius: 26px;
  border: 1px solid rgba(196, 166, 120, 0.26);
  background:
    linear-gradient(168deg, rgba(253, 250, 244, 0.09), rgba(253, 250, 244, 0.03) 62%),
    #1c150f;
  /* ⚠️ PAS DE LÈVRE CLAIRE EN HAUT, et il ne faut pas la remettre.
     La carte portait `inset 0 1px 0 rgba(253,250,244,0.1)`, un second trait
     ivoire posé juste sous le liseré bronze, censé lui donner de l'épaisseur.
     Sur une carte plein écran, c'est l'inverse qui se produit : mesuré au
     pixel physique, le bord HAUT faisait six pixels de haut (trois de bronze
     à 195, puis trois d'ivoire à 192) quand le bord BAS en fait trois, d'un
     seul bronze franc à 221. Deux traits accolés de deux teintes différentes,
     ça ne se lit pas comme une épaisseur, ça se lit comme un trait flou.
     Le bord haut est désormais le même objet que le bord bas : un liseré,
     un seul, un pixel.
     L'ombre portée, elle, n'est pas ici : le calque `::after` la porte, et le
     script ne l'allume que sur la carte du dessus et sur celle qui s'envole.
     Sur les rangs du dessous, quatre ombres décalées de quelques pixels ne se
     fondent pas, elles s'additionnent en un rectangle sombre autour du
     paquet. */
  /* ⚠️ `none`, pas « rien » : retirer la déclaration ne suffit pas, la carte
     retombe alors sur la lèvre de la tuile d'origine (0,07) et le bord haut
     reste double. Mesuré : 173 au lieu de 192, mais toujours six pixels
     physiques de clair. Il faut l'éteindre explicitement. */
  box-shadow: none;
  /* ── Un liseré OPAQUE, et c'est ce qui le rend net ──
     Il était à 26 % d'opacité, donc il se mélangeait à ce qu'il couvrait. Or
     ce qu'il couvre n'est pas le même en haut et en bas : le fond de la carte
     est un dégradé, et `background-origin` vaut `padding-box` par défaut, donc
     la tuile se cale à l'intérieur du liseré puis se RÉPÈTE dans la bande d'un
     pixel qu'occupe la bordure. Le bandeau du haut reçoit la fin du dégradé
     (sombre), celui du bas son début (clair). Mesuré au pixel physique : 195
     en haut, 221 en bas, 197 à gauche, 206 à droite : pour une seule et même
     couleur déclarée. Sur fond plat, les quatre côtés donnent 177, ce qui le
     prouve.
     La couleur posée ici est celle que le bandeau du BAS rendait, relevée à
     l'écran : rgb(87,75,59). Opaque, elle ne dépend plus de ce qu'il y a
     dessous, et les quatre côtés tombent à 226-229, soit trois unités d'écart
     sur 765. Le liseré du bas court désormais tout autour de la carte.
     ⚠️ Ne pas « simplifier » en passant `background-origin: border-box` : ça
     ne fait qu'inverser le défaut, le haut monte et le bas tombe à 193. */
  border-color: rgb(87, 75, 59);
  transform-origin: 50% 118%;
  will-change: transform, opacity;
  /* Le script réécrit transform et opacity à chaque image : une transition
     ici ferait traîner la carte d'un cran derrière la molette. */
  transition: border-color 0.5s var(--ease-lux);
}

/* Le halo de survol de la tuile d'origine reste, mais il ne s'allume que sur
   la carte du dessus : le script coupe les événements de pointeur sur les
   autres, sinon on éclairerait une carte qu'on ne voit pas. */


/* ── La carte hors champ ──
   Sous la troisième profondeur, plus rien n'est visible : le script pose cette
   classe et la retire, uniquement quand l'état change. Trois ou quatre cartes
   sur huit y sont en permanence.
   `visibility` ne change rien à ce qu'on voit, elles sont déjà à opacité nulle
   : elle change ce que le navigateur transporte : une carte cachée sort du
   dessin, et `will-change: auto` lui reprend la mémoire de calque que la règle
   générale lui réservait. C'est la moitié des calques de la scène rendue au
   téléphone. */
.eng-stack .eng-item.est-hors-champ {
  visibility: hidden;
  will-change: auto;
}

/* ── L'ombre portée de la pile ──
   Un calque à part, et non un `box-shadow` posé et retiré par une classe : une
   ombre qu'on allume et qu'on éteint redécoupe la carte à chaque changement,
   alors qu'une opacité ne coûte rien au compositeur. Les deux chemins s'en
   servent : le repli écrit `--ombre` sur la carte, la frise anime directement
   l'opacité du calque.

   ⚠️ `inset: -1px`, et pas 0. Un enfant en `inset: 0` se cale sur la boîte de
   REMBOURRAGE, donc à l'intérieur du liseré d'un pixel de la carte. Une ombre
   portée se peint hors de la boîte de son élément : depuis l'intérieur du
   liseré, elle se peignait par-dessus lui, et comme elle est décalée de 44 px
   vers le bas, c'est le liseré bas qui disparaissait. Mesuré : le bronze du
   bord passait de (87,74,58) à (46,39,32). */
.eng-stack .eng-item::after {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: -2;
  border-radius: inherit;
  box-shadow: 0 44px 76px -40px rgba(0, 0, 0, 0.9);
  opacity: var(--ombre, 0);
  /* ⚠️ La tuile d'origine transitionne cette opacité sur une demi-seconde,
     pour son survol. Ici l'opacité est écrite à chaque image du défilement :
     la transition la ferait traîner une demi-seconde derrière la carte, et
     l'ombre resterait allumée sous une carte déjà partie. Sur le chemin de la
     frise, l'animation l'emporte de toute façon ; c'est le repli qu'il faut
     protéger. */
  transition: none;
}

/* Le contenu s'est d'abord réparti sur toute la hauteur, bande en haut et texte
   en bas. C'était juste en théorie et faux à l'œil : tout le mou de la carte
   tombait d'un seul côté, entre la bande et le titre, et la carte se lisait
   comme un en-tête suivi d'un trou. Le mou est rendu au centrage, qui le
   partage en deux marges égales, et la bande garde un écart fixe avec le
   titre quelle que soit la longueur du texte. */
.eng-stack .eng-block { display: block; }

/* ── La lueur qui suit le curseur ──
   Le pendant exact de la lueur qui suivait le curseur sur l'ancien cadre
   « 02 · Le détail » : même dégradé, même `soft-light`, même extinction. Le mode de fusion n'est
   pas décoratif. Un calque blanc en `normal` délaverait le verre bronze de la
   carte et éteindrait le contraste du texte ; en `soft-light` il éclaire la
   surface sans toucher à ce qui est posé dessus. L'`isolation: isolate` de la
   tuile d'origine cantonne d'ailleurs la fusion à la carte, jamais à la pile.

   Le calque est monté par js/main.js, en même temps que le rail, et seulement
   là où il y a un vrai survol : au doigt, la lueur resterait allumée. */
.eng-glare {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  mix-blend-mode: soft-light;
  transition: opacity 0.5s var(--ease-lux);
}

/* Le composant d'origine posait un `backdrop-filter` sur ses cartes, et un
   fond translucide avec. Les deux ont été essayés ici, puis retirés, pour la
   raison que DESIGN.md avait déjà écrite à propos du premier écran : le flou
   ne tient pas la lisibilité, seul le voile la tient.

   Le fond est donc plein, et ce n'est pas un renoncement. Sur une pile, la
   carte du dessus doit CACHER celle du dessous, sinon on lit deux textes l'un
   dans l'autre. Même à 3,5 % de transparence, le titre de la carte suivante
   restait lisible en filigrane au travers (constaté à l'écran) : c'est le
   contraste du texte qui passe, ivoire franc sur presque noir, pas la couleur
   du fond. L'épaisseur de verre vient d'ailleurs, du liseré clair en haut et
   du dégradé d'ivoire dilué posé par-dessus.

   Le flou, lui, coûtait un calque recomposé à chaque image d'un défilement,
   sous une lueur en mode de fusion, sur des cartes qui portent déjà une
   perspective et une ombre de 76 px de flou. */

/* ── Le rythme intérieur, à l'échelle d'une carte plein écran ──
   Les tailles de la tuile en damier étaient calibrées pour une colonne de
   300 px posée à côté de trois autres. Ici la carte fait 440 px et porte le
   regard à elle seule : le pictogramme, le numéro et le titre montent d'un
   cran, et le texte avec. */
/* Le rythme intérieur s'écarte à mesure que la carte grandit. Ce n'est pas de
   la décoration : la carte occupe maintenant 63 % de la scène pour resserrer le
   vide autour d'elle, et le texte, lui, n'a pas grandi. Sans cet écartement,
   les trois blocs se serraient au milieu d'un grand cadre vide. Écartés, ils
   occupent la carte. */
.eng-stack .eng-band { gap: 16px; margin-bottom: clamp(24px, 3.6vh, 42px); }
.eng-stack .eng-ico { width: 56px; height: 56px; }
.eng-stack .eng-ico svg { width: 26px; height: 26px; stroke-width: 1.15; }
.eng-stack .eng-n { font-size: clamp(2.05rem, 3.6vw, 2.75rem); opacity: 0.88; }
/* Le titre ne fait plus qu'un ou deux mots : il cesse d'être une phrase à lire
   pour devenir l'étiquette de la carte. C'est lui qui doit être attrapé au vol
   par quelqu'un qui fait défiler sans lire, donc il monte pendant que le texte
   courant redescend. Le rapport entre les deux passe de 1,4 à 1,8. */
.eng-stack .eng-t {
  font-size: clamp(1.32rem, 2.1vw, 1.72rem);
  line-height: 1.22;
  letter-spacing: -0.015em;
  margin-bottom: clamp(14px, 2.1vh, 22px);
}
/* Le texte courant est le second niveau de lecture, pas le premier : on ne
   l'atteint qu'une fois le titre attrapé. Il tient donc la mesure de lecture
   (42 signes par ligne à 400 px de colonne) sans chercher à remplir la carte,
   ce que fait maintenant le format portrait. */
.eng-stack .eng-d {
  font-size: clamp(0.9rem, 1.1vw, 0.98rem);
  line-height: 1.88;
  color: rgba(253, 250, 244, 0.7);
}

/* ── L'invite au défilement ──
   Un chevron bronze posé à cheval sur le bord bas de la carte du dessus. Il
   répond à un blocage observé : une visiteuse a balayé vers la droite, la
   pile n'avance qu'au défilement vertical, et rien ne le disait. Le filet
   d'avancement dit où l'on en est ; celui-ci dit comment avancer.

   Trois décisions à ne pas défaire :
   1. **Il est pièce de la grille, en 1 / 1**, comme les cartes, et calé sur le
      bord bas par `align-self: end` puis remonté à cheval dessus. La grille
      est la seule boîte stable de la scène : les cartes portent un `transform`
      que le script réécrit à chaque image. Se caler sur une carte, c'est se
      caler sur une cible mouvante.
   2. **À cheval sur le bord, jamais dedans.** Posé à l'intérieur, il tombe sur
      la dernière ligne de l'engagement le plus long. Sur le bord, il ne touche
      jamais le texte et il se lit comme une poignée attachée à la carte.
   3. **Il ne clignote pas.** Un clignotement est un signal d'alarme ; ici il
      faut une respiration. La pastille descend de six pixels et remonte en
      2,6 s, et le chevron s'éclaircit à la descente. C'est le geste qu'on
      demande, joué au ralenti.

   Sur ordinateur c'est l'éventail qui se monte, jamais la pile : ce chevron
   n'apparaît de fait que sur les écrans étroits. La requête de média le
   confine quand même, pour que le repli ne dépende pas d'un autre module. */
.eng-nudge {
  grid-area: 1 / 1;
  align-self: end;
  justify-self: center;
  /* La moitié de la pastille passe sous le bord de la carte. */
  transform: translateY(50%);
  /* Au-dessus des huit cartes : le script leur donne des z-index de 1 à 8. */
  z-index: 20;
  pointer-events: none;
  display: none;
}
.eng-nudge-orb {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  /* Même grammaire que le pictogramme de la carte, mais PLEIN, et c'est le
     point. Essayé d'abord en verre bronze dilué comme .eng-ico : posé à cheval
     sur le bord, la pastille laissait voir les tranches de l'éventail au
     travers et se lisait comme une salissure, pas comme un objet. Le fond de
     la carte lui-même (#1c150f) la redécoupe nettement sur les deux plans.
     Le liseré monte à 45 % pour la même raison : elle doit se détacher du
     paquet, pas s'y fondre. */
  background: #1c150f;
  border: 1px solid rgba(196, 166, 120, 0.45);
  /* Lèvre haute pour l'épaisseur, ombre courte pour l'asseoir sur le bord.
     Pas de halo en anneau : il repeignait un disque sombre par-dessus les
     tranches de l'éventail, ce qui creusait un trou dans le paquet. */
  box-shadow:
    inset 0 1px 0 rgba(253, 250, 244, 0.14),
    0 8px 18px -8px rgba(0, 0, 0, 0.85);
  animation: eng-nudge-bob 2.6s var(--ease-lux) infinite;
}
.eng-nudge-orb svg {
  width: 21px;
  height: 21px;
  fill: none;
  stroke: var(--bronze-soft);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  animation: eng-nudge-glow 2.6s var(--ease-lux) infinite;
}
/* La hampe s'efface derrière la pointe : c'est la pointe qui porte le sens, la
   hampe ne fait que lui donner sa direction. En dessous de 0,55 la flèche se
   lit comme un chevron seul et perd son axe. */
.eng-nudge-orb svg path:first-child { opacity: 0.62; }

@keyframes eng-nudge-bob {
  0%, 62%, 100% { transform: translateY(0); }
  31%           { transform: translateY(6px); }
}
@keyframes eng-nudge-glow {
  0%, 62%, 100% { opacity: 0.72; }
  31%           { opacity: 1; }
}

/* La pile ne se monte que sous 861 px, mais on ne fait pas dépendre l'affichage
   d'un autre module : la requête est écrite ici, en clair. */
@media (max-width: 860px) {
  .eng-stack .eng-nudge { display: block; }
}

/* Mouvement réduit : le chevron reste, la respiration part. C'est l'information
   qui compte, pas le geste, et une visiteuse qui demande moins de mouvement a
   davantage besoin qu'on lui dise où aller, pas moins. */
@media (prefers-reduced-motion: reduce) {
  .eng-nudge-orb,
  .eng-nudge-orb svg { animation: none; }
}

/* ── La scène avancée par le compositeur ──
   Quand le navigateur sait lire une frise de défilement, la pile n'est plus
   calculée : elle est DÉCRITE, et c'est le compositeur qui l'avance, sur le
   même fil que le défilement lui-même. C'est la seule façon d'en finir avec le
   tremblement des cartes sur téléphone : là-bas le défilement ne vit pas sur le
   fil principal, et un script qui lit `scrollY` lit toujours une position en
   retard, pendant que la scène collante, elle, suit le doigt exactement.

   Les images clés sont écrites par js/main.js au montage, une fois : elles
   viennent des mêmes fonctions que le repli, donc les deux chemins dessinent
   la même scène par construction.

   ⚠️ La plage est écrite en unités CSS, jamais en pixels de page. Une plage en
   pixels absolus dépend de la hauteur de tout ce qui précède la section, donc
   des polices et des images : elle est fausse dès qu'elle vieillit, et c'est
   exactement le défaut qu'on vient de corriger dans le repli. Ici :
     début = hauteur de fenêtre − accroche de la scène
     fin   = course du rail + hauteur de fenêtre − accroche
   soit 0 % quand le rail s'accroche et 100 % quand il se décroche.
   `dvh` et non `svh` : c'est la hauteur RÉELLE de la fenêtre qui définit la
   frise, et sur téléphone elle change quand la barre d'URL se replie. */
.eng-stack.jn-frise {
  view-timeline-name: --jn-rail;
  view-timeline-axis: block;
}
.jn-frise .eng-item,
.jn-frise .eng-item::after,
.jn-frise .eng-pace-fill,
.jn-frise .eng-nudge {
  animation-timeline: --jn-rail;
  animation-range: cover calc(100dvh - var(--eng-pin-top))
                   cover calc(var(--eng-t) * var(--eng-step) + 100dvh - var(--eng-pin-top));
  /* `auto` : sur une frise de défilement, c'est la frise qui donne la durée,
     de 0 à 100 % de sa plage. Une durée en millisecondes serait une valeur
     valide mais d'un autre monde, et les navigateurs ne s'accordent pas sur
     ce qu'ils en font. */
  animation-duration: auto;
  animation-timing-function: linear;
  animation-fill-mode: both;
}
/* Le survol ne s'arme pas sur ce chemin (voir js/main.js). Rien à neutraliser
   en plus : une animation l'emporte sur une déclaration ordinaire, donc le
   `transform` de survol de la tuile ne peut de toute façon pas passer devant
   celui de la frise. */

/* ── Le fil d'avancement ──
   Une pile qui retient l'écran doit dire combien de temps elle le retient,
   sinon on croit la page bloquée. Le filet reprend exactement la grammaire du
   .eng-rule de la carte : un cheveu bronze, rien de plus. Purement décoratif,
   donc aria-hidden côté script, et sans un mot à traduire. */
.eng-stack .eng-pace {
  width: min(330px, 84vw);
  height: 1px;
  /* Le filet est revenu dans le flux, sous la pile. Il en était sorti quand la
     carte remplissait l'écran : calé en bas de scène, il restait à sa place
     pendant que l'éventail lui passait dessus. Depuis que la carte se cale sur
     son contenu, la scène est plus haute que son contenu et un filet collé au
     bord se retrouvait orphelin, à 175 px sous la pile. Dans le flux, il suit
     la pile et le tout se lit comme un seul bloc. L'éventail ne le rattrape
     pas : --eng-drop réserve déjà sa retombée juste au-dessus. */
  margin: clamp(30px, 4vh, 44px) auto 0;
  background: rgba(196, 166, 120, 0.16);
  overflow: hidden;
}
.eng-stack .eng-pace-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, rgba(196, 166, 120, 0.4), var(--bronze-soft));
  transform: scaleX(0);
  transform-origin: 0 50%;
}

/* ── Téléphones ──
   La barre d'actions fixe mange 77 px en bas : sans ce dégagement, le fil
   d'avancement passe dessous. La pilule de navigation est plus basse qu'au
   large, d'où un rembourrage haut resserré. */
@media (max-width: 860px) {
  /* L'éventail se resserre : la carte occupe déjà toute la largeur utile,
     donc le balayage latéral n'a plus que le débord de la scène pour lui. */
  /* L'éventail se resserre et la dérive avec : la carte occupe déjà toute la
     largeur utile, il ne reste que le débord de la scène pour le balayage. */
  /* ⚠️ --eng-drop doit valoir la retombée RÉELLE de l'éventail, sinon le filet
     d'avancement se fait rattraper. Elle se calcule, elle ne s'estime pas : la
     carte du troisième rang est décalée de 4,8 % vers le bas, réduite de 6 %,
     et tournée de 7,5° autour d'un pivot posé 18 % sous elle, si bien que ce
     sont ses angles bas qui descendent le plus. Pour une carte de 471 px, cela
     fait 47 px. À 30, il ne restait que 8 px entre l'éventail et le filet, et
     ils se touchaient à l'œil. Formule vérifiée au pixel près contre les boîtes
     peintes (67 px calculés, 67 mesurés sur écran large). */
  /* ⚠️ L'angle est plafonné par la LARGEUR, pas par le goût. Le pivot est posé
     18 % sous la carte : le bras de levier fait 556 px pour une carte de 471,
     et un degré promène donc le coin haut d'une vingtaine de pixels. Avec une
     carte qui occupe déjà 84 % de l'écran, l'éventail sortait à 402 px sur un
     écran de 375 (mesuré) et se faisait trancher net. Emprise calculée par
     degré : 0,8° → 357 px, 1° → 363, 1,2° → 368, 1,5° → 376, 2° → 389. À 1°,
     il reste douze pixels de marge et plus rien ne coupe. --eng-drop suit :
     la retombée tombe à 35 px. */
  .eng-stack { --eng-step: 60vh; --eng-fan: 1; --eng-lean: 0px; --eng-drop: 36px; }
  /* ⚠️ Le vrai levier sur le vide autour de la carte, et il n'est pas dans les
     rembourrages. La carte est centrée dans la scène : la somme des deux vides
     vaut donc (hauteur de scène − hauteur du paquet), et aucun rembourrage ne
     la change, il ne fait que la répartir. Mesuré à 375 × 812 : 164 px sous
     l'en-tête avant la première carte, 190 px sous la dernière avant la bande
     des maisons partenaires.

     Ce qui la change, c'est de raccourcir la scène. Elle cesse de faire tout
     l'écran pour n'occuper que la bande utile, entre le bas de la pilule de
     navigation et le haut de la barre d'actions, et elle s'y accroche par
     `top`. C'est ce `top` qui fait tout le travail : il ne s'applique QUE
     lorsque la scène est accrochée, donc il place la carte sous la barre
     pendant la lecture sans compter dans les écarts d'entrée et de sortie,
     là où un rembourrage haut, lui, aurait compté. Résultat mesuré : 75 px à
     l'entrée, 101 px à la sortie. */
  .eng-stack {
    --eng-pin-top: calc(var(--annbar-h) + 90px);
    /* La scène occupe la bande utile : sous la pilule de navigation, au-dessus
       de la barre d'actions. Elle est exprimée en soustraction plutôt qu'en
       part d'écran pour que les deux barres, qui ne changent pas de taille
       avec la hauteur, soient toujours dégagées. */
    --eng-pin-h: calc(100vh - var(--annbar-h) - 90px - 84px);
  }
  @supports (height: 100svh) {
    .eng-stack { --eng-pin-h: calc(100svh - var(--annbar-h) - 90px - 84px); }
  }
  .eng-stack .eng-pin {
    padding: 0;
    /* La scène commence sous la pilule de navigation : la carte qui part est
       coupée derrière elle, plus besoin des 128 px de fondu. Vingt suffisent à
       adoucir la coupe. */
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 20px);
    mask-image: linear-gradient(to bottom, transparent 0, #000 20px);
  }
  .eng-stack .eng-pace { margin-top: clamp(26px, 3.4vh, 36px); }
  /* Le plancher de hauteur est là pour donner à la carte une proportion de
     carte. Sur un téléphone, la colonne est étroite donc le texte est haut :
     le plancher n'a presque plus rien à rattraper, et le garder à 46 svh
     ouvrait 120 px de vide sous le dernier mot. */
  .eng-stack .eng-item {
    border-radius: 22px;
    /* ⚠️ Sur téléphone, la carte NE se cale PAS sur son contenu, contrairement
       à l'écran large : elle garde une part d'écran. Essayé dans l'autre sens
       le 23/08, revenu aussitôt : à 357 px la carte était juste, serrée et sans
       air, et l'aération vaut ici plus que l'économie de vide. Le plancher
       reste donc une fraction de la hauteur d'écran, et c'est --eng-pin-h qui
       tient les écarts d'entrée et de sortie. */
    min-height: clamp(340px, 58vh, 480px);
    min-height: clamp(340px, 58svh, 480px);
  }
  .eng-stack .eng-ico { width: 50px; height: 50px; }
  .eng-stack .eng-ico svg { width: 23px; height: 23px; }
}

/* ── Téléphones courts (moins de 700 px de haut) ──
   La carte garde une part de la hauteur d'écran, mais les deux barres, elles,
   ne rétrécissent pas : à 360 × 640 il ne reste que 447 px de bande utile pour
   un bloc qui en fait exactement 447, et le filet passait 3 px derrière la
   barre d'actions. La part est donc réduite ici, et seulement ici : sur un
   téléphone de taille courante la carte ne bouge pas d'un pixel. */
@media (max-width: 860px) and (max-height: 700px) {
  .eng-stack .eng-item {
    min-height: clamp(300px, 55vh, 380px);
    min-height: clamp(300px, 55svh, 380px);
  }
  .eng-stack .eng-pace { margin-top: 20px; }
}

/* Téléphone tenu à l'horizontale : il ne reste que 375 px de haut, dont 200
   partaient en rembourrage. On les rend à la carte, sinon elle déborde de sa
   scène. La carte, elle, est large : son texte tient en trois lignes. */
@media (max-height: 600px) and (orientation: landscape) {
  /* Téléphone tenu à l'horizontale : il ne reste que 375 px de haut. La scène
     raccourcie du bloc téléphone n'en ferait plus que 209, soit moins que le
     plancher de la carte : elle déborderait par le bas. On rend donc à la
     scène toute la hauteur, et à la carte sa hauteur naturelle : la colonne
     fait 84vw ici, donc le texte tient en trois lignes et le plancher n'a plus
     rien à rattraper. */
  .eng-stack {
    --eng-pin-top: 0px;
    --eng-pin-h: 100vh;
    /* L'éventail se referme presque : la carte fait 560 px de large ici, donc
       ses angles bas balaient loin en tournant et retombent de 48 px à 2,5°,
       pour une scène qui n'en fait que 375. À un degré, la retombée tient dans
       30 px et le filet reste dégagé. */
    --eng-fan: 1;
    --eng-drop: 30px;
  }
  .eng-stack .eng-pin {
    padding: calc(var(--annbar-h) + 36px) 0 12px;
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 96px);
    mask-image: linear-gradient(to bottom, transparent 0, #000 96px);
  }
  .eng-stack .eng-item { min-height: 0; }
  .eng-stack .eng-pace { margin-top: 14px; }
  /* Et la colonne s'élargit, ce qui est le contraire de la règle partout
     ailleurs. Ailleurs elle est étroite pour faire descendre le texte en
     lignes et obtenir le format portrait ; ici il n'y a que 375 px de haut, il
     faut donc que le texte MONTE en lignes. À 560 px la ligne fait 60 signes,
     hors mesure de lecture idéale, mais c'est trois lignes au lieu de six et
     la carte tient dans l'écran. Mesuré sans : carte de 328 px dans une scène
     de 287, soit 61 px hors cadre. */
  .eng-stack .eng-grid,
  .eng-stack .eng-pace { width: min(560px, 84vw); }
}
@supports (height: 100svh) {
  @media (max-height: 600px) and (orientation: landscape) {
    .eng-stack { --eng-pin-h: 100svh; }
  }
}


/* ═══════════ ENGAGEMENTS : L'ÉVENTAIL ═══════════
   Sur ordinateur, les engagements ne se lisent plus en pile déroulée au
   défilement mais en éventail, comme un jeu de cartes étalé sur une table :
   la carte du milieu est droite et pleine, les autres s'ouvrent de part et
   d'autre en tournant et en reculant. On survole pour écarter le paquet, on
   avance à la flèche ou à la touche.

   ⚠️ Ce balisage n'existe dans AUCUN fichier HTML, exactement comme la pile :
   js/main.js monte .eng-fan autour de .eng-grid, et seulement sur un écran
   large muni d'un vrai survol. En dessous de 861 px, au doigt, sans script ou
   si la visiteuse a demandé moins de mouvement, c'est la pile au défilement
   qui prend la main, et à défaut la grille nue. Les trois états sont lisibles ;
   aucun n'est une avarie.

   Les deux mécaniques ne cohabitent jamais : le script choisit l'une OU
   l'autre. C'est pourquoi tout ce bloc est préfixé .eng-fan, comme l'autre
   l'est par .eng-stack. Les dix pages qui portent .eng-grid en héritent sans
   qu'on touche à leur balisage, et l'anglais suit le français par
   construction. Huit engagements sur les accueils : l'éventail en montre sept
   et tourne. Quatre sur les pages d'atterrissage : il les montre toutes et la
   navigation disparaît, elle n'aurait rien à faire tourner. */

/* La scène pose son propre vide autour des cartes : le rembourrage bas de la
   section s'y ajoutait pour rien. */
.engagements.has-fan { padding-bottom: clamp(28px, 4vh, 52px); }
/* L'écart n'est pas une marge de confort : c'est le dégagement de la carte
   survolée. Elle se soulève de 40 px et grossit de 8 %, ce qui la porte
   22 px au-dessus de sa scène. À 28 px il restait 12 px sous le paragraphe
   d'introduction (mesuré), assez pour que les deux se lisent comme collés. */
.engagements.has-fan .eng-head { margin-bottom: clamp(40px, 5vh, 58px); }

.eng-fan {
  /* Les deux seules mesures dont tout le reste se déduit. Le format est celui
     d'une carte à jouer (0,64) : une carte plus large se lirait comme une
     bande de texte posée en biais, pas comme une carte qu'on soulève. */
  /* ⚠️ La largeur ne se règle PAS au confort du texte : elle se règle sur
     l'écart entre deux rangs voisins, qui vaut 0,3667 × --fan-x. À 296 px de
     large pour 176 px d'écart, chaque carte en recouvrait la suivante de
     120 px et l'éventail se lisait comme un paquet mal battu (mesuré, puis
     constaté à l'écran). Le composant d'origine tient un rapport de 0,79 entre
     l'écart et la largeur ; c'est lui qu'on reprend ici, ce qui donne 53 px de
     recouvrement et laisse voir de chaque carte sa vignette et son titre. */
  --fan-w: 248px;
  --fan-h: 452px;
  /* Écart latéral du rang le plus extérieur. Le script le rabote quand la
     fenêtre est trop étroite pour l'étalement complet : c'est un maximum, pas
     une consigne. */
  --fan-x: 520px;
  /* Hauteur de scène. Elle tient la carte droite (452) et la retombée du rang
     extérieur, qui descend de --fan-x-y en tournant de 21°. Mesurée dans le
     DOM, pas estimée : voir REPRISE-ACCUEIL.md. */
  --fan-scene: 524px;
  position: relative;
  /* L'éventail balaie plus large que la section ne le permet sur les écrans
     de 1280. `clip` et non `hidden` : `hidden` ferait de ce bloc un conteneur
     de défilement et décrocherait les positions collantes de la page. */
  overflow-x: clip;
  /* Le débord latéral rend à l'éventail la largeur mangée par le rembourrage
     de la section. Sans lui, les cartes des bords sont rognées au lieu de
     s'ouvrir. */
  margin-inline: calc(-1 * clamp(24px, 7vw, 110px));
  padding-inline: clamp(24px, 7vw, 110px);
}

/* La grille cesse d'être une grille : elle devient la scène, et les cartes s'y
   posent en absolu, toutes au même point de départ. C'est le script qui les
   écarte. */
.eng-fan .eng-grid {
  display: block;
  position: relative;
  height: var(--fan-scene);
  width: 100%;
  max-width: none;
  gap: 0;
  margin: 0 auto;
  /* ⚠️ PAS de `perspective` ici. Elle y a été, et c'était une erreur : posée
     sur la scène, elle ancre son point de fuite au centre de celle-ci, si bien
     qu'une carte étalée à 520 px de là est vue de trois quarts au lieu d'être
     vue de face, et sa bascule au survol part de travers. Chaque carte porte
     donc sa propre `perspective()` dans sa chaîne de transformation, après ses
     translations : le point de fuite tombe alors en son centre, exactement
     comme celui du cadre de l'institut tombe au centre du cadre. Voir peins()
     dans js/main.js, où l'ordre de la chaîne est expliqué. */
}

/* ── La carte ──
   Même verre bronze que la tuile d'origine, mais opaque : les cartes se
   recouvrent, et un fond translucide laisserait lire le titre de celle du
   dessous en filigrane. Le constat vaut aussi pour la pile, où il est écrit
   plus haut : c'est le contraste du texte qui passe au travers, ivoire franc
   sur presque noir, pas la couleur du fond. */
.eng-fan .eng-item {
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--fan-w);
  height: var(--fan-h);
  /* Le script écrit la transformation complète, translation de recentrage
     comprise. Une marge négative ferait le recentrage en CSS, mais alors la
     carte ne tournerait plus autour de son propre centre : elle tournerait
     autour du centre de la scène, et l'éventail deviendrait un carrousel. */
  margin: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Le rembourrage suit la carte : à 248 px de large, 30 px de chaque côté ne
     laisseraient que 188 px de ligne, soit 26 signes. À 26 px il en reste 196,
     et le texte le plus long tient en huit lignes (mesuré). */
  padding: clamp(22px, 1.9vw, 28px) clamp(22px, 1.8vw, 26px) clamp(24px, 2vw, 30px);
  border-radius: 24px;
  border: 1px solid rgba(196, 166, 120, 0.26);
  background:
    linear-gradient(168deg, rgba(253, 250, 244, 0.09), rgba(253, 250, 244, 0.03) 62%),
    #1c150f;
  box-shadow:
    inset 0 1px 0 rgba(253, 250, 244, 0.1),
    0 44px 76px -40px rgba(0, 0, 0, 0.9);
  /* Le pivot est au centre : une carte qu'on étale en éventail tourne sur
     elle-même. Le pivot bas est la grammaire de la PILE, où l'on soulève les
     cartes d'un paquet ; ce n'est pas le même geste. */
  transform-origin: 50% 50%;
  transform: translate(-50%, -50%);
  will-change: transform, opacity;
  /* Le script réécrit transform et opacity à chaque image du ressort : une
     transition ici ferait traîner la carte d'un cran derrière lui. Le liseré,
     lui, n'est pas touché par le script : il se transitionne, et sur la durée
     du ressort (0,52 s pour neuf dixièmes de la course) plutôt qu'en deçà. */
  transition: border-color 0.55s var(--ease-lux);
  /* La tuile d'origine hérite d'un halo et d'une ombre en pseudo-éléments,
     calibrés pour un damier de quatre colonnes. Ici la carte porte déjà son
     ombre propre. */
}
.eng-fan .eng-item::before,
.eng-fan .eng-item::after { content: none; }

/* Seule la carte du milieu répond au pointeur pour ce qui est de la couleur :
   les autres sont vues de biais, un liseré qui s'allume dessus se lit comme un
   défaut de rendu. Le script pose la classe. */
.eng-fan .eng-item.is-front { border-color: rgba(196, 166, 120, 0.42); }

/* Le rythme intérieur, à l'échelle d'une carte de 296 px. Entre la tuile du
   damier (300 px, posée à côté de trois autres) et la carte de la pile
   (330 px, seule à l'écran) : plus proche de la seconde, puisqu'ici aussi
   c'est une carte qu'on regarde et non une colonne qu'on parcourt. */
.eng-fan .eng-block { display: block; }
.eng-fan .eng-band { gap: 14px; margin-bottom: clamp(20px, 2.6vh, 30px); }
.eng-fan .eng-ico { width: 52px; height: 52px; }
.eng-fan .eng-ico svg { width: 24px; height: 24px; stroke-width: 1.15; }
.eng-fan .eng-n { font-size: 2.1rem; opacity: 0.88; }
.eng-fan .eng-t {
  font-size: 1.32rem;
  line-height: 1.24;
  letter-spacing: -0.015em;
  margin-bottom: 14px;
}
.eng-fan .eng-d {
  font-size: 0.9rem;
  line-height: 1.78;
  color: rgba(253, 250, 244, 0.7);
}

/* La lueur qui suit le curseur, reprise telle quelle de la pile : même
   dégradé, même `soft-light`. Le mode de fusion n'est pas décoratif : un
   calque blanc en `normal` délaverait le verre bronze et éteindrait le
   contraste du texte. Elle n'est montée que sur la carte du milieu. */

/* ── La navigation ──
   Une flèche, sept pastilles, une flèche. Elle ne paraît que si l'éventail a
   plus de cartes qu'il n'en montre : à quatre engagements, il n'y a rien à
   faire tourner et le script ne la monte pas. */
.eng-fan-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  margin-top: clamp(26px, 3.4vh, 40px);
  position: relative;
  z-index: 20;
}
.eng-fan-arrow {
  width: 46px;
  height: 46px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--bronze-soft);
  background: rgba(196, 166, 120, 0.09);
  border: 1px solid rgba(196, 166, 120, 0.24);
  cursor: pointer;
  transition:
    color 0.55s var(--ease-lux),
    background 0.55s var(--ease-lux),
    border-color 0.55s var(--ease-lux),
    box-shadow 0.55s var(--ease-lux),
    transform 0.55s var(--ease-lux);
}
.eng-fan-arrow svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
@media (hover: hover) and (pointer: fine) {
  .eng-fan-arrow:hover {
    color: var(--espresso);
    background: linear-gradient(145deg, var(--bronze-pale), var(--bronze));
    border-color: transparent;
    box-shadow: 0 0 0 5px rgba(196, 166, 120, 0.13);
  }
}
/* L'enfoncement garde sa propre durée, courte : c'est un accusé de réception,
   pas un mouvement de scène. Sur 0,55 s il arriverait après le doigt. */
.eng-fan-arrow:active { transform: scale(0.95); transition-duration: 0.12s; }
.eng-fan-dots {
  display: flex;
  align-items: center;
  gap: 9px;
}
/* Les pastilles disent où l'on en est, elles ne se cliquent pas : à sept
   cartes visibles sur huit, « aller à la cinquième » ne veut rien dire de
   précis pour l'œil, alors qu'« avancer d'un cran » se lit tout seul. Elles
   sont donc décoratives, et le script les masque aux lecteurs d'écran. */
.eng-fan-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: rgba(196, 166, 120, 0.22);
  transition:
    background 0.6s var(--ease-lux),
    transform 0.6s var(--ease-lux);
}
.eng-fan-dot.is-on {
  background: var(--bronze-soft);
  transform: scale(1.45);
}

/* ── Écrans intermédiaires ──
   Entre 861 et 1200 px, l'étalement complet ne tient plus dans la fenêtre. Le
   script rabote --fan-x tout seul, mais la carte, elle, doit aussi maigrir :
   sinon les rangs extérieurs se chevauchent au lieu de s'ouvrir. */
@media (max-width: 1200px) {
  .eng-fan {
    --fan-w: 232px;
    --fan-h: 424px;
    --fan-scene: 494px;
  }
  .eng-fan .eng-n { font-size: 1.9rem; }
  .eng-fan .eng-t { font-size: 1.22rem; }
  .eng-fan .eng-d { font-size: 0.86rem; line-height: 1.72; }
  .eng-fan .eng-ico { width: 48px; height: 48px; }
  .eng-fan .eng-ico svg { width: 22px; height: 22px; }
}

/* ═══════════ ÉCRANS TACTILES ═══════════
   Sans souris, un survol se déclenche au doigt et RESTE actif jusqu'au tap
   suivant : les cartes resteraient soulevées. On neutralise les déplacements,
   en gardant les changements de couleur qui, eux, ne gênent pas. */
@media (hover: none) {
  .hs-card:hover,
.ba-card:hover,
.avis-card:hover,
.garantie:hover,
.faq-item:hover,
.autorite-band span:hover,
.idx-link:hover,
.row:hover,
.bezel:hover .media img,
.garantie:hover .garantie-icon,
.ba-wrap:hover .ba-knob{ transform: none; }

  .regard-media .bezel.tilt-r:hover { transform: rotate(1.5deg); }
  .ba-wrap:hover .ba-knob { transform: translate(-50%, -50%); }
}

/* ═══════════ BARRE D'ACTIONS MOBILE ═══════════ */

.mobile-bar { display: none; }

@media (max-width: 860px) {
  .mobile-bar {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 85;
    display: flex;
    gap: 10px;
    padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
    background: rgba(253, 250, 244, 0.82);
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    border-top: 1px solid var(--hairline);
  }
  /* Les trois libellés tiennent sur une ligne, sinon la barre gagne 22 px et
     vient recouvrir la carte de garantie du premier écran. Mesuré en anglais :
     « Book now » demandait 105 px pour 105 px disponibles et cassait en deux,
     ce qui portait la barre de 77 à 99 px. Le rembourrage se resserre avec la
     largeur pour que « Directions », le libellé le plus long, tienne aussi sur
     un écran de 320 px. */
  .mobile-bar .btn {
    flex: 1;
    justify-content: center;
    padding: 14px clamp(8px, 5.3vw, 20px);
    white-space: nowrap;
    min-width: 0;
  }
  .mobile-bar .btn-ghost { background: rgba(253, 250, 244, 0.6); }
  .footer { padding-bottom: 130px; }
}

/* ═══════════ RESPONSIVE ═══════════ */

@media (max-width: 1024px) {
  .menu-grid { grid-template-columns: 1fr; }
  .menu-index { position: static; flex-direction: row; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
  .idx-link {
  min-height: 44px;
  display: flex;
  align-items: center; border: 1px solid var(--hairline); font-size: 0.78rem; }
  .hs-grid { grid-template-columns: 1fr; }
}

/* Entre le passage au burger et le grand écran, le sélecteur ajoute assez de
   largeur pour que la barre déborde : on resserre gouttières et rembourrage
   plutôt que de sacrifier un élément. Le seuil est celui de la barre en trois
   zones, plus haut que l'ancienne barre en flex : le menu centré laisse moins
   de place perdue, donc davantage de largeur utile. */
@media (min-width: 861px) and (max-width: 1120px) {
  .nav { gap: 16px; padding: 10px 10px 10px 22px; }
  .nav-wrap { padding-left: 16px; padding-right: 16px; }
  .nav .nav-links { gap: 17px; }
  .nav-actions { gap: 12px; }
}

@media (max-width: 860px) {
  .nav-links, .btn-nav { display: none; }
  .burger { display: block; }
  /* Le menu disparaît : la colonne centrale n'a plus d'objet. Deux zones
     suffisent, logo contre le bord gauche, actions contre le bord droit. */
  .nav { grid-template-columns: 1fr auto; gap: 16px; padding: 8px 8px 8px 22px; }
  .nav-wrap { padding-left: 16px; padding-right: 16px; }
  .nav-actions { gap: 14px; }

  .eng-grid { grid-template-columns: repeat(2, 1fr); gap: 26px 22px; }

  .regard-grid { grid-template-columns: 1fr; gap: 48px; }
  .regard-grid .regard-media { order: -1; }
  .tilt-l, .tilt-r { transform: none; }

  .rows-2col { grid-template-columns: 1fr; }
  .r-time { display: none; }

  .contact-core { grid-template-columns: 1fr; }
  /* La carte passe en UNE colonne : le bas appartient désormais au NAP, qui
     descend jusqu'au bord. Le filigrane remonte donc au coin haut droit, à
     côté du titre, où la colonne est seule. Le texte du haut s'arrête à
     137 px sur toutes les pages et dans les deux langues, mesuré. */
  .contact-core::after {
    /* Les côtés proches deviennent le HAUT et la DROITE : ce sont ces deux
       marges qui doivent être égales ici, pas la paire du desktop. */
    top: clamp(26px, 5.4vw, 34px);
    right: clamp(26px, 5.4vw, 34px);
    bottom: auto;
    width: clamp(64px, 17vw, 78px);
  }
  .contact-right { grid-template-columns: 1fr 1fr; gap: 28px 24px; }
  .map-core { aspect-ratio: 4 / 3; }
  .ba-grid { grid-template-columns: 1fr; gap: 24px; }
  /* Une seule comparaison sur téléphone, le semi-permanent rouge. Empilées,
     trois comparaisons demandaient trois écrans de défilement pour un même
     argument, et les deux premières s'y lisaient moins bien que la troisième :
     leur avant/après joue sur la matière (gainage, capsule), qui se distingue
     mal à 315 px de large, là où le rouge se lit d'un coup d'œil. Les deux
     autres restent dans le balisage et sur l'écran large. */
  .ba-card-extra { display: none; }
  /* Seule rescapée, elle n'a plus de rang à attendre. */
  .ba-grid .ba-card { transition-delay: 0s; }

  .menu, .headspa, .regard, .contact { padding-left: 20px; padding-right: 20px; }
}

/* ═══════════ TÉLÉPHONES ═══════════
   L'échelle typographique du bureau est trop grande sur un écran de 375 px :
   le titre à lui seul remplissait deux écrans. Barème resserré. */


@media (max-width: 640px) {
  .eyebrow { font-size: 9px; letter-spacing: 0.2em; padding: 6px 13px 5px; margin-bottom: 20px; }
  .h2 { font-size: 2.05rem; margin-bottom: 20px; }
  .lead { font-size: 0.97rem; line-height: 1.7; }

  .autorite { padding: 44px 20px; }
  .autorite-label { font-size: 9px; letter-spacing: 0.2em; margin-bottom: 24px; }
  .autorite-band span { font-size: 0.76rem; letter-spacing: 0.08em; }

  /* Légende avant/après : « en repartant » se coupait en deux */

  .faq-q { font-size: 1.05rem; }
  .faq-item summary { padding: 19px 20px; gap: 14px; }
  .faq-a p { padding: 0 20px 22px; font-size: 0.9rem; }
  .garantie-core { padding: 22px; gap: 16px; }
  .garantie-title { font-size: 1.2rem; }
  .garantie-text { font-size: 0.86rem; }

  .hs-title { font-size: 1.35rem; }
  .hs-price { font-size: 1.45rem; }
  .hs-body { padding: 24px 20px; }
  .hs-steps li { font-size: 0.86rem; gap: 12px; }
  .f-logo-img { width: min(220px, 62vw); }

  /* Menu : les pointillés se réduisaient à un moignon illisible */
  .r-dots { display: none; }
  .row { gap: 16px; align-items: flex-start; }
  .r-name { flex: 1; font-size: 0.9rem; }
  .r-price { font-size: 1.1rem; }
  .cat-core { padding: 26px 22px; }
  .cat-title { font-size: 1.4rem; margin-bottom: 16px; }

  /* Contact : deux colonnes de 112 px cassaient l'adresse en trois lignes */
  .contact-right { grid-template-columns: 1fr; gap: 26px; }
  .c-value { font-size: 1.08rem; line-height: 1.45; }
  .c-label { font-size: 8px; letter-spacing: 0.22em; }
  .contact-core { padding: 30px 22px; }

  .avis-core { padding: 24px 22px; }
  .avis-core blockquote { font-size: 1.1rem; }
  /* 104 px et non 54 : la couture haute mange les 44 premiers. À 54, le logo
     décollait de dix pixels du coin arrondi et la marque se retrouvait
     coincée contre la transition. 104 lui rend une soixantaine de pixels de
     souffle sous le coin, l'écart qu'il avait avant la couture. */
  .footer { padding-top: 104px; }
  .f-meta { font-size: 0.72rem; gap: 8px; }
  .f-credit { font-size: 0.72rem; padding: 10px 18px; }
  .autorite-band { gap: 11px 20px; }
  .autorite-label { padding-top: 30px; margin-bottom: 22px; }
  .eng-grid { grid-template-columns: 1fr; gap: 26px; }
  .garantie-core { flex-direction: column; align-items: flex-start; gap: 14px; }
  .avis-head { padding: 0 20px; }
  .avis-card { width: min(82vw, 340px); }
  .avis-core figcaption { flex-direction: column; align-items: flex-start; gap: 5px; }
  .avis-presta { text-align: left; }
  .avis-name { white-space: nowrap; }
}



/* ═══════════════════════════════════════════════════════════════════════
   PAGES D'ATTERRISSAGE : le strict complément
   Les pages réutilisent les composants de l'accueil tels quels : .opening et
   sa vigne, .menu-cat, .garantie, .engagements, .faq, .contact, .btn, .lang.
   Ce bloc n'ajoute que ce qui n'existait pas : une bande de texte courant,
   la grille des cartes de tarifs, le rappel final et le pied de page court.
   ═══════════════════════════════════════════════════════════════════════ */

/* Bande de texte courant, au gabarit des sections de l'accueil */
.lp-band {
  padding: clamp(100px, 14vh, 170px) clamp(24px, 7vw, 110px);
}
/* Même correction que sur .avis : le filet haut est retiré, la guirlande
   reprenant elle-même les deux fonds, et le rembourrage haut monte à 150 px sur
   ordinateur parce que la bande mord 96,5 px dans la section contre 46 px pour
   l'ancienne vigne. Cette section n'a pas de fond propre, elle hérite du crème
   du body : les bandes posées au-dessus prennent donc la classe on-cream.
   Le sélecteur vise la section qui SUIT une bande, sinon les pages légales, qui
   utilisent .lp-band sans guirlande, gagneraient 38 px de blanc pour rien. */
@media (min-width: 861px) {
  .vine-rule + .lp-band { padding-top: max(150px, clamp(100px, 14vh, 170px)); }
}
.lp-prose { max-width: 860px; margin: 0 auto; }
.lp-prose .lead + .lead { margin-top: 18px; }

/* B3 et B6 : la carte sombre se pose sur la bande claire, jamais l'inverse
   (.hs-steps écrit en ivoire à 72 %, il n'existe que sur fond sombre).
   .hs-card n'avait jamais été rendu hors de .hs-grid : il lui faut une largeur
   propre et un centrage (TODO-RENDU-HS-CARD). Règles de calage de contexte,
   aucun composant nouveau. */
.lp-band > .hs-card { max-width: 860px; margin: 54px auto 0; }
.lp-band > .hs-grid { margin-top: 54px; }
/* Phrase posée sous la carte du déroulé ou sous les cartes de tarifs (clôture
   du protocole pieds, phrase du duo head spa). */
.lp-band > .hs-card + .lp-prose,
.lp-cats + .lp-prose { margin-top: 44px; text-align: center; }
/* Deux .lp-band CLAIRES adjacentes cumulent leurs rembourrages (140 + 140 =
   281 px de vide). Même correction que .legal-intro + .lp-band. Le :not() est
   obligatoire : quand l'une des deux est sombre, la frontière est une vraie
   frontière de fond, et son rembourrage porte la couture. */
.lp-band:not(.on-dark) + .lp-band:not(.on-dark) { padding-top: 0; }

/* ── B3, la scannabilité de la liste d'étapes ──
   L'accroche de chaque étape passe à l'ivoire plein pendant que le reste de la
   ligne garde ses 72 % : la colonne des accroches se lit seule, en deux
   secondes, et le détail reste disponible pour qui le veut. Aucun mot n'est
   ajouté ni retiré, c'est une segmentation du texte existant. */
.hs-steps li strong { color: var(--ivory); font-weight: 500; }

/* ═══════ B3 · LE DÉROULÉ EN GRILLE D'ÉTAPES ═══════
   La liste verticale dans une carte sombre est remplacée par une grille de
   cartes, une par geste : le regard prend les six accroches d'un balayage au
   lieu de descendre six lignes. C'est la grammaire de tuile des engagements
   (.eng-item), reprise trait pour trait : même bande icône + filet + numéro,
   mêmes rayons, mêmes durées, même halo au survol. Seules les VALEURS passent
   en clair, parce que ces cartes se posent sur une bande crème et non sur
   l'espresso : c'est exactement ce que h2-light / lead-light font en sens
   inverse. Le double biseau .bezel porte la carte, comme sur les tarifs et
   les témoignages.

   ⚠️ La grille ne s'appelle PAS .eng-grid : js/main.js monte l'éventail et la
   pile sur `.engagements .eng-grid`, et une seconde grille de ce nom sur la
   page finirait par se faire étaler en éventail au premier changement de
   sélecteur. Nom distinct, aucun risque. */
.lp-steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(16px, 1.7vw, 24px);
  max-width: 1320px;
  margin: 54px auto 0;
}
/* L'en-tête et la phrase de clôture se centrent au-dessus de la grille : un
   texte aligné à gauche sur une grille centrée décroche à droite, et la
   section se lit comme deux blocs qui ne se répondent pas. */
.lp-band:has(.lp-steps) .lp-prose,
.lp-band:has(.hs-grid) .lp-prose { text-align: center; }
/* 🔴 `text-align: center` ne suffit JAMAIS seul sur un chapô : `.lead` est
   borné à 46ch et, sans marge automatique, cette colonne reste calée à gauche
   de son bloc. Le texte se centre alors dans une colonne elle-même décalée, ce
   qui le fait tomber environ 190 px à gauche du titre qu'il accompagne, sur
   une bande de 1320. C'est l'idiome déjà employé par `.inst-head .lead` et
   `.eng-head .lead` : centrer le texte ET la colonne. */
.lp-band:has(.lp-steps) .lp-prose .lead,
.lp-band:has(.hs-grid) .lp-prose .lead,
.lp-steps + .lp-prose .lead,
.lp-cats + .lp-prose .lead { margin-inline: auto; }

/* ── Les en-têtes de section des pages d'atterrissage ──
   Tarifs, avis et questions gardaient l'alignement à gauche des accueils
   au-dessus d'un composant centré et large : le titre et son chapô semblaient
   décrochés vers la gauche de leur propre section. Sur ces pages, tout ce qui
   coiffe un composant se centre.
   Le sélecteur vise exactement les cinq pages d'atterrissage : elles seules
   ont à la fois un premier écran `.opening` et une bande `.lp-band`. Les
   accueils n'ont pas de `.lp-band`, les pages légales n'ont pas d'`.opening`,
   et leur prose légale doit rester alignée à gauche. */
main:has(.opening):has(.lp-band) .menu-head,
main:has(.opening):has(.lp-band) .avis-head,
main:has(.opening):has(.lp-band) .faq-head { text-align: center; }
/* Les chapôs bornés en `ch` sont centrés en bloc, sinon le texte se centre
   dans une colonne qui, elle, reste calée à gauche. */
main:has(.opening):has(.lp-band) .menu-head .lead,
main:has(.opening):has(.lp-band) .avis-head .lead { margin-inline: auto; }
/* La note et le badge suivent le titre qu'ils accompagnent. */
main:has(.opening):has(.lp-band) .avis-score { justify-content: center; }
.lp-step { transition: transform 0.5s var(--ease-lux); }
.lp-step > .bezel-core { padding: 26px 26px 30px; height: 100%; }
/* La bande de tête est celle des engagements, en valeurs claires : le bronze
   plein ne porte pas de texte sur fond clair (3,22:1), c'est --bronze-deep qui
   prend le relais (5,28:1). Règle de contraste du projet, §7.3 du brief. */
.lp-step .eng-ico {
  color: var(--bronze-deep);
  background: rgba(196, 166, 120, 0.14);
  border-color: rgba(168, 127, 76, 0.3);
}
.lp-step .eng-rule {
  background: linear-gradient(90deg, rgba(168, 127, 76, 0.34), rgba(168, 127, 76, 0.08));
}
.lp-step .eng-n { color: var(--bronze-deep); opacity: 0.72; }
.lp-step-t {
  font-family: "Plus Jakarta Sans", sans-serif;
  font-weight: 600;
  font-size: 1.02rem;
  line-height: 1.4;
  letter-spacing: -0.005em;
  color: var(--ink);
  margin-bottom: 12px;
}
.lp-step-d {
  font-size: 0.86rem;
  font-weight: 300;
  line-height: 1.74;
  color: var(--ink-soft);
}
@media (hover: hover) and (pointer: fine) {
  .lp-step:hover { transform: translateY(-6px); }
  .lp-step:hover .eng-ico {
    color: var(--ivory);
    background: linear-gradient(145deg, var(--bronze), var(--bronze-deep));
    border-color: transparent;
    box-shadow: 0 0 0 5px rgba(196, 166, 120, 0.16);
  }
  .lp-step:hover .eng-n { opacity: 1; }
}
/* Sur une bande sombre, la carte reprend les valeurs des engagements : elle
   n'a alors plus rien à surcharger, le double biseau sombre fait le reste. */
.lp-band.on-dark .lp-step .eng-ico { color: var(--bronze-soft); background: rgba(196, 166, 120, 0.09); border-color: rgba(196, 166, 120, 0.24); }
.lp-band.on-dark .lp-step .eng-n { color: var(--bronze-soft); }
.lp-band.on-dark .lp-step-t { color: var(--ivory); }
.lp-band.on-dark .lp-step-d { color: rgba(253, 250, 244, 0.66); }
/* La phrase qui ferme le bloc (clôture du protocole pieds) suit la grille. */
.lp-steps + .lp-prose { margin-top: 44px; text-align: center; }
@media (max-width: 1100px) {
  .lp-steps { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .lp-steps { grid-template-columns: 1fr; gap: 20px; margin-top: 38px; }
  .lp-step > .bezel-core { padding: 22px 22px 26px; }
  /* ⚠️ Le style de la carte est le MÊME qu'en grande largeur, vérifié
     propriété par propriété. Ce qui change est la colonne : empilées à 20 px
     d'écart, l'ombre portée de 70 px de chaque carte retombe sur la suivante
     et noie son liseré bas, si bien que le pourtour ne se lit plus de façon
     égale. On resserre l'ombre pour lui rendre son cadre entier. */
  .lp-step.bezel-dark, .lp-step.bezel { box-shadow: 0 14px 30px -20px rgba(0, 0, 0, 0.5); }
}

/* ── B6, le renversement de risque ──
   La carte ne s'étire plus sur toute la bande : à 1238 px, ses 45 mots
   tenaient en deux lignes de 150 signes, très au-delà de la mesure de lecture,
   et le bloc se lisait comme un bandeau. Resserrée, elle redevient une carte,
   et l'icône passe au-dessus du texte comme elle le fait déjà sous 640 px. */
.lp-band > .garantie { max-width: 620px; margin: 0 auto; }
.lp-band > .garantie .garantie-core { flex-direction: column; align-items: flex-start; gap: 18px; }
/* Le CTA médian est centré par .ba-more, qui porte seul l'écart. */
.lp-band > .garantie + .ba-more { margin-top: clamp(34px, 4.5vh, 52px); }

/* ── L'alternance des fonds ──
   Quatre blocs clairs d'affilée après le premier écran, la page s'aplatissait.
   Une bande prend donc le fond sombre, exactement celui de la section head spa
   de l'accueil, jetons compris : ce n'est pas un fond nouveau, c'est le même.
   🔴 Elle ne peut jamais être posée en deuxième position, juste après la
   guirlande : le voile de .vine-rule est une IMAGE (crème ou ivoire), il
   n'existe pas en version sombre, et une bande espresso sous la guirlande
   ferait apparaître un rectangle crème en travers. Voir le bloc TRANSITION.
   Les titres qu'elle porte prennent h2-light, lead-light et eyebrow-light,
   comme sur .engagements : sans eux, texte encre sur fond espresso. */
.lp-band.on-dark {
  position: relative;
  isolation: isolate;
  /* Un seul halo, ancré en haut : voir la note de .avis.on-dark. Un second
     halo ancré en bas se ferait couper net par la couture de la section
     suivante, qui est un aplat uni. */
  background:
    radial-gradient(60% 50% at 85% 0%, rgba(168, 127, 76, 0.14), transparent 60%),
    var(--espresso);
  color: var(--ivory);
}
/* La couture, reprise du bloc COUTURE DES SECTIONS : le voisin clair prolonge
   sa couleur de 44 px dans la bande sombre et s'y arrondit. */
.lp-band.on-dark::before,
.lp-band.on-dark::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 44px;
  z-index: -1;
  pointer-events: none;
}
.lp-band.on-dark::before { top: 0; border-radius: 0 0 44px 44px; background: var(--seam-t); }
.lp-band.on-dark::after { bottom: 0; border-radius: 44px 44px 0 0; background: var(--seam-b); }
.menu + .lp-band.on-dark,
.avis + .lp-band.on-dark { --seam-t: var(--ivory); }
.lp-band + .lp-band.on-dark { --seam-t: var(--cream); }
/* 🔴 Pas de `--seam-b` ici. La bande sombre en portait un ALORS QUE ses voisins
   portaient déjà leur `--seam-t` : deux coutures se peignaient l'une sous
   l'autre à la même frontière, et la couleur était en plus celle du voisin du
   DESSOUS au lieu de celui du dessus. C'est la section qui reçoit qui porte le
   coin, toujours, et elle prend la couleur de ce qu'il y a au-dessus d'elle :
   ici l'espresso de la bande sombre, qui déborde et s'arrondit dans le clair. */
.lp-band.on-dark + .avis,
.lp-band.on-dark + .menu,
.lp-band.on-dark + .lp-band { --seam-t: var(--espresso); }
/* La carte du déroulé garde son double biseau sombre sur ce fond, exactement
   comme les deux cartes de rituels sur la section head spa de l'accueil. */
.lp-band.on-dark .lp-prose .eyebrow { color: var(--bronze-soft); }

/* ── Le geste qui ferme la section des tarifs ──
   Même métal que la bande finale : c'est le même geste, au même rang. Son
   dégagement était celui de .ba-more (38 à 60 px au-dessus) plus le
   rembourrage bas de la section (100 à 170 px en dessous), soit trois fois
   plus de vide sous le bouton qu'au-dessus. On l'équilibre. */
/* Le bouton appartient à sa section : l'espace qui l'en sépare est plus court
   que celui qui sépare la section de la suivante. 63 px au-dessus, 90 en
   dessous à 900 px de haut, ce qui porte la frontière à 216 px, dans la
   fourchette commune mesurée à 239-252. */
.menu > .ba-more { margin-top: clamp(56px, 7vh, 84px); }
.menu:has(> .ba-more) { padding-bottom: clamp(80px, 10vh, 120px); }
.menu .ba-more .btn-gold .btn-orb { background: rgba(34, 26, 19, 0.16); color: var(--ink); }

/* ── Les deux gestes de la bande finale ──
   🔴 Refait le 29/08. Les deux boutons étaient un plein ivoire et un fantôme
   dont l'orbe disparaissait sur le fond sombre : sur un aplat espresso, un
   fantôme n'a plus de bordure lisible et son glyphe se perdait.
   La hiérarchie se lit maintenant par la matière, comme sur le bouton de fin
   de section 02 : l'action principale prend le métal doré (.btn-gold, repris
   tel quel), la seconde reste un plein ivoire. Deux pleins, deux valeurs, et
   plus aucun contour à deviner.
   Chaque orbe rend l'inverse de son bouton : disque sombre sur métal clair,
   glyphe à l'encre. Sans ces deux lignes, l'orbe du plein reste un disque
   d'ivoire à 16 % sur de l'ivoire, et celui du fantôme hérite de la règle
   .btn-ghost .btn-orb, calibrée pour un fond CLAIR. */
.lp-final-cta { margin-top: clamp(30px, 4vh, 44px); }
.lp-final-band .btn-gold { color: var(--ink); }
.lp-final-band .btn-gold .btn-orb { background: rgba(34, 26, 19, 0.16); color: var(--ink); }
/* Le second geste est un aplat sombre, plus foncé que la bande, avec le liseré
   bronze du site : sur l'espresso, un bouton de la même valeur que son fond
   disparaîtrait. C'est la même valeur que le pied de page, qui est déjà le
   « cran plus bas » du site. */
.lp-final-band .btn-dark {
  background: #17110c;
  color: var(--ivory);
  /* 0,58 et non 0,34 : c'est le liseré qui détache ce bouton de sa bande, son
     aplat n'ayant que 1,09:1 contre elle. À 0,34 il rendait 2,21:1 et à 0,5
     encore 2,92, sous les 3:1 que WCAG 1.4.11 demande à un contour de
     composant. Mesuré à 0,58 : 3,25:1 contre le bouton, 3,17 contre la bande. */
  border: 1px solid rgba(196, 166, 120, 0.58);
  box-shadow: 0 18px 40px -18px rgba(0, 0, 0, 0.65);
}
.lp-final-band .btn-dark:hover { background: #100c08; border-color: rgba(196, 166, 120, 0.55); }
.lp-final-band .btn-dark .btn-orb { background: rgba(196, 166, 120, 0.16); color: var(--bronze-soft); }

/* ── L'éventail des objections sur les pages d'atterrissage ──
   🔴 La carte garde EXACTEMENT la géométrie des accueils : 248 x 452 dans une
   scène de 524, mêmes ombres, même liseré, même rotation. Une tentative de
   l'agrandir pour ces pages a été refusée, à raison : deux formats de carte
   sur un même site, et les cartes débordaient sur la section suivante.
   Ce qui change ici n'est donc pas la carte, c'est ce qu'on met dedans. Les
   objections d'une page d'atterrissage portent la phrase entière de la
   cliente et jusqu'à 60 mots, contre trois mots et 35 sur les accueils : la
   même carte demande une échelle typographique plus serrée, mesurée pour que
   le texte le plus long tienne sans déborder. */
.eng-fan:not(:has(.eng-grid > .eng-item:nth-child(5))) .eng-t {
  font-size: 1.06rem;
  line-height: 1.22;
  margin-bottom: 10px;
}
.eng-fan:not(:has(.eng-grid > .eng-item:nth-child(5))) .eng-d {
  font-size: 0.78rem;
  line-height: 1.62;
}
.eng-fan:not(:has(.eng-grid > .eng-item:nth-child(5))) .eng-band {
  margin-bottom: clamp(14px, 1.8vh, 20px);
}
.eng-fan:not(:has(.eng-grid > .eng-item:nth-child(5))) .eng-ico { width: 44px; height: 44px; }
.eng-fan:not(:has(.eng-grid > .eng-item:nth-child(5))) .eng-n { font-size: 1.8rem; }
/* 🔴 Le dégagement sous les cartes. Sur les accueils, la rangée de flèches et
   de pastilles occupe cet espace ; à quatre cartes, le script ne la monte pas
   (il n'y a rien à faire tourner) et la scène se retrouvait collée à la
   section suivante, cartes soulevées comprises. On rend ici exactement la
   place que la navigation prenait, plus le débord de l'ombre des cartes
   (80 px sous leur boîte : `0 44px 76px -40px`). */
.engagements.has-fan:not(:has(.eng-fan-nav)) { padding-bottom: clamp(96px, 13vh, 152px); }

/* ── Le rythme vertical des pages d'atterrissage ──
   Mesuré section par section avant correction : le vide entre le dernier
   contenu d'une section et le premier de la suivante allait de 180 à 252 px,
   selon que la frontière tombait sur une section à 100/170 (bandes, avis,
   menu, questions, nous trouver) ou sur les engagements, calibrés à 70/120
   pour les accueils. L'écart se voit, surtout à la frontière du menu.
   Les deux `.engagements` des pages d'atterrissage prennent donc le rythme
   commun. Le sélecteur nomme leur voisin, qui n'existe que sur ces pages :
   sur les accueils, `.engagements` suit `.realisations` et garde son
   calibrage. Cible mesurée après correction : 210 à 230 px partout. */
.lp-band + .engagements,
.menu + .engagements,
.contact + .engagements { padding-top: clamp(92px, 12.5vh, 146px); }

/* Cartes de tarifs : même gouttière que la carte de l'accueil */
.lp-cats { max-width: 1320px; margin: 0 auto; display: grid; gap: clamp(18px, 2vw, 26px); }

/* Rappel final : la bande sombre des engagements, resserrée */
.lp-final-band { padding-bottom: clamp(70px, 9vh, 110px); }
.lp-final-band .eng-head { margin-bottom: 46px; }
/* 16 px : la gouttière commune des groupes de boutons du site. À 12 px, les
   deux pastilles de ~140 px se touchaient presque et le couple se lisait
   comme un seul objet. */
.lp-final-cta { display: flex; flex-wrap: wrap; gap: 16px; justify-content: center; }
.lp-final-band .btn-solid { background: var(--ivory); color: var(--ink); }
.lp-final-band .btn-solid:hover { background: #fff; }
.lp-final-band .btn-ghost { color: var(--ivory); border-color: rgba(253, 250, 244, 0.32); }
.lp-final-band .btn-ghost:hover { background: rgba(253, 250, 244, 0.1); border-color: rgba(253, 250, 244, 0.55); }

/* Liens vers les autres pages, sur le fond sombre.
   ⚠️ Leur poids visuel est délibérément faible, et c'est un arbitrage de
   conversion : sur une page qui reçoit du trafic payé, tout lien sortant est
   une fuite. Ils restent parce que ces pages reçoivent AUSSI de l'organique et
   que le maillage entre les cinq est un actif (aucune page orpheline, ancres
   descriptives), et parce qu'ils rattrapent la visiteuse tombée sur la
   mauvaise page, qui repartirait sinon vers les résultats de recherche.
   Mais ils viennent après sept appels à l'action et ne doivent jamais les
   concurrencer du regard : d'où la colonne plus étroite que les boutons, le
   fond retiré et le liseré presque éteint. */
.lp-links { max-width: 720px; margin: 0 auto; display: grid; gap: 9px; }
.lp-link {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px 20px;
  border-radius: 14px;
  border: 1px solid rgba(196, 166, 120, 0.13);
  background: none;
  text-decoration: none;
  color: var(--ivory);
  transition: border-color 0.5s var(--ease-lux), background 0.5s var(--ease-lux),
    transform 0.5s var(--ease-lux);
}
.lp-link-txt { flex: 1; font-size: 0.94rem; }
.lp-link-txt span { display: block; font-size: 0.79rem; color: rgba(253, 250, 244, 0.46); margin-top: 2px; }
.lp-link-orb {
  width: 27px; height: 27px;
  flex-shrink: 0;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--bronze-soft);
  background: rgba(196, 166, 120, 0.14);
  transition: transform 0.5s var(--ease-lux), background 0.5s var(--ease-lux);
}
@media (hover: hover) and (pointer: fine) {
  .lp-link:hover { border-color: rgba(196, 166, 120, 0.45); transform: translateY(-3px); }
  .lp-link:hover .lp-link-orb { transform: translate(2px, -1px) scale(1.08); background: rgba(196, 166, 120, 0.26); }
}

/* Pied de page court.
   ⚠️ Il suit immédiatement .lp-final-band, qui est déjà en espresso : deux
   plans identiques séparés par un filet à 10 % d'opacité se lisaient comme un
   seul bloc sombre de 900 px. Deux corrections, aucune ne rajoute un dégradé
   ancré en haut (interdit ici : .engagements en porte déjà un, et deux halos
   à la couture font une bavure : cf. la section « Les maisons »).
   1. Le pied descend d'un cran en valeur : deux plans distincts, donc deux
      surfaces, même si l'écart est faible.
   2. Un filet dégradé bronze centré marque la couture : c'est le séparateur
      maison entre deux fonds sombres. */
.lp-footer {
  position: relative;
  background: #1b140e;
  color: rgba(253, 250, 244, 0.74);
  padding: clamp(52px, 6.5vw, 74px) clamp(24px, 7vw, 110px)
           calc(clamp(46px, 6vw, 68px) + env(safe-area-inset-bottom));
  font-size: 0.9rem;
  border-top: 1px solid rgba(253, 250, 244, 0.07);
}
.lp-footer::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 50%;
  transform: translateX(-50%);
  width: min(280px, 46vw);
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(196, 166, 120, 0.55), transparent);
}

/* Le crédit du développeur reprend la pilule de l'accueil (.f-credit) au lieu
   d'être noyé en fin de ligne de méta : il devient le point final de la page.
   Centré avec la ligne de méta au-dessus de lui. ⚠️ .f-credit est en
   inline-block : `margin: auto` n'y ferait rien, c'est le text-align du
   conteneur qui le centre : d'où le bloc dédié ci-dessous. */
.lp-credit-wrap { text-align: center; }
.lp-credit { margin-top: 34px; }
.lp-footer a { color: var(--ivory); }
/* ⚠️ La ligne au-dessus repeint TOUS les liens du pied de page en ivoire, y
   compris l'e-mail de la pilule de crédit, qui doit rester bronze comme sur
   l'accueil. Même spécificité (0,1,1) que .f-credit a, mais déclarée plus bas :
   elle gagnait. On la rattrape ici en (0,2,1). Le survol, lui, n'a jamais été
   touché : .f-credit a:hover est déjà en (0,2,1). */
.lp-footer .f-credit a { color: var(--bronze-soft); }
/* ⚠️ Exactement le même piège, sur les trois pastilles sociales : `.lp-footer a`
   en (0,1,1) l'emportait sur `.f-soc` en (0,1,0), et les glyphes sortaient en
   ivoire au lieu du bronze du pied de page de l'accueil. Rattrapé en (0,2,1),
   comme la pilule de crédit juste au-dessus. */
.lp-footer .f-soc { color: var(--bronze-soft); }
.lp-wrap { max-width: 1320px; margin-inline: auto; }
.lp-foot-grid { display: grid; gap: 20px 36px; margin-bottom: 26px; }
.lp-foot-k {
  font-size: 9px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--bronze-soft);
  margin-bottom: 8px;
}
/* Le bas du pied est centré : la grille à quatre colonnes au-dessus porte
   déjà l'axe gauche, et lui répondre par un second bloc aligné à gauche
   laissait toute la moitié droite vide sous le dernier filet. */
.lp-foot-bottom {
  border-top: 1px solid rgba(253, 250, 244, 0.14);
  padding-top: 22px;
  font-size: 0.82rem;
  color: rgba(253, 250, 244, 0.55);
  text-align: center;
}
.lp-foot-bottom a { color: rgba(253, 250, 244, 0.82); }

/* Survol des liens du pied des pages générées : le téléphone et le courriel de
   la colonne « Contact », et les trois liens de la ligne du bas. Aucun ne
   portait de règle de survol.
   On reprend mot pour mot le geste de la ligne de méta de l'accueil
   (.f-meta a) : le filet est là **au repos**, à l'ivoire faible, et il
   s'illumine en bronze au survol. Un trait qui se déroule au passage de la
   souris désigne le lien après coup ; celui-ci le désigne d'emblée et ne fait
   que s'allumer, ce qui est plus calme et plus sûr. Un seul idiome de lien de
   pied de page sur tout le site.
   ⚠️ .f-social vit DANS .lp-foot-grid : sans le :not(), les trois pastilles
   rondes se feraient traverser d'un soulignement. */
.lp-foot-grid a:not(.f-soc),
.lp-foot-bottom a {
  border-bottom: 1px solid rgba(253, 250, 244, 0.25);
  transition: border-color 0.4s;
}
.lp-foot-grid a:not(.f-soc):hover,
.lp-foot-bottom a:hover { border-color: var(--bronze-soft); }

@media (min-width: 861px) {
  /* Trois colonnes de texte, puis les réseaux qui ne prennent que leur largeur :
     à parts égales, la quatrième colonne laisserait un vide de 200 px. */
  .lp-foot-grid { grid-template-columns: repeat(3, 1fr) auto; align-items: start; }
}
@media (max-width: 860px) {
  .lp-footer { padding-bottom: calc(100px + env(safe-area-inset-bottom)); }
  .lp-band { padding: clamp(64px, 9vh, 96px) 20px; }
}
@media (prefers-reduced-motion: reduce) {
  .lp-link:hover { transform: none; }
}


/* Liens vers les pages de prestations, au pied de l'accueil. Volontairement
   hors du menu principal : ils font circuler le PageRank sans alourdir la
   navigation. Ancres descriptives, jamais sur-optimisées. */
.f-pages {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px 26px;
  max-width: 760px;
  margin: 0 auto 22px;
}
.f-pages a {
  font-size: 0.86rem;
  color: rgba(253, 250, 244, 0.68);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color 0.5s var(--ease-lux), border-color 0.5s var(--ease-lux);
}
.f-pages a:hover { color: var(--ivory); border-bottom-color: rgba(196, 166, 120, 0.5); }
@media (max-width: 640px) {
  .f-pages { flex-direction: column; align-items: center; gap: 9px; text-align: center; }
}


/* ═══════════ RÉSEAUX SOCIAUX ═══════════
   Trois pastilles bronze posées sur l'espresso du pied de page. Aucune couleur
   de marque : un Instagram rose et un Facebook bleu feraient descendre le site
   d'un cran et casseraient la seule palette qu'il s'autorise. Le glyphe reste
   filaire, à la graisse des icônes d'engagement, et c'est le contour qui
   s'allume au survol : comme partout ailleurs. Le bloc vit au pied de page,
   après l'appel à l'action : ce sont des sorties, elles ne doivent jamais
   passer devant le téléphone ni devant la réservation. */
.f-social-k {
  font-size: 9px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--bronze-soft);
  margin-bottom: 14px;
}
.f-social {
  display: flex;
  gap: 12px;
}
.f-soc {
  width: 42px;
  height: 42px;
  flex-shrink: 0;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--bronze-soft);
  border: 1px solid rgba(196, 166, 120, 0.28);
  background: rgba(196, 166, 120, 0.06);
  transition: color 0.5s var(--ease-lux), border-color 0.5s var(--ease-lux),
    background 0.5s var(--ease-lux), transform 0.5s var(--ease-lux);
}
.f-soc svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
@media (hover: hover) and (pointer: fine) {
  .f-soc:hover {
    color: var(--ivory);
    border-color: rgba(196, 166, 120, 0.62);
    background: rgba(196, 166, 120, 0.17);
    transform: translateY(-2px);
  }
}
.f-soc:active { transform: translateY(0); }

/* Au pied de l'accueil, le bloc est centré comme tout le reste du footer. */
.footer .f-social-wrap { margin: 0 auto 34px; }
.footer .f-social { justify-content: center; }

/* Variante claire : section « Nous trouver », posée sur l'ivoire. Le bronze
   clair y devient illisible : on reprend le bronze plein, celui du .c-label
   juste au-dessus, pour que la pastille appartienne visiblement au bloc. */
.f-social-light .f-soc {
  width: 38px;
  height: 38px;
  color: var(--bronze-deep);
  border-color: rgba(168, 127, 76, 0.3);
  background: rgba(168, 127, 76, 0.05);
}
.f-social-light .f-soc svg { width: 16px; height: 16px; }
@media (hover: hover) and (pointer: fine) {
  .f-social-light .f-soc:hover {
    color: var(--ivory);
    background: var(--bronze);
    border-color: var(--bronze-deep);
  }
}
/* La grille de contact est en deux colonnes : le bloc social occupe une
   cellule pleine et ses pastilles sont alignées sur la ligne de base du
   texte des blocs voisins, d'où le décalage de 4 px. */
.contact-right .f-social { margin-top: 4px; }

/* ═══════════ PAGES LÉGALES ═══════════
   Mentions légales et politique de confidentialité. Aucun composant nouveau :
   les faits sont présentés sur le motif à pointillés de la carte des tarifs
   (.bezel > .cat-core > .rows > .row), qui convient exactement à un couple
   libellé → valeur. Quatre classes seulement, pour ce que le système n'avait
   pas : une colonne à largeur de lecture, une valeur qui a le droit de
   revenir à la ligne, et un paragraphe de clause.
   Rappel : l'ancienne page utilisait .lp-head / .lp-hero / .lp-h1 / .lp-rows…,
   classes de la version refusée, supprimées depuis. Ne pas les réintroduire. */

/* Deux .lp-band adjacentes cumulent leurs paddings : 140 px + 140 px creusaient
   281 px de vide entre le chapô et la première carte. Or l'en-tête et les
   cartes sont ici un seul flux de lecture, pas deux sections : on soude. */
.legal-intro { padding-bottom: clamp(48px, 6vh, 76px); }
.legal-intro + .lp-band { border-top: 0; padding-top: 0; }

/* .lp-cats monte à 1320 px : c'est la bonne mesure pour une carte de tarifs,
   beaucoup trop large pour du texte juridique suivi. */
.legal-stack {
  max-width: 980px;
  margin: 0 auto;
  display: grid;
  gap: clamp(18px, 2vw, 26px);
}

/* La valeur, à droite. Contrairement à .r-price elle n'est pas en nowrap :
   une adresse ou une phrase d'explication doit pouvoir se replier. */
.r-val {
  font-family: "Cormorant Garamond", serif;
  font-weight: 500;
  font-size: 1.12rem;
  line-height: 1.45;
  text-align: right;
  max-width: 46ch;
}
.r-val a {
  border-bottom: 1px solid rgba(168, 127, 76, 0.4);
  transition: color 0.5s var(--ease-lux), border-color 0.5s var(--ease-lux);
}
.r-val a:hover { color: var(--bronze-deep); border-color: var(--bronze-deep); }
.r-val small {
  display: block;
  font-family: "Plus Jakarta Sans", sans-serif;
  font-size: 0.74rem;
  color: var(--taupe-deep);
  font-weight: 300;
  margin-top: 6px;
  line-height: 1.6;
}
/* Les lignes légales portent des valeurs longues : on leur donne de l'air et
   on empêche le libellé de se faire écraser par la valeur. */
.rows-legal .row { padding: 16px 0; align-items: baseline; }
.rows-legal .r-name { flex: 0 1 auto; color: var(--ink-soft); }
.rows-legal .r-name small { margin-top: 4px; }

/* Inventaire des services tiers. Le nom prend le gabarit d'étiquette du site
   (.c-label / .lp-foot-k) et l'explication court dessous, au fil du texte :
   une description de quatre lignes alignée à droite se lit mal, son bord
   gauche partant en drapeau. */
.tools { display: flex; flex-direction: column; }
.tool { padding: 20px 0; border-top: 1px solid var(--hairline); }
.tool:first-child { padding-top: 6px; border-top: 0; }
.tool-name {
  font-size: 9px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--bronze-deep);
  margin-bottom: 9px;
}
.tool-sub {
  display: block;
  margin-top: 5px;
  letter-spacing: 0.04em;
  text-transform: none;
  font-size: 0.76rem;
  font-style: italic;
  font-weight: 300;
  color: var(--taupe-deep);
}

/* Paragraphe de clause. Mesure courte et interligne généreux : c'est du texte
   qu'on lit mal par nature, autant ne pas l'aggraver. */
.legal-p {
  font-size: 0.95rem;
  font-weight: 300;
  line-height: 1.78;
  color: var(--ink-soft);
  max-width: 68ch;
}
.legal-p + .legal-p { margin-top: 16px; }
.legal-p strong { font-weight: 500; color: var(--ink); }
.legal-p a {
  color: var(--bronze-deep);
  border-bottom: 1px solid rgba(168, 127, 76, 0.35);
  transition: color 0.5s var(--ease-lux), border-color 0.5s var(--ease-lux);
}
.legal-p a:hover { color: var(--ink); border-color: var(--ink); }

@media (max-width: 640px) {
  /* Côte à côte, un libellé et une adresse complète se partagent 150 px
     chacun : illisible. On empile, et la valeur repasse à gauche. */
  .rows-legal .row { flex-direction: column; align-items: flex-start; gap: 5px; }
  .rows-legal .r-name { flex: none; font-size: 0.82rem; }
  .r-val { text-align: left; max-width: none; font-size: 1.05rem; }
}

/* Lien contextuel sous les avant/après. C'est le seul endroit du site où
   Instagram est utile plutôt que décoratif : on vient de montrer trois
   résultats, la question suivante est « et le reste ? ». Bouton fantôme sans
   orbe : .btn-ghost pose padding-right:26px sans condition, un orbe y serait
   décentré de 11 px. Les 7 autres .btn-ghost du site n'en portent pas non plus. */
.ba-more { text-align: center; margin-top: clamp(38px, 5vh, 60px); }

@media (prefers-reduced-motion: reduce) {
  .f-soc, .f-soc:hover { transform: none; }
}

/* ═══════════ CONSENTEMENT ═══════════
   Une carte posée en bas à gauche, jamais un voile plein écran : la visiteuse
   doit pouvoir lire la page pendant qu'elle décide. Sur mobile elle prend
   toute la largeur et la barre d'actions s'efface le temps du choix, sinon les
   deux se superposent au même endroit. */

.jn-consent {
  position: fixed;
  z-index: 200;
  right: 22px;
  bottom: 22px;
  /* 340 et non 390 : depuis que le texte tient en une phrase, la carte large
     laissait une ligne courte au milieu d'un bloc vide. Une bannière discrète
     est d'abord une bannière petite. */
  width: min(340px, calc(100vw - 44px));
  background: rgba(253, 250, 244, 0.94);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--hairline);
  border-radius: 22px;
  box-shadow: 0 28px 60px -28px rgba(42, 32, 24, 0.42);
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.6s var(--ease-settle), transform 0.6s var(--ease-settle);
  pointer-events: none;
}
.jn-consent.est-visible { opacity: 1; transform: none; pointer-events: auto; }

.jn-consent-corps { padding: 18px 20px 20px; }

.jn-consent-texte {
  margin: 0 0 14px;
  font-size: 0.8rem;
  line-height: 1.65;
  color: var(--ink-soft);
}
/* Le coeur qui ferme la phrase est un emoji : il porte sa propre chasse et
   colle au lien. L'espace se met sur le lien, pas dans la chaîne, pour qu'il
   ne se replie pas en fin de ligne. */
.jn-consent-texte a {
  margin-left: 0.35em;
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--taupe);
}
.jn-consent-texte a:hover { text-decoration-color: var(--ink); }

/* Les deux boutons partagent la largeur : refuser doit peser autant
   qu'accepter, à la taille près comme au contraste près. */
.jn-consent-actions { display: flex; gap: 10px; }
.jn-consent-actions .btn {
  flex: 1;
  justify-content: center;
  padding: 12px 20px;
  font-size: 0.82rem;
}

@media (max-width: 860px) {
  .jn-consent {
    left: 12px;
    right: 12px;
    bottom: calc(12px + env(safe-area-inset-bottom));
    width: auto;
    border-radius: 20px;
  }
  /* La barre d'actions occupe exactement cette place : elle attend son tour. */
  body.jn-consent-ouvert .mobile-bar { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .jn-consent { transition: opacity 0.2s linear; transform: none; }
  .jn-consent.est-visible { transform: none; }
}

/* ═══════════ CARTE EN ATTENTE ═══════════
   Tant que Google n'est pas autorisé, la carte est remplacée par une vignette
   qui occupe le même volume, pour que le bloc contact ne saute pas quand
   l'iframe arrive. Le clic charge la carte seule, sans rien accepter d'autre :
   la CNIL admet ce consentement au cas par cas. */


/* Le lien qui rouvre la bannière depuis la politique de confidentialité. Un
   bouton, parce qu'il déclenche une action et ne mène nulle part, mais dessiné
   comme les liens qui l'entourent pour ne pas trouer le paragraphe. */
.lien-consent {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--ink);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--taupe);
}
.lien-consent:hover { text-decoration-color: var(--ink); }
