/* =====================================================================
   MASSE-MON-STRESS — démo de référence MASSOTHÉRAPIE · YannCode Solutions
   ---------------------------------------------------------------------
   Palette « lin et eucalyptus ». Volontairement l'opposé de la démo
   Déneigement Roger (nuit, Archivo Black, blizzard) : ici c'est le jour,
   le lin, la respiration. Deux niches, deux ambiances, même finition.

   Divergence assumée avec Roger : racine typographique NORMALE (16 px)
   au lieu de la racine gonflée à ~11 px. Les planchers de lisibilité de
   CLAUDE.md sont écrits en rem « normaux » — avec une racine normale ils
   se vérifient littéralement, sans conversion mentale.
   ===================================================================== */

/* ---------- POLICES (auto-hébergées, zéro Google Fonts externe) ------- */
@font-face {
  font-family: 'Playfair Display';
  src: url('../fonts/playfair-display.woff2') format('woff2');
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Geist';
  src: url('../fonts/geist.woff2') format('woff2');
  font-weight: 400 700; font-style: normal; font-display: swap;
}

/* ---------- TOKENS --------------------------------------------------- */
:root {
  /* Socle jour */
  --lin:        #F4F0E8;  /* [USAGE : fond principal du site] */
  --lin-2:      #EAE3D7;  /* [USAGE : bandes de section alternées] */
  --craie:      #FDFCFA;  /* [USAGE : cartes, surfaces surélevées] */
  --encre:      #23302B;  /* [USAGE : tout le texte courant — vert-noir, pas noir pur] */
  --eucalyptus: #4A6B5D;  /* [USAGE : titres, filets, couleur de marque] */
  --sauge:      #55695E;  /* [USAGE : texte secondaire, filigranes, états inactifs]
                             Assombri à la QA du 13 août : le #8FA79A d'origine
                             donnait 2,0:1 sur le lin — sous le 4,5:1 de WCAG AA,
                             et il porte de vraies phrases (descriptions des soins,
                             signatures d'avis). Ici : 5,2:1 sur lin, 4,6:1 sur lin-2. */
  --argile:     #96502E;  /* [USAGE : UNIQUEMENT les actions — boutons, prix, focus]
                             Assombri à la QA du 13 août : le #C0714A d'origine
                             tombait à 3,2:1 en TEXTE (prix « 90 $ », personas,
                             spécialités) et 3,7:1 en fond de bouton — sous AA
                             des deux côtés, et l'argile porte les PRIX.
                             Ici : 5,3:1 sur lin, 4,7:1 sur lin-2. */
  --argile-f:   #7E4126;  /* [USAGE : survol des boutons pleins] */
  --bord:       rgba(35, 48, 43, 0.12);  /* [USAGE : filets, séparateurs] */
  --bord-fort:  rgba(35, 48, 43, 0.30);  /* [USAGE : bordures de boutons fantômes] */

  /* Typo */
  --police-titre: 'Playfair Display', Georgia, 'Times New Roman', serif;
  --police-corps: 'Geist', -apple-system, 'Segoe UI', Roboto, sans-serif;

  /* Échelle — mobile. Les valeurs desktop sont plus bas, en media query. */
  --fs-corps: 1.05rem;   /* 16,8 px · plancher mobile corps = 1rem      ✓ */
  --fs-lead:  1.2rem;    /* [USAGE : chapeaux d'intro] */
  --fs-ui:    0.95rem;   /* 15,2 px · plancher mobile UI = 0,8rem       ✓ */
  --fs-note:  0.9rem;    /* 14,4 px · `.note` porte de vraies phrases
                            (descriptions des soins, note du téléphone) :
                            c'est du TEXTE SECONDAIRE, plancher 0,9rem —
                            pas un badge à 0,75rem.                     ✓ */

  /* Rythme */
  --pad-x:      clamp(1.25rem, 0.6rem + 3vw, 3.5rem);
  --section-y:  clamp(4rem, 2rem + 9vw, 9rem);
  --col-gap:    clamp(2rem, 1rem + 4vw, 5rem);
  --radius:     2px;     /* presque droit : le lin ne fait pas de coins ronds */
  --max:        1280px;

  /* Ombres — teintées à l'encre, jamais au noir pur : sur du lin, une ombre
     grise vire au sale. Deux couches (proche + large) pour éviter le halo
     uniforme qui trahit un `box-shadow` de gabarit. */
  --ombre:      0 1px 2px rgba(35, 48, 43, 0.05), 0 6px 14px rgba(35, 48, 43, 0.06);
  --ombre-haut: 0 2px 4px rgba(35, 48, 43, 0.07), 0 14px 30px rgba(35, 48, 43, 0.13);

  --sortie: cubic-bezier(0.22, 1, 0.36, 1);
}

/* Desktop : le plancher absolu de CLAUDE.md est 1,1rem pour TOUT,
   y compris les boutons, labels, badges et le pied de page. */
@media (min-width: 769px) {
  :root {
    --fs-corps: 1.2rem;
    --fs-lead:  1.55rem;
    --fs-ui:    1.1rem;
    --fs-note:  1.1rem;
  }
}

/* ---------- SOCLE ---------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--lin);
  color: var(--encre);
  font-family: var(--police-corps);
  font-size: var(--fs-corps);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; height: auto; display: block; }
/* Un <picture> est `inline` par défaut : il garde alors l'espace sous la
   ligne de base et pousse un décalage de mise en page (CLS). */
picture { display: block; }
a { color: inherit; }

h1, h2, h3 {
  font-family: var(--police-titre);
  font-weight: 700;
  line-height: 1.06;
  letter-spacing: -0.015em;
  margin: 0;
  color: var(--eucalyptus);
}
h1 { font-size: clamp(2.75rem, 1.4rem + 5.4vw, 6rem); }
h2 { font-size: clamp(2rem, 1.2rem + 3.1vw, 3.5rem); }
h3 { font-size: clamp(1.35rem, 1.1rem + 1.1vw, 1.9rem); }
p  { margin: 0 0 1.1em; }
p:last-child { margin-bottom: 0; }

.contenu { width: 100%; max-width: var(--max); margin-inline: auto; padding-inline: var(--pad-x); }
.section { padding-block: var(--section-y); }
.section-lin2 { background: var(--lin-2); }

/* Le petit sur-titre en capitales espacées : signature du site. */
.kicker {
  font-size: var(--fs-note);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--sauge);
  margin: 0 0 1.2rem;
  font-weight: 600;
}
.lead { font-size: var(--fs-lead); line-height: 1.5; color: var(--encre); }
.note { font-size: var(--fs-note); color: var(--sauge); }

/* Accessibilité : un focus visible partout, en argile. */
:focus-visible { outline: 3px solid var(--argile); outline-offset: 3px; }
.saut-contenu {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--eucalyptus); color: var(--lin); padding: 0.9rem 1.4rem;
  font-size: var(--fs-ui);
}
.saut-contenu:focus { left: 0; }

/* ---------- BOUTONS -------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: 0.6rem;
  font-family: var(--police-corps);
  font-size: var(--fs-ui); font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.95em 1.9em;
  border: 1px solid transparent;
  border-radius: var(--radius);
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.3s var(--sortie), color 0.3s var(--sortie),
              border-color 0.3s var(--sortie), transform 0.3s var(--sortie),
              box-shadow 0.3s var(--sortie);
}
.btn-plein { background: var(--argile); color: var(--lin); }
.btn-fantome { background: transparent; color: var(--eucalyptus); border-color: var(--bord-fort); }
.btn-clair { background: transparent; color: var(--lin); border-color: rgba(244, 240, 232, 0.5); }

/* Le changement de couleur reste partout ; la LEVÉE, elle, est réservée aux
   appareils à souris. Sur tactile, `:hover` s'accroche au tap et ne se relâche
   jamais : un bouton figé 2 px trop haut, pour toujours. (Le `.btn-plein`
   levait déjà sans garde — corrigé ici en même temps.) */
@media (hover: hover) {
  .btn:hover { transform: translateY(-2px); }
  .btn-plein:hover { background: var(--argile-f); box-shadow: 0 6px 16px rgba(126, 65, 38, 0.28); }
  .btn-fantome:hover { border-color: var(--eucalyptus); background: rgba(74, 107, 93, 0.06); }
  .btn-clair:hover { background: rgba(244, 240, 232, 0.14); border-color: var(--lin); }
}
/* Sans souris, le retour visuel se fait à la pression — pas au survol. */
.btn:active { transform: translateY(0); }

/* ---------- NAVIGATION (patron Roger : fixe + voile au défilement) ---- */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background-color 0.4s, border-color 0.4s, backdrop-filter 0.4s;
}
.nav.nav-fond {
  background: color-mix(in srgb, var(--lin) 86%, transparent);
  backdrop-filter: blur(14px);
  border-bottom-color: var(--bord);
}
.nav-inner {
  max-width: var(--max); margin-inline: auto; padding: 1.1rem var(--pad-x);
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
}
.logo {
  font-family: var(--police-titre); font-size: clamp(1.25rem, 1rem + 0.9vw, 1.6rem);
  color: var(--eucalyptus); text-decoration: none; letter-spacing: -0.02em; line-height: 1;
}
/* Sur le héros sombre, la nav passe en clair tant qu'elle n'est pas voilée. */
.nav:not(.nav-fond) .logo,
.nav:not(.nav-fond) .nav-liens a { color: var(--lin); }
.nav:not(.nav-fond) .nav-burger span { background: var(--lin); }

.nav-liens { display: flex; align-items: center; gap: clamp(1.2rem, 0.5rem + 1.8vw, 2.4rem); }
.nav-liens a {
  font-size: var(--fs-ui); text-decoration: none; color: var(--encre);
  letter-spacing: 0.04em; position: relative; padding-block: 0.3rem;
}
/* `.nav-liens a` (0,2,0) l'emportait sur `.btn-plein` (0,1,0) : dès que la nav
   se voile — donc en permanence sur les pages internes — le bouton Réserver
   repassait en texte encre sur fond argile (2,3:1). Il garde son texte clair. */
.nav-liens a.btn-plein { color: var(--lin); }
.nav-liens a::after {
  content: ''; position: absolute; left: 0; bottom: 0; height: 1px; width: 0;
  background: currentColor; transition: width 0.4s var(--sortie);
}
.nav-liens a:hover::after, .nav-liens a[aria-current='page']::after { width: 100%; }

.nav-burger {
  display: none; flex-direction: column; gap: 5px; justify-content: center;
  width: 44px; height: 44px; padding: 0; background: none; border: 0; cursor: pointer;
}
.nav-burger span {
  display: block; height: 2px; width: 24px; background: var(--encre);
  transition: transform 0.35s var(--sortie), opacity 0.2s;
}
.nav-ouverte .nav-burger span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-ouverte .nav-burger span:nth-child(2) { opacity: 0; }
.nav-ouverte .nav-burger span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

@media (max-width: 900px) {
  .nav-burger { display: flex; }
  .nav-liens {
    position: absolute; inset: 100% 0 auto 0;
    flex-direction: column; align-items: flex-start; gap: 0;
    background: var(--lin); border-bottom: 1px solid var(--bord);
    /* max-height plutôt que display:none — l'a11y survit à la transition */
    max-height: 0; overflow: hidden; transition: max-height 0.45s var(--sortie);
  }
  .nav-ouverte .nav-liens { max-height: 70vh; }
  .nav-liens a { width: 100%; padding: 1.1rem var(--pad-x); color: var(--encre) !important; }
  /* `width: auto` + `align-self: stretch` : le `width: 100%` hérité de
     `.nav-liens a` donnait 375 px de bouton PLUS 2 × var(--pad-x) de marge —
     le bouton Réserver dépassait de 21 px à droite et le `overflow: hidden`
     du menu lui coupait le flanc. `stretch` déduit les marges au lieu de les
     ajouter. (Corrigé le 26 août ; visible menu OUVERT seulement.) */
  /* `justify-content` : `.btn` est un inline-flex sans alignement — tant qu'il
     se dimensionne sur son texte ça ne se voit pas, mais étiré sur toute la
     largeur du menu son libellé restait collé à gauche. */
  .nav-liens .btn {
    margin: 0.6rem var(--pad-x) 1.4rem; width: auto; align-self: stretch;
    justify-content: center;
  }
  .nav:not(.nav-fond) .nav-liens a { color: var(--encre); }
}

/* ---------- HÉROS ---------------------------------------------------- */
.hero {
  position: relative; min-height: 100svh;
  display: flex; align-items: flex-end;
  overflow: clip; isolation: isolate;
}
.hero-fond { position: absolute; inset: 0; z-index: -2; overflow: clip; }
/* <picture> est un élément INLINE à hauteur auto. Sans cette ligne, le
   `height: 100%` de l'image se résolvait contre une hauteur auto — donc pas du
   tout : l'image reprenait son ratio natif (1400×764) et s'arrêtait à 690 px
   dans un héros de 860 px, laissant 170 px de vide au pied. L'ancien voile
   global, très sombre en bas, rendait la couture invisible ; en le retirant on
   la voit. Corrigé le 26 août. */
.hero-fond picture { display: block; width: 100%; height: 100%; }
/* 50% 32% : le héros est une photo d'équipe, les visages vivent dans le haut
   du cadre. Un cadrage plus bas leur coupait le front en plein écran. */
.hero-fond img { width: 100%; height: 100%; object-fit: cover; object-position: 68% 32%; }
/* 26 août — RÈGLE DU 25 : le but d'une image héros est de la MONTRER. Le voile
   GLOBAL est parti (dégradé 5 paliers jusqu'à 0,88 en pied + lavis 105° à 0,34,
   qui ensemble noyaient le bas du cadre et éteignaient la salle de soin). Ne
   reste que le RACCORD DE NAV : une bande courte en haut, pour que la barre
   claire tienne sur le haut de la photo. Le contraste du texte, lui, se pose
   LOCALEMENT derrière le bloc (.hero-texte::before).
   ⚠️ Jamais `transparent` dans ces dégradés : en CSS il vaut rgba(0,0,0,0),
   donc l'interpolation traverse du NOIR et salit la photo. On écrit la couleur
   du voile en alpha 0. */
.hero-fond::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to bottom,
    rgba(21, 30, 26, 0.58) 0%, rgba(21, 30, 26, 0.30) 6%, rgba(21, 30, 26, 0) 13%);
}
/* width:100% est nécessaire : en enfant de flex, la boîte se rétrécirait
   sur son contenu et les marges auto la centreraient, ce qui décalerait
   le héros par rapport à la colonne du reste du site. */
.hero-texte { width: 100%; padding-block: clamp(3.5rem, 2rem + 7vw, 7rem); position: relative; }
/* SCRIM LOCAL — le seul assombrissement du corps du héros, et il ne vit que
   derrière ce bloc. Recette reprise de la refonte Glacier (Roger V2, 25 août),
   re-teintée vert-noir pour le lin. Deux effets superposés, tous les deux
   MASQUÉS pour que les bords meurent en fondu (jamais de boîte à arêtes) :
   ① backdrop-filter: brightness — rabat les hautes lumières (le rideau clair
     derrière la massothérapeute de gauche, là où le H1 tombait à 1,9:1) sans
     aplatir la couleur ;
   ② un aplat vert-noir très léger — plancher de contraste là où la photo est
     déjà sombre et où le brightness seul ne donne plus rien.
   Masque en DEUX dégradés croisés (intersect) plutôt qu'une ellipse — et
   surtout pas un dégradé radial, proscrit sur les maquettes comme ici :
   · horizontal — plein jusqu'à 46 % (l'encre s'arrête à x≈585 sur 1265), puis
     un fondu long qui meurt à 74 %. Un fondu court laissait une arête verticale
     lisible sur le rideau, qui est un aplat sans texture ;
   · vertical — fondu sur les 12 % du haut (le scrim ne remonte pas au ras de la
     nav) et les 8 % du bas (il ne coupe pas net au pied du héros). */
.hero-texte::before {
  content: ''; position: absolute; z-index: -1; pointer-events: none;
  inset: 0 0 0 -4rem;
  background: rgba(21, 30, 26, 0.20);
  backdrop-filter: brightness(0.52);
  -webkit-backdrop-filter: brightness(0.52);
  -webkit-mask-image:
    linear-gradient(to right, #000 0 46%, rgba(0, 0, 0, 0) 74%),
    linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, #000 12%, #000 92%, rgba(0, 0, 0, 0) 100%);
  mask-image:
    linear-gradient(to right, #000 0 46%, rgba(0, 0, 0, 0) 74%),
    linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, #000 12%, #000 92%, rgba(0, 0, 0, 0) 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
.hero h1 { max-width: 20ch; }
.hero h1 { color: var(--lin); }
.hero .kicker { color: rgba(244, 240, 232, 0.8); }
.hero-sous {
  color: rgba(244, 240, 232, 0.92);
  font-size: var(--fs-lead); line-height: 1.5;
  max-width: 34rem; margin: 1.6rem 0 2.4rem;
}
.hero-actions { display: flex; flex-wrap: wrap; gap: 0.9rem; }
/* Halo SERRÉ sur les textes du héros — c'est le contraste le plus local qui
   existe : il colle du sombre contre le glyphe au lieu d'assombrir une région.
   C'est lui qui permet de garder le scrim discret. Le H1 (91 px) se contente
   d'une ombre douce ; les PETITS textes (sous-titre, bouton fantôme) prennent
   le halo court, ce sont eux qui décrochent en premier sur une photo claire. */
.hero h1 { text-shadow: 0 0.2rem 1.6rem rgba(21, 30, 26, 0.7); }
.hero-sous, .hero .btn-clair {
  text-shadow: 0 0 0.5rem rgba(21, 30, 26, 0.95), 0 0 1.1rem rgba(21, 30, 26, 0.85),
               0 0.2rem 1.6rem rgba(21, 30, 26, 0.7);
}

/* Le titre monte ligne par ligne derrière un masque. Tout en CSS : aucune
   bibliothèque, et ça tourne même dans un onglet d'arrière-plan. */
.ligne-masque { overflow: hidden; display: block; }
.ligne-masque > span { display: block; }
.js .hero-fond img { animation: poser 2.4s var(--sortie) both; }

/* Le héros respire — DESKTOP SEULEMENT (voir la note de régime plus bas).
   `poser` cadre l'arrivée (2,4 s), puis `ken-burns` prend le relais et dérive
   1 → 1.055 en 22 s, aller-retour infini. Les deux vivent dans la MÊME
   déclaration `animation` : pendant son délai, `ken-burns` n'a pas de `fill`,
   donc il ne pose aucune valeur et `poser` règne ; à 2,4 s il devient actif et,
   déclaré plus loin dans la liste, il gagne. Aucun chaînage JS, aucun
   `animationend` à écouter.

   ⚠️ Pourquoi pas sur téléphone : le Speed Index de Lighthouse compare les
   images du film au rendu FINAL. Une animation qui ne s'arrête jamais fait que
   plus aucune image ne correspond tout à fait à la dernière, et le score se
   dégrade — sur le régime mobile (85), le plus serré des deux. Sur téléphone,
   le héros est de toute façon une bande 5/6 posée AU-DESSUS du texte, pas un
   plein écran : la dérive lente s'y voit à peine. On garde le wow là où il
   paie et où le budget de score le permet. */
@media (min-width: 768px) {
  .js .hero-fond img {
    will-change: transform;
    animation:
      poser 2.4s var(--sortie) both,
      ken-burns 22s ease-in-out 2.4s infinite alternate;
  }
}
.js .ligne-masque > span { animation: monte 1.1s var(--sortie) both; }
.js .ligne-masque:nth-child(1) > span { animation-delay: 0.15s; }
.js .ligne-masque:nth-child(2) > span { animation-delay: 0.24s; }
.js .ligne-masque:nth-child(3) > span { animation-delay: 0.33s; }
.js .hero-sous    { animation: leve 0.9s var(--sortie) 0.55s both; }
.js .hero-actions { animation: leve 0.9s var(--sortie) 0.72s both; }

@keyframes poser { from { transform: scale(1.12); } to { transform: scale(1); } }
@keyframes ken-burns { from { transform: scale(1); } to { transform: scale(1.055); } }
@keyframes monte { from { transform: translateY(115%); } to { transform: translateY(0); } }
@keyframes leve  { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }

/* ---------- HÉROS SUR TÉLÉPHONE : SÉPARÉ, PAS SUPERPOSÉ ---------------
   Le portrait généré répartit les trois massothérapeutes sur toute la
   hauteur du cadre : en plein écran, le titre couvrait le visage de la
   deuxième et le sous-titre celui de la troisième. Aucun cadrage ne le
   règle (l'image tombe pile à la hauteur du bloc). On sépare donc les
   deux : la photo en haut, le texte dessous sur le lin.
   -------------------------------------------------------------------- */
@media (max-width: 767px) {
  .hero { min-height: 0; display: block; }
  /* Sort du fond absolu et redevient une bande dans le flux. La bande se
     mesure sur la LARGEUR, jamais en svh : une hauteur d'écran fait varier le
     recadrage, et sur un 640 de haut la troisième massothérapeute perdait
     le menton. Adossée à la largeur, elle donne le même cadrage partout.
     26 août — le `aspect-ratio: 5/6` qui vivait ici ne s'appliquait pas : le
     <picture> inline poussait la boîte à la hauteur naturelle de l'image et le
     ratio cédait. Le rendu réel — le portrait ENTIER, non recadré — est le bon
     (les trois massothérapeutes, les galets, les huiles), alors on l'assume :
     ratio naturel déclaré, plus de recadrage. Rendre le 5/6 effectif coupait
     le front de la première. La règle du 25 vaut ici aussi : montrer l'image. */
  .hero-fond { position: relative; inset: auto; z-index: 0; height: auto; }
  .hero-fond picture, .hero-fond img { height: auto; }
  /* 26 août — le voile de nav MESURÉ : le logo clair sur le rideau donnait
     2,41:1 au 5e centile de ses glyphes, sous le 4,5:1, ET il assombrissait
     les 60 % du haut de la photo — les trois visages. Assombrir davantage
     aurait aggravé le premier défaut pour corriger le second.
     Ici le texte du héros vit sur le lin, sous la photo : la nav est le SEUL
     texte posé dessus. On lui donne donc son propre fond opaque en permanence
     (l'état que le site prend déjà au défilement et sur les pages internes)
     et on retire le voile. Zéro assombrissement de la photo, nav à 8,7:1. */
  .hero-fond::after { background: none; }
  .nav {
    /* 97 % et non 86 % comme `.nav-fond` : ici le lin est posé sur une PHOTO,
       pas sur la page. À 92 % le logo eucalyptus mesurait 4,54:1 au 5e centile
       — au-dessus du seuil, mais d'un cheveu, et le fond bouge d'un téléphone
       à l'autre selon le recadrage. À 97 % on retrouve le 5,2:1 du lin plein. */
    background: color-mix(in srgb, var(--lin) 97%, transparent);
    backdrop-filter: blur(14px);
    border-bottom-color: var(--bord);
  }
  .nav:not(.nav-fond) .logo { color: var(--eucalyptus); }
  .nav:not(.nav-fond) .nav-liens a { color: var(--encre); }
  .nav:not(.nav-fond) .nav-liens a.btn-plein { color: var(--lin); }
  .nav:not(.nav-fond) .nav-burger span { background: var(--encre); }
  /* Texte sombre sur lin : les couleurs « sur photo » ne tiennent plus. */
  .hero-texte { padding-block: 2.4rem 3rem; }
  /* Le scrim et les halos ne servent qu'au texte SUR la photo. */
  .hero-texte::before { display: none; }
  .hero h1, .hero-sous, .hero .btn-clair { text-shadow: none; }
  .hero h1 { color: var(--eucalyptus); }
  .hero-sous { color: var(--encre); }
  .hero .btn-clair { color: var(--eucalyptus); border-color: var(--bord-fort); }
  .hero .btn-clair:hover { background: rgba(74, 107, 93, 0.06); border-color: var(--eucalyptus); }
}

/* ---------- EN-TÊTE DES PAGES INTERNES -------------------------------
   Pas de héros ici : il faut dégager la hauteur de la nav fixe, sinon le
   H1 passe dessous. La nav y reste voilée en permanence (voir site.js).
   -------------------------------------------------------------------- */
.entete { padding-top: calc(var(--section-y) + 4.5rem); }
.entete h1 { max-width: 16ch; }
.entete .lead { max-width: 44rem; margin-top: 1.6rem; }

/* ---------- MEDIA + HOVER-ZOOM --------------------------------------
   L'effet demandé par Yannick. Roger n'en a pas : ses zooms sont au
   chargement et au défilement. On reprend SA valeur (1.12) pour rester
   dans la même famille de sensation.
   `@media (hover: hover)` évite l'état qui reste collé au doigt sur
   tactile, où :hover se déclenche au tap et ne se relâche jamais.
   -------------------------------------------------------------------- */
.media { position: relative; overflow: clip; background: var(--lin-2); }
.media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 0.75s var(--sortie);
}
@media (hover: hover) {
  .media:hover img, a:hover .media img { transform: scale(1.12); }
}
/* ---------- LEVÉE DES CARTES AU SURVOL (24 août) ---------------------
   Le zoom d'image existait déjà ; ce qui manquait, c'est que la carte
   ENTIÈRE réagisse. Une seule règle pour les trois familles de cartes
   (soins, équipe, avis) — elles ne diffèrent que par leur contenu.
   Sous `@media (hover: hover)` : sur tactile, `:hover` se déclenche au tap
   et reste collé, et une carte figée en position levée est un bogue visuel.
   -------------------------------------------------------------------- */
.carte-soin, .membre, .avis {
  transition: transform 0.3s var(--sortie), box-shadow 0.3s var(--sortie);
}
@media (hover: hover) {
  .carte-soin:hover, .membre:hover, .avis:hover {
    transform: translateY(-4px);
    box-shadow: var(--ombre-haut);
  }
}
/* L'avis est la seule carte à porter un fond et une bordure : elle vit donc
   déjà avec une ombre de repos, sinon la levée sort de nulle part. */
.avis { box-shadow: var(--ombre); }

.media-4-5 { aspect-ratio: 4 / 5; }
.media-3-2 { aspect-ratio: 3 / 2; }
.media-16-9 { aspect-ratio: 16 / 9; }

/* ---------- INTRO DEUX TEMPS ----------------------------------------- */
.duo {
  display: grid; gap: var(--col-gap);
  grid-template-columns: 1fr;
  align-items: start;
}
@media (min-width: 860px) { .duo { grid-template-columns: 0.85fr 1.15fr; } }
.duo h2 { margin-bottom: 0.4rem; }

/* ---------- GRILLE D'APERÇU DES SOINS (accueil) ----------------------- */
.grille-soins {
  display: grid; gap: clamp(1.5rem, 0.8rem + 2vw, 2.6rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  margin-top: 3.5rem;
}
.carte-soin { text-decoration: none; display: block; color: inherit; }
.carte-soin h3 { margin: 1.1rem 0 0.35rem; font-size: clamp(1.25rem, 1.05rem + 0.7vw, 1.6rem); }
.carte-soin .note { color: var(--sauge); }
.carte-soin .num {
  font-family: var(--police-titre); font-size: var(--fs-note);
  color: var(--argile); letter-spacing: 0.1em;
}

/* ---------- ZIGZAG DES 6 SOINS (page services) ------------------------ */
.soin {
  position: relative;
  display: grid; gap: var(--col-gap); align-items: center;
  grid-template-columns: 1fr;
  padding-block: clamp(3rem, 1.5rem + 6vw, 6.5rem);
  border-top: 1px solid var(--bord);
}
.soin:first-of-type { border-top: 0; }
@media (min-width: 900px) {
  .soin { grid-template-columns: 1fr 1fr; }
  /* Le zigzag : une ligne sur deux, l'image passe à droite. */
  .soin:nth-child(even) .soin-media { order: 2; }
}
.soin-corps { position: relative; z-index: 1; }
/* Le numéro d'index, à GAUCHE du titre (13 août). Il était auparavant un
   chiffre géant en filigrane DERRIÈRE le texte : il brouillait à la fois le
   sur-titre et le nom du soin. Il vit maintenant dans le <h2>, dimensionné
   en `em` pour suivre le titre, et remonté à hauteur de capitale. */
.soin-num {
  font-family: var(--police-titre);
  font-size: 0.45em;
  vertical-align: 0.62em;
  color: var(--argile);
  letter-spacing: 0.04em;
  margin-right: 0.45em;
  user-select: none;
}
.soin-persona {
  font-size: var(--fs-note); font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--argile); margin: 0 0 0.8rem;
}
.soin h2 { margin-bottom: 0.9rem; }
.soin-desc { max-width: 34rem; }

/* Lignes de conduite tarifaires — le plus beau détail de la référence,
   repris puis animé : le filet se trace au défilement. */
.tarifs { list-style: none; margin: 2rem 0 2.2rem; padding: 0; max-width: 30rem; }
.tarif {
  display: flex; align-items: baseline; gap: 0.75rem;
  padding-block: 0.7rem;
  font-size: var(--fs-ui);
}
.tarif-duree { font-family: var(--police-titre); color: var(--eucalyptus); white-space: nowrap; }
.tarif-filet {
  flex: 1 1 auto; height: 1px; background: var(--bord-fort);
  transform-origin: left center;
}
/* Le filet n'est rétracté que si JS est là pour le dérouler. Sans JS,
   il est simplement tracé — jamais d'élément invisible sans issue. */
.js .tarif-filet { transform: scaleX(0); }
.tarif-prix { font-family: var(--police-titre); color: var(--argile); white-space: nowrap; }

/* ---------- RAIL D'INDEX COLLANT (page services) ---------------------- */
/* ⚠️ L'ordre compte : `.rail { display:none }` doit être déclaré AVANT la
   media query, sinon (spécificité égale) c'est lui qui gagne, le rail
   disparaît, et les six soins tombent dans la colonne de 7rem. */
.services-corps { display: grid; grid-template-columns: 1fr; gap: 0; }
.rail { display: none; position: sticky; top: 45vh; align-self: start; }
@media (min-width: 1080px) {
  .services-corps { grid-template-columns: 7rem 1fr; gap: 3rem; }
  .rail { display: block; }
}
.rail ol { list-style: none; margin: 0; padding: 0; }
.rail li { margin-bottom: 0.85rem; }
.rail a {
  font-family: var(--police-titre); font-size: var(--fs-note);
  color: var(--sauge); text-decoration: none;
  display: inline-flex; align-items: center; gap: 0.6rem;
  transition: color 0.4s var(--sortie);
}
.rail a::before {
  content: ''; width: 1.1rem; height: 1px; background: currentColor;
  transition: width 0.4s var(--sortie);
}
.rail a.actif { color: var(--argile); }
.rail a.actif::before { width: 2.2rem; }

/* ---------- BANDEAU DÉFILANT DE BIENFAITS ---------------------------- */
.marquee {
  overflow: hidden; border-block: 1px solid var(--bord);
  padding-block: 1.5rem; background: var(--lin);
}
.marquee-piste {
  display: flex; gap: 3.5rem; width: max-content;
  animation: glisse 42s linear infinite;
}
.marquee span {
  font-family: var(--police-titre); font-size: clamp(1.1rem, 0.9rem + 0.9vw, 1.7rem);
  color: var(--eucalyptus); white-space: nowrap;
  display: inline-flex; align-items: center; gap: 3.5rem;
}
.marquee span::after { content: '·'; color: var(--argile); }
@keyframes glisse { to { transform: translateX(-50%); } }
.marquee:hover .marquee-piste { animation-play-state: paused; }

/* ---------- TÉMOIGNAGES ---------------------------------------------- */
.grille-avis {
  display: grid; gap: clamp(1.5rem, 0.8rem + 2vw, 2.4rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 19rem), 1fr));
  margin-top: 3rem;
}
.avis {
  background: var(--craie); border: 1px solid var(--bord);
  padding: clamp(1.6rem, 1rem + 1.6vw, 2.4rem);
}
.avis blockquote {
  margin: 0 0 1.2rem; font-family: var(--police-titre);
  font-size: clamp(1.15rem, 1rem + 0.6vw, 1.45rem);
  line-height: 1.4; color: var(--eucalyptus);
}
.avis figcaption { font-size: var(--fs-note); color: var(--sauge); }

/* ---------- ÉQUIPE (page à propos) ------------------------------------ */
.grille-equipe {
  display: grid; gap: clamp(1.5rem, 0.8rem + 2.4vw, 3rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  margin-top: 3.5rem;
}
/* RETIRÉ le 13 août : un voile eucalyptus (0,55 en bas → 0,12 en haut) couvrait
   les trois portraits et ne se levait qu'au SURVOL. Sur un téléphone, où le
   survol n'existe pas, la version ternie était la seule que le visiteur voyait
   — et sur desktop, la vraie photo n'apparaissait qu'à la souris. Même famille
   que le piège du 12 août : ce qui est dégradé par défaut ne doit pas dépendre
   d'un seul geste pour redevenir normal. Les portraits sont déjà générés dans
   la palette du site, ils n'ont pas besoin d'être teintés. */
.membre h3 { margin: 1.1rem 0 0.3rem; font-size: clamp(1.2rem, 1.05rem + 0.6vw, 1.5rem); }
.membre .specialites { font-size: var(--fs-note); color: var(--argile); letter-spacing: 0.06em; }
.membre p { margin-top: 0.7rem; }

/* ---------- SECTION RÉSERVER ----------------------------------------- */
.reserver { background: var(--eucalyptus); color: var(--lin); }
.reserver h2 { color: var(--lin); }
.reserver .kicker { color: rgba(244, 240, 232, 0.7); }
.reserver-grille {
  display: grid; gap: var(--col-gap); grid-template-columns: 1fr;
  align-items: start; margin-top: 2.5rem;
}
@media (min-width: 900px) { .reserver-grille { grid-template-columns: 1fr 1fr; } }

.champ { margin-bottom: 2rem; }
.champ > legend, .champ > .champ-titre {
  font-size: var(--fs-note); letter-spacing: 0.16em; text-transform: uppercase;
  color: rgba(244, 240, 232, 0.7); margin-bottom: 0.9rem; padding: 0;
}
.champ fieldset { border: 0; margin: 0; padding: 0; }
.pastilles { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.pastille { position: relative; }
.pastille input { position: absolute; opacity: 0; width: 0; height: 0; }
.pastille span {
  display: inline-block; cursor: pointer;
  font-size: var(--fs-ui);
  padding: 0.7em 1.2em;
  border: 1px solid rgba(244, 240, 232, 0.35);
  border-radius: var(--radius);
  transition: background-color 0.3s, border-color 0.3s, color 0.3s;
}
.pastille input:checked + span { background: var(--argile); border-color: var(--argile); color: var(--lin); }
.pastille input:focus-visible + span { outline: 3px solid var(--lin); outline-offset: 3px; }

.recap {
  background: rgba(244, 240, 232, 0.09);
  border: 1px solid rgba(244, 240, 232, 0.22);
  padding: clamp(1.6rem, 1rem + 1.8vw, 2.6rem);
}
.recap-prix {
  font-family: var(--police-titre);
  font-size: clamp(2.6rem, 1.6rem + 4vw, 4.4rem);
  line-height: 1; color: var(--lin); margin: 0.4rem 0 0.2rem;
}
.recap-detail { font-size: var(--fs-ui); color: rgba(244, 240, 232, 0.85); min-height: 1.6em; }
.recap .btn { margin-top: 1.8rem; }
.recap .note { color: rgba(244, 240, 232, 0.62); margin-top: 1.2rem; }

/* ---------- FAQ ------------------------------------------------------- */
.faq { max-width: 46rem; margin-top: 2.5rem; }
.faq details { border-bottom: 1px solid var(--bord); }
.faq summary {
  cursor: pointer; list-style: none;
  padding: 1.4rem 2.5rem 1.4rem 0; position: relative;
  font-family: var(--police-titre);
  font-size: clamp(1.15rem, 1rem + 0.6vw, 1.45rem);
  color: var(--eucalyptus);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: '+'; position: absolute; right: 0; top: 50%; transform: translateY(-50%);
  font-size: 1.5rem; color: var(--argile); transition: transform 0.3s;
}
.faq details[open] summary::after { transform: translateY(-50%) rotate(45deg); }
.faq details p { padding-bottom: 1.5rem; max-width: 40rem; }

/* ---------- CONTACT + PIED ------------------------------------------- */
.contact-grille {
  display: grid; gap: var(--col-gap);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  margin-top: 2.5rem;
}
.contact-grille h3 { font-size: clamp(1.1rem, 1rem + 0.4vw, 1.3rem); margin-bottom: 0.6rem; }
.contact-grille a { text-decoration: none; border-bottom: 1px solid var(--bord-fort); }
.contact-grille a:hover { border-bottom-color: var(--argile); color: var(--argile); }

.pied { background: var(--encre); color: rgba(244, 240, 232, 0.78); padding-block: 3.5rem 2.5rem; }
.pied a { color: inherit; }
.pied-haut {
  display: flex; flex-wrap: wrap; gap: 1.5rem 2.5rem;
  justify-content: space-between; align-items: baseline;
  padding-bottom: 2rem; border-bottom: 1px solid rgba(244, 240, 232, 0.16);
}
.pied .logo { color: var(--lin); }
.pied-nav { display: flex; flex-wrap: wrap; gap: 1.5rem; font-size: var(--fs-ui); }
.pied-bas { padding-top: 2rem; font-size: var(--fs-note); line-height: 1.7; }
.pied-bas p { margin-bottom: 0.6rem; }
/* La note de provenance des couleurs — obligation /demo du 15 juillet. */
.pied-couleurs { color: rgba(244, 240, 232, 0.55); }

/* ---------- LÉGAL (patron :target de Roger) --------------------------- */
.legal { display: none; background: var(--lin-2); }
.legal:target { display: block; }
.legal h2 { margin-bottom: 1.5rem; }
.legal h3 { font-size: clamp(1.1rem, 1rem + 0.4vw, 1.3rem); margin: 2rem 0 0.7rem; }
.legal .contenu { max-width: 48rem; }

/* ---------- RÉVÉLATIONS ----------------------------------------------
   Cachées seulement si JS est là pour les révéler : sans JS, `.js` n'est
   jamais posée sur <html> et tout le contenu reste visible.
   Un IntersectionObserver pose `.vu`, le CSS fait la transition. `--i`
   est le rang de l'élément dans sa fratrie : c'est lui qui cascade.

   ⚠️ 24 août — la révélation se déplace en `translate`, PAS en `transform`.
   Les cartes se lèvent au survol en `transform`, et un élément n'a qu'UNE
   liste `transition` : la dernière déclaration qui gagne la cascade remplace
   l'autre en entier. Résultat avec deux `transform` : soit la révélation
   durait 0,3 s, soit la levée au survol attendait le délai de cascade (jusqu'à
   0,5 s) puis mettait 0,7 s. `translate` est une propriété indépendante qui se
   compose avec `transform` — les deux gestes cohabitent sans s'écraser, à
   condition que la liste ci-dessous nomme les QUATRE propriétés, chacune avec
   sa durée et son délai.
   -------------------------------------------------------------------- */
.js .reveler {
  opacity: 0; translate: 0 20px;
  transition:
    opacity   0.7s var(--sortie) calc(var(--i, 0) * 90ms),
    translate 0.7s var(--sortie) calc(var(--i, 0) * 90ms),
    transform 0.3s var(--sortie),
    box-shadow 0.3s var(--sortie);
}
.js .reveler.vu { opacity: 1; translate: none; }

/* Les filets tarifaires se tracent quand leur bloc est révélé. */
.js .tarif-filet { transition: transform 1s var(--sortie); }
.js .reveler.vu .tarif-filet { transform: scaleX(1); }
.js .reveler.vu .tarif:nth-child(1) .tarif-filet { transition-delay: 0.25s; }
.js .reveler.vu .tarif:nth-child(2) .tarif-filet { transition-delay: 0.39s; }

/* ---------- MOUVEMENT RÉDUIT (filet de sécurité global) --------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .marquee-piste { animation: none; }
  .js .reveler { opacity: 1 !important; translate: none !important; transform: none !important; }
  .tarif-filet { transform: scaleX(1) !important; }
  /* Le héros ne dérive plus : `animation: none` coupe `poser` ET `ken-burns`
     (la règle générique ci-dessus ramène la durée à 0,01 ms, mais un
     `alternate infinite` à 0,01 ms reste un élément qu'on repeint pour rien).
     `will-change` part avec : plus d'animation, plus de couche à promouvoir. */
  .js .hero-fond img { animation: none !important; will-change: auto; transform: none; }
  @media (hover: hover) {
    .media:hover img, a:hover .media img { transform: none; }
    .carte-soin:hover, .membre:hover, .avis:hover { transform: none; }
    .btn:hover { transform: none; }
  }
}
