/* ==========================================================================
   AlyCare — Styles globaux
   ========================================================================== */

/* POLICES AUTO-HÉBERGÉES (2026-09-06) — remplace l'@import Google Fonts.
   Deux raisons :
   1) Performance. L'@import chaînait deux allers-retours réseau AVANT que
      la moindre police ne commence à se télécharger : main.css →
      fonts.googleapis.com (DNS + TLS + CSS) → fonts.gstatic.com (DNS +
      TLS + woff2). Les fichiers vivent maintenant dans assets/fonts/,
      servis par le même domaine, et les deux plus critiques sont
      préchargés dans le <head> de chaque page (voir les <link
      rel="preload"> — à RECOPIER sur toute nouvelle page).
   2) RGPD. Plus aucun appel aux serveurs Google au chargement, donc un
      sous-traitant de moins à déclarer dans confidentialite.html.

   Ce sont les VERSIONS VARIABLES (axe wght) : un seul fichier par famille
   couvre toutes les graisses, au lieu d'un fichier par graisse. Ne pas
   remplacer par des fichiers statiques sans refaire ce calcul.
   Sources : paquets npm @fontsource-variable/* (miroir officiel des Google
   Fonts). Sous-ensembles latin + latin-ext uniquement — cyrillique,
   grec et vietnamien retirés, inutiles pour un site en français.
   Les noms de familles ci-dessous sont ceux d'origine (et NON les
   "... Variable" de Fontsource) pour que css/variables.css reste
   inchangé. Si Aly fournit un jour les vraies polices licenciées
   (DIN Condensed / Century Gothic Pro / Handsome Pro), remplacer les
   fichiers ici et dans --font-heading/--font-body/--font-script. */

/* ⚠️ L'ORDRE DE CES @font-face COMPTE, et il est volontaire :
   latin-ext D'ABORD, latin EN DERNIER, pour chaque famille et chaque style.

   Les deux plages se RECOUVRENT (latin-ext couvre U+0100-02BA, qui contient
   le U+0152-0153 « Œ/œ » déjà présent dans latin). Quand plusieurs @font-face
   d'une même famille couvrent un caractère, c'est la déclaration la PLUS
   TARDIVE qui l'emporte. Avec latin en premier — l'ordre livré le matin du
   2026-09-06 — le seul « œ » de la page (dans « cœur ») faisait gagner
   latin-ext et déclenchait le téléchargement de DEUX fichiers inutiles,
   45 Ko, sur un site entièrement en français. Mesuré, puis corrigé.

   C'est exactement pour cette raison que le CSS servi par Google Fonts
   place lui aussi latin en dernier. Ne pas « ranger » ces blocs par ordre
   alphabétique ou par sous-ensemble : ça réintroduirait le bug. */

@font-face {
  font-family: 'Big Shoulders Display';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../assets/fonts/big-shoulders-display-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: 'Big Shoulders Display';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../assets/fonts/big-shoulders-display-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../assets/fonts/jost-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../assets/fonts/jost-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: 'Jost';
  font-style: italic;
  font-weight: 100 900;
  font-display: swap;
  src: url('../assets/fonts/jost-latin-ext-italic.woff2') format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: 'Jost';
  font-style: italic;
  font-weight: 100 900;
  font-display: swap;
  src: url('../assets/fonts/jost-latin-italic.woff2') format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: 'Caveat';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../assets/fonts/caveat-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: 'Caveat';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../assets/fonts/caveat-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
*,
*::before,
*::after {
  box-sizing: border-box;
  cursor: none;
}

/* Tous les éléments cliquables ont leur icône curseur custom
   (.cursor-link, voir components.css + js/components/cursor.js) : jamais
   de pointer natif ici, sinon les deux curseurs se superposent. Le
   sélecteur universel ci-dessus a une spécificité nulle, donc sans cette
   règle explicite les navigateurs réappliqueraient quand même leur
   "pointer" par défaut sur ces éléments. */
a,
button,
.btn,
select,
label[for],
[role="button"],
[onclick],
summary,
input[type="submit"],
input[type="button"],
input[type="checkbox"],
input[type="radio"] {
  cursor: none;
}

/* Tactile ET mouvement réduit : on rend le curseur natif. Le curseur custom
   ne se lance que si `!finePointer || reduced` est FAUX (voir
   js/components/cursor.js, la ligne `if (!finePointer || reduced) return;`)
   — mais le `cursor: none` ci-dessus, lui, s'appliquait à tout le monde sans
   condition. Sans souris fine ça ne se voyait pas (cas tactile, corrigé le
   2026-08-26) ; découvert le 2026-09-15 que la même chose se produit sous
   `prefers-reduced-motion: reduce` (Windows : Accessibilité → Effets
   visuels → Effets d'animation désactivé, ou équivalent macOS/Linux) : le
   script s'arrête au tout début sans rien dessiner, et il ne restait ALORS
   AUCUN curseur à l'écran — custom comme natif — pour une souris classique.
   Les deux conditions du bail-out JS sont donc reprises ici en OR (liste de
   media queries séparées par une virgule = disjonction en CSS), pour ne
   jamais laisser passer un cas où le script renonce sans que le CSS ne
   restaure le curseur natif. */
@media (pointer: coarse), (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    cursor: auto;
  }

  a,
  button,
  .btn,
  select,
  label[for],
  [role="button"],
  [onclick],
  summary,
  input[type="submit"],
  input[type="button"],
  input[type="checkbox"],
  input[type="radio"] {
    cursor: pointer;
  }
}

/* ---- Lenis (sensation de scroll fluide) — CSS recommandé par la lib ---- */
html.lenis,
html.lenis body {
  height: auto;
}

.lenis.lenis-smooth {
  scroll-behavior: auto !important;
}

.lenis.lenis-smooth [data-lenis-prevent] {
  overscroll-behavior: contain;
}

.lenis.lenis-stopped {
  overflow: hidden;
}

.lenis.lenis-scrolling iframe {
  pointer-events: none;
}


/* Sélection de texte (2026-08-21, thème sombre) : la surbrillance native
   du navigateur, calibrée pour du texte noir sur blanc, rend le texte
   crème illisible sur fond sombre — on la repeint aux couleurs de la
   charte. */
::selection {
  background: var(--color-primary);
  color: var(--color-text-on-primary);
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
  color: var(--color-text);
  background-color: var(--color-bg);
}

/* Pages courtes (2026-08-25) — <body class="page-short">
   AUCUNE PAGE NE PORTE CETTE CLASSE AUJOURD'HUI (retirée partout les 02 et
   03/09, une fois toutes les pages remplies) : la règle est conservée pour
   une future page volontairement courte.
   Quand le contenu ne remplit pas l'écran, le pied de page s'arrêtait au milieu de la
   fenêtre en laissant une bande vide en dessous. Ces trois lignes le
   poussent en bas de l'écran SANS le rendre fixe : dès que le contenu
   dépasse une hauteur d'écran, margin-top: auto ne pousse plus rien et le
   pied de page reprend sa place normale à la suite.
   Opt-in par une classe plutôt qu'appliqué à tout le site : l'accueil
   empile des calques (pin en sticky du store, manifeste en absolute) et
   n'a aucun besoin de ce traitement — inutile de lui changer son body en
   conteneur flex. Les éléments du curseur custom sont en position fixed,
   donc hors flux : ils ne deviennent pas des enfants flex. */
body.page-short {
  display: flex;
  flex-direction: column;
  min-height: 100svh;
}

body.page-short .site-footer { margin-top: auto; }

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  font-weight: 500;
  line-height: var(--line-height-heading);
  margin: 0 0 var(--space-sm);
  /* Vert réservé aux titres (2026-08-21, demande de Rom) : c'est ici,
     et quasiment nulle part ailleurs, qu'on doit voir --color-accent. */
  color: var(--color-heading);
}

h1 { font-size: var(--font-size-3xl); }
h2 { font-size: var(--font-size-2xl); }
h3 { font-size: var(--font-size-xl); }

p {
  margin: 0 0 var(--space-sm);
}

a {
  color: var(--color-primary-dark);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

img {
  max-width: 100%;
  display: block;
}

ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

button {
  font-family: inherit;
}

/* ---- Layout helpers ---- */
.container {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 var(--space-md);
}

.section {
  /* --space-section, pas --space-2xl : sur un viewport court les 96 px
     du haut ET du bas mangeaient 32 % de l'écran. Voir variables.css
     § ÉCHELLE LAPTOP — inchangé dès 1000 px de hauteur de viewport,
     donc sur tout écran de bureau. */
  padding: var(--space-section) 0;
}

.section--alt {
  background-color: var(--color-bg-alt);
}

/* ==========================================================================
   CTA POINTILLÉ — repris de obermann-webdesign.de ("Ready for the next
   step?") : une grille de petits points (::before, tuile ~19px, même
   recette radial-gradient(couleur 15%, transparent 8%) que la référence),
   révélée uniquement au centre de la section via un mask radial (même
   ellipse 60% / 43% que l'original) — elle s'estompe vers les bords plutôt
   que de couvrir tout le bloc en aplat. Couleur adaptée à la palette du
   site (rose --color-primary) au lieu du blanc de la référence (fond
   sombre là-bas, fond clair ici). N'affecte que les sections qui portent
   la classe — actuellement seule .cta-dots sur epilation-laser.html.
   ========================================================================== */
.cta-dots {
  position: relative;
  overflow: hidden;
  /* Position du "spot" qui suit la souris (2026-08-20, voir ::after et
     js/cta-dots.js) — hors écran par défaut tant que le JS ne l'a pas
     positionné (pas de survol au chargement). */
  --spot-x: 50%;
  --spot-y: -9999px;
  /* Dégradé (2026-08-20) : raccorde en douceur le fond de la FAQ juste
     au-dessus (var(--color-bg), pas de .section--alt) au blanc de cette
     section (var(--color-surface)) sur les 260 premiers pixels, plutôt
     qu'un changement net à la frontière des deux sections. */
  background: linear-gradient(
    to bottom,
    var(--color-bg) 0,
    var(--color-surface) 260px,
    var(--color-surface) 100%
  );
}

.cta-dots::before {
  content: '';
  position: absolute;
  inset: 0;
  /* Rose vif de la charte (--color-primary, #e6325e) repris des lignes de
     la timeline "Épilation laser" (voir css/timeline.css et
     .institut-video__contours dans home.css), pour rester cohérent avec
     les fonds décoratifs déjà en place plus haut sur le site. */
  background-image: radial-gradient(color-mix(in srgb, var(--color-primary) 30%, transparent) 15%, transparent 8%);
  background-size: 19px 19px;
  background-repeat: repeat;
  /* Masque agrandi en hauteur (2026-08-20) : révèle plus de points vers le
     haut/bas de la section (ellipse 43% → 78% de hauteur, fondu repoussé à
     96%) — la tuile de points (taille/espacement ci-dessus) ne change pas,
     seule la zone où le masque les laisse visibles s'étend. */
  mask-image: radial-gradient(60% 48%, black 0%, black 10%, transparent 96%);
  -webkit-mask-image: radial-gradient(60% 48%, black 0%, black 10%, transparent 96%);
  pointer-events: none;
}

/* Points à pleine opacité (2026-08-20) : même tuile/couleur que ::before,
   mais SANS l'atténuation à 30% — révélés uniquement autour du curseur via
   un second masque (petit cercle centré sur --spot-x/--spot-y), combiné en
   intersection avec la même vignette globale que ::before (pour ne jamais
   dépasser la zone où les points existent déjà). Résultat : chaque point
   sous la souris passe à 100% d'opacité, le reste garde son opacité
   réduite (::before, en dessous). Purement visuel, voir js/cta-dots.js
   pour la position du spot — aucune ligne de JS dans ce fichier. */
.cta-dots::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image: radial-gradient(var(--color-primary) 15%, transparent 8%);
  background-size: 19px 19px;
  background-repeat: repeat;
  mask-image:
    radial-gradient(60% 48%, black 0%, black 10%, transparent 96%),
    radial-gradient(64px 64px at var(--spot-x) var(--spot-y), black 0%, transparent 100%);
  mask-composite: intersect;
  -webkit-mask-image:
    radial-gradient(60% 48%, black 0%, black 10%, transparent 96%),
    radial-gradient(64px 64px at var(--spot-x) var(--spot-y), black 0%, transparent 100%);
  -webkit-mask-composite: source-in;
  pointer-events: none;
  transition: opacity 0.15s linear;
}

.cta-dots .container {
  position: relative;
  z-index: 1;
}

/* CTA CARTE (2026-08-20) — réutilise .benefit-card telle quelle (fond,
   radius, shadow-soft, ombre rosée + lueur curseur + trait d'accent au
   survol : tout vient de css/benefits.css, aucune duplication). .cta-card
   n'ajoute que le gabarit propre à une carte seule et centrée (les 3
   cartes "bienfaits" sont dans une grille, celle-ci est isolée dans son
   propre container). */
.cta-card {
  max-width: 420px;
  margin: 0 auto;
  align-items: center;
  text-align: center;
  gap: var(--space-sm);
  /* overflow: visible (2026-08-20) : .benefit-card le met à "hidden" par
     défaut — sans l'override, l'anneau ci-dessous (§"bordure animée")
     serait rogné, même collé au bord de la carte. */
  overflow: visible;
  /* Repositionnement (2026-08-20) : la carte, une fois verrouillée par
     js/cta-dots.js (transform revenu à 0), se retrouve dans sa position
     "naturelle" au sein de .cta-dots, centrée par le padding symétrique de
     .section (haut/bas identiques). On la remonte en transférant de
     l'espace du dessus vers le dessous (marge négative en haut, positive
     en bas, même valeur) : la hauteur totale de .cta-dots ne change pas
     (donc le fond pointillé, en absolute inset:0 sur toute la section,
     reste rigoureusement identique), seule la carte se déplace à
     l'intérieur. */
  margin-top: calc(-1 * var(--space-xl));
  margin-bottom: var(--space-xl);
}

/* CTA CARTE — bordure animée une fois en place (2026-08-20, corrigée n°2) :
   dès que la carte est verrouillée (.is-locked, posée par js/cta-dots.js
   une fois --ctp à 1), un anneau conic-gradient rose se met à tourner,
   collé au contour exact de la carte (inset: 0, même border-radius) pour
   attirer l'œil sur le CTA.
   Première version (mask-composite: exclude, technique à double masque)
   testée en direct sur le site : le masque n'excluait pas juste le centre
   comme prévu, il rendait tout l'anneau invisible — bug constaté en ligne.
   Deuxième version (background multi-couches + `transform: rotate()` sur
   le pseudo-élément entier) : bug constaté en ligne, pire que le premier —
   on tourne un RECTANGLE arrondi sur lui-même, donc à chaque angle autre
   que 0/90/180/270°, ses coins sortent du contour arrondi de la carte
   (visible seulement là où overflow le permet) → un polygone rose déformé
   au lieu d'un anneau, au lieu de rien.
   Version actuelle : on ne fait plus tourner l'ÉLÉMENT, on fait tourner
   l'ANGLE de départ du conic-gradient lui-même, via une custom property
   enregistrée (@property, seule façon de faire animer un angle par le
   navigateur). Le pseudo-élément reste fixe (même rectangle arrondi que la
   carte, jamais transformé) — seule la couleur qui tourne À L'INTÉRIEUR de
   l'anneau bouge. Fonctionne comme avant : border transparent réserve
   l'épaisseur de l'anneau, deux fonds superposés — le dégradé en
   border-box (visible uniquement DANS l'épaisseur du border), la couleur
   de la carte en padding-box par-dessus (couvre le centre).
   Bug constaté en ligne (2026-08-20, corrigé n°3) : une fois --ctp/opacity
   à 1, la carte devenait toute blanche — icône, titre et bouton disparus.
   Cause : ::before est en position: absolute avec z-index: auto, donc par
   les règles de l'ordre d'empilement CSS, un descendant positionné se peint
   TOUJOURS au-dessus du contenu en flux normal non positionné (icône,
   titre, bouton), même s'il est déclaré avant eux dans le DOM — l'ordre du
   HTML ne joue aucun rôle ici. ::before masquait donc tout le contenu de la
   carte. Correction : z-index: -1 le fait peindre juste après le fond
   propre de .cta-card (qui crée son propre contexte d'empilement, via le
   transform posé par js/cta-dots.js) mais avant tout le contenu en flux —
   l'anneau reste bien derrière le texte et le bouton, sans jamais sortir
   du contexte de la carte. */
@property --cta-ring-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

.cta-card::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: var(--radius-lg);
  border: 2px solid transparent;
  --cta-ring-angle: 0deg;
  background:
    linear-gradient(var(--color-surface), var(--color-surface)) padding-box,
    conic-gradient(from var(--cta-ring-angle), transparent 0deg, var(--color-primary) 55deg, transparent 130deg, transparent 360deg) border-box;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.6s ease;
}

.cta-card.is-locked::before {
  opacity: 1;
  animation: cta-card-ring-spin 4s linear infinite;
}

@keyframes cta-card-ring-spin {
  to {
    --cta-ring-angle: 360deg;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cta-card.is-locked::before {
    animation: none;
  }
}

/* Pas de lueur qui suit le curseur sur cette carte (2026-08-20) — présente
   par défaut car héritée de .benefit-card (js/benefits.js écoute le
   mousemove sur toutes les cartes, y compris celle-ci, pour la §2 "lueur
   au survol"). On la neutralise ici en CSS plutôt que de toucher au JS
   partagé avec les cartes "bienfaits" (01/02/03, qui la gardent). */
.cta-card .benefit-card__spot {
  display: none;
}

/* Entrée depuis hors écran, verrouillée (2026-08-20) — --ctp (0 → 1) posé
   par js/cta-dots.js, PAS par js/benefits.js (cette carte en est
   volontairement exclue, voir son commentaire "entranceCards") : contrairement
   aux cartes "bienfaits" (réversibles, on peut les voir repartir en arrière
   en remontant), --ctp se fige à 1 une fois la carte en place et ne bouge
   plus, quel que soit le scroll ensuite. Décalage de départ beaucoup plus
   grand (90dvh, hors écran — "comme si elle venait de sous le footer") que
   les 70-90px des cartes bienfaits.
   Spécificité doublée (.cta-card.benefit-card) pour l'emporter sur
   .benefit-card { opacity: var(--p); } (css/benefits.css) quel que soit
   l'ordre des feuilles de style — --p n'est de toute façon plus posé sur
   cette carte, mais mieux vaut ne pas en dépendre.
   --ctp par défaut à 1 (carte visible, en place) — PAS 0 (2026-08-20,
   correctif) : si js/cta-dots.js échoue à s'exécuter pour une raison ou
   une autre (bloqué, erreur, navigateur récalcitrant — carte invisible
   constatée sous Firefox), la carte reste visible par défaut plutôt que de
   disparaître définitivement. C'est le script qui bascule --ctp à 0 pour
   démarrer l'entrée hors écran — amélioration progressive, jamais l'inverse. */
.cta-card {
  --ctp: 1;
  transform: translateY(calc((1 - var(--ctp)) * 90dvh));
}

.cta-card.benefit-card {
  opacity: var(--ctp);
}

@media (prefers-reduced-motion: reduce) {
  .cta-card {
    transform: none;
  }

  .cta-card.benefit-card {
    opacity: 1;
  }
}

.cta-card .benefit-card__title {
  margin-bottom: 0;
}

.cta-card .btn {
  margin-top: var(--space-xs);
}

/* Trait d'accent (.benefit-card__accent) supprimé sur cette carte
   (2026-08-20) — devenu redondant avec l'anneau rose qui tourne autour de
   la carte une fois verrouillée (voir §"CTA carte — bordure animée") : les
   deux au hover donnaient un résultat chargé. Gardé tel quel sur les
   cartes "bienfaits" (01/02/03, css/benefits.css, non touchées). */
.cta-card .benefit-card__accent {
  display: none;
}

/* Pas de rotation au survol sur cette carte (2026-08-20) — seule la taille
   change (scale), contrairement aux cartes "bienfaits" (01/02/03, qui
   gardent leur rotate(-8deg), non touchées). Sélecteur à spécificité plus
   élevée (.benefit-card.cta-card) pour l'emporter sur la règle de base
   (.benefit-card:hover .benefit-card__icon, css/benefits.css) quel que
   soit l'ordre des feuilles de style. */
.benefit-card.cta-card:hover .benefit-card__icon {
  transform: scale(1.12);
}

.section-title {
  text-align: center;
  /* Voir variables.css § ÉCHELLE LAPTOP. */
  margin-bottom: var(--space-title-gap);
}

.section-title__eyebrow {
  /* Police "signature" (écho de "Handsome Pro" dans la charte cliente) :
     touche manuscrite au-dessus des titres condensés en majuscules —
     ajoutée le 2026-08-21 en appliquant la vraie charte de couleurs/typos. */
  display: block;
  font-family: var(--font-script);
  font-size: clamp(1.5rem, 1.1rem + 1.4vw, 2.1rem);
  font-weight: 600;
  letter-spacing: 0.01em;
  text-transform: none;
  /* Token dédié (voir variables.css) : le rose VIF de la charte en thème
     clair, sa déclinaison CLAIRE en thème sombre — à cette taille de
     script, le rose vif reste sourd sur un fond sombre alors que le rose
     clair s'y lit comme une signature lumineuse. */
  color: var(--color-eyebrow);
  margin-bottom: var(--space-xs);
  /* Sur les pages prestation, ce sur-titre EST le <h1> (2026-09-07, SEO :
     c'est lui qui porte le mot-cle, plus l'accroche en dessous). La regle
     h1..h4 imposerait --line-height-heading ; `inherit` reproduit
     exactement ce que faisait le <span>, quel que soit le conteneur. */
  line-height: inherit;
}

/* Titre principal des sections (eyebrow + h2) : agrandi par rapport au h2
   par défaut du site pour bien marquer chaque section. */
.section-title h2 {
  font-size: var(--font-size-3xl);
}

/* ---- Boutons ---- */
.btn {
  display: inline-block;
  padding: 0.9em 1.8em;
  border-radius: var(--radius-lg);
  border: none;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--font-size-base);
  transition: transform var(--transition-base), box-shadow var(--transition-base);
}

.btn:hover {
  text-decoration: none;
  transform: translateY(-2px);
  box-shadow: var(--shadow-soft);
}

/* Historique du 2026-08-21 : d'abord repassé en outline (contour rose,
   fond blanc, texte rose) suite à "boutons roses trop marqués" ; un essai
   "liquid glass" a suivi puis a été annulé. Puis fond rose pâle de la
   charte (--color-secondary), texte noir/neutre, sans bordure — encore
   plus doux que l'outline.
   Passage au thème sombre (2026-08-21) : le bouton reste un aplat rose
   pâle, mais son encre passe de var(--color-text) — devenu crème, donc
   illisible sur rose pâle — à var(--color-text-on-secondary), le vert
   très sombre prévu exactement pour ça. Le rendu voulu est le même qu'en
   clair (rose pâle + écriture sombre), sauf qu'il pose maintenant une
   pastille lumineuse sur le fond vert profond. */
/* INVERSION DES DEUX ÉTATS le 2026-08-25, à la demande de Rom : ce qui
   était le survol devient le repos, et inversement. Le CTA se présente
   donc désormais en rose VIF (l'aplat rose pâle passe au survol), ce qui
   renverse la décision du 2026-08-21 ("boutons roses trop marqués") — si
   quelqu'un s'étonne de la contradiction avec l'historique ci-dessus, elle
   est volontaire. Seules les COULEURS sont échangées : le léger
   soulèvement + ombre de .btn:hover reste au survol, un bouton doit
   toujours réagir quand on le vise. */
.btn--primary {
  background-color: var(--color-primary);
  color: var(--color-text-on-primary);
  border: none;
}

.btn--primary:hover {
  background-color: var(--color-secondary);
  color: var(--color-text-on-secondary);
  /* Filet fin au survol (2026-08-25, demande de Rom). Posé en box-shadow
     INSET et non en `border` : le bouton est sans bordure au repos, une
     vraie bordure de 1px l'élargirait de 2px au survol et ferait sauter
     son libellé — l'ombre intérieure, elle, ne prend aucune place. Elle
     est de la même encre que le texte du bouton, donc noire à l'œil dans
     les deux thèmes, et se fond dans la transition d'ombre déjà déclarée
     sur .btn. L'ombre douce de .btn:hover doit être RÉPÉTÉE ici : une
     déclaration box-shadow remplace la précédente, elle ne s'y ajoute
     pas. */
  box-shadow: inset 0 0 0 1px var(--color-text-on-secondary), var(--shadow-soft);
}

.btn--outline {
  background-color: transparent;
  color: var(--color-primary-dark);
  border: 2px solid var(--color-primary);
}

/* ---- Cards ---- */
.card {
  background-color: var(--color-surface);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-soft);
  padding: var(--space-md);
}

/* ---- Grid utilitaire ---- */
.grid {
  display: grid;
  gap: var(--space-md);
}

.grid--3 {
  grid-template-columns: repeat(3, 1fr);
}

/* Variante 2 colonnes (2026-09-02, page Soins INDIBA — bloc "Des
   résultats visibles" / "Un protocole sur-mesure") : mêmes cartes
   .benefit-card que "Les bienfaits" (voir css/benefits.css), réutilisées
   ici sans les modificateurs --featured/--sub (réservés à la grille bento
   à 3 cartes) ni data-direction (ces deux cartes n'ont pas de sens
   d'arrivée dédié, elles apparaissent simplement en fondu au scroll,
   piloté par le même js/benefits.js). */
.grid--2 {
  grid-template-columns: repeat(2, 1fr);
}

@media (max-width: 900px) {
  .grid--3,
  .grid--2 {
    grid-template-columns: 1fr;
  }

  h1 { font-size: var(--font-size-2xl); }
  h2 { font-size: var(--font-size-xl); }
}

/* ---- Hero (accueil) ---- */
.hero__inner {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: var(--space-xl);
  align-items: center;
}

.hero__actions {
  display: flex;
  gap: var(--space-sm);
  margin-top: var(--space-md);
  flex-wrap: wrap;
}

.hero__media {
  min-height: 340px;
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-soft);
}

.hero__media img {
  width: 100%;
  height: 100%;
  min-height: 340px;
  object-fit: cover;
}

/* ---- À propos ---- */
.a-propos__inner {
  display: grid;
  grid-template-columns: 1fr 1.1fr;
  gap: var(--space-xl);
  align-items: center;
}

.a-propos__media {
  min-height: 320px;
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-soft);
}

.a-propos__media img {
  width: 100%;
  height: 100%;
  min-height: 320px;
  object-fit: cover;
}

/* ---- Cartes prestations ---- */
.prestation-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  color: var(--color-text);
}

.prestation-card:hover {
  text-decoration: none;
}

.prestation-card__link {
  color: var(--color-primary-dark);
  font-weight: 600;
  margin-top: auto;
}

.prestation-card__media {
  border-radius: var(--radius-sm);
  overflow: hidden;
  aspect-ratio: 4 / 3;
}

.prestation-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--transition-base);
}

.prestation-card:hover .prestation-card__media img {
  transform: scale(1.04);
}

/* ---- Avis ---- */
.avis-card {
  margin: 0;
  font-style: italic;
}

/* ---- Booking ---- */
.booking__inner {
  text-align: center;
}

.booking__iframe-placeholder {
  min-height: 420px;
}

/* ---- Pages prestations (laser / indiba / cils) ---- */
.page-hero {
  text-align: center;
  padding-top: var(--space-xl);
}

/* Accroche du hero prestation. Ce texte etait le <h1> jusqu'au 2026-09-07 ;
   le <h1> est desormais le sur-titre manuscrit juste au-dessus. La classe
   recopie a l'identique les styles de h1 (regle h1..h4 et `h1 { font-size }`
   plus haut) : rendu verifie identique au pixel, desktop et mobile.
   /!\ La bascule mobile doit rester JUSTE EN DESSOUS : la media query
   `h1 { font-size: 2xl }` (@900px) est declaree plus HAUT dans ce fichier,
   donc a specificite egale elle perdrait contre la regle ci-dessous. */
.page-hero__title {
  font-family: var(--font-heading);
  font-weight: 500;
  line-height: var(--line-height-heading);
  color: var(--color-heading);
  margin: 0 0 var(--space-sm);
  font-size: var(--font-size-3xl);
}

@media (max-width: 900px) {
  .page-hero__title { font-size: var(--font-size-2xl); }
}

.page-hero__media {
  min-height: 260px;
  margin-top: var(--space-lg);
}

/* ---- La même case, une fois la vraie photo arrivée ----
   (page laser, 2026-08-25 ; les deux autres pages attendent encore leur
   cliché et gardent le placeholder juste au-dessus.)
   Le bloc n'est plus un aplat en attente : il EST la photo, recadrée en
   bandeau large. La source est un 4/3 — la pièce photographiée en entier —
   dont on ne garde ici qu'une bande : plafond et haut des étagères sortent
   du cadre. Le point de mire est bas (62 %) parce que le sujet l'est : à
   cette hauteur la table de soin est entière et l'appareil laser repose
   sur ses roulettes ; seul le haut de son bras articulé est coupé, ce qui
   se voit beaucoup moins qu'un appareil flottant sans son sol.
   (Bandeau descendu de 16/9 à 21/9 le 2026-08-25 à la demande de Rom : en
   16/9 la photo occupait presque tout l'écran sous le titre.) */
.page-hero__media--photo {
  min-height: 0;
  margin: var(--space-lg) 0 0;
  aspect-ratio: 21 / 9;
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-soft);
}

.page-hero__media--photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 62%;
}

/* indiba_hero.webp (2026-09-02) — la photo de la page Soins INDIBA cadre
   ses deux sujets (la praticienne ET la cliente allongée) plus haut dans
   l'image que la photo laser : l'ancre par défaut à 62% coupe le visage
   de la praticienne en 21/9. Testé à plusieurs hauteurs (25/38/45/62 %),
   25 % est la seule qui garde les deux visages ET l'écran de l'appareil
   INDIBA dans le bandeau desktop. Règle plus spécifique
   (sélecteur d'attribut) que .page-hero__media--photo img ci-dessus,
   l'emporte sans !important. */
.page-hero__media--photo img[src$="indiba_hero.webp"] {
  object-position: 50% 25%;
}

/* laser_hero.webp (2026-09-02, REMPLACÉE le même jour — Rom a supprimé
   la 1ʳᵉ version, "le rendu n'était pas bon", et livré une nouvelle photo
   sous le même nom). Toujours PORTRAIT (1600×2000, 4:5), mais scène très
   différente : plan resserré sur la pièce à main du laser appliquée sur
   l'avant-bras de la cliente allongée, main de la praticienne visible
   (manche rayée), fond rose flou avec l'appareil en arrière-plan — un vrai
   plan d'action, plus proche en esprit de indiba_hero.webp que de
   l'ancienne version. Même géométrie de recadrage que la 1ʳᵉ version (cover
   cale sur la LARGEUR, ~34 % de la hauteur source visible en 21/9) mais
   ancre différente puisque le sujet a changé : testé à 10/20/30/40/50/62 %,
   retenue **40 %** — c'est la seule qui centre le point de contact
   pièce-à-main/peau (le "moment du soin") tout en gardant la main et la
   manche de la praticienne en haut du cadre. En dessous de 30 % le point
   de contact sort quasiment du cadre ; au-delà de 50 % la manche
   disparaît et le cadrage perd son repère humain. */
.page-hero__media--photo img[src$="laser_hero.webp"] {
  object-position: 50% 40%;
}

/* regard_hero.webp (2026-09-02) — page beauté du regard : pose d'extension
   de cil, la praticienne (pince en main) penchée sur le visage d'une
   cliente allongée, tête vers le haut du cadre. Ancre par défaut à 62 %
   ne montrerait que le haut du visage de la praticienne (le geste et le
   visage de la cliente sortiraient du bandeau) : testé à
   0/15/30/45/62/80/90/100 %, retenue 90 %, la seule qui garde le visage
   entier de la cliente ET le geste (pince + patch sous l'œil) ET une
   bonne partie du visage souriant de la praticienne en 21/9. */
.page-hero__media--photo img[src$="regard_hero.webp"] {
  object-position: 50% 90%;
}

/* Sur mobile, le bandeau deviendrait une fente : on rend la photo
   entière, dans son format d'origine. */
@media (max-width: 700px) {
  .page-hero__media--photo { aspect-ratio: 4 / 3; }
}

.pricing-table {
  width: 100%;
  border-collapse: collapse;
  background-color: var(--color-surface);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-soft);
}

.pricing-table th,
.pricing-table td {
  text-align: left;
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--color-border);
}

.pricing-table th {
  background-color: var(--color-bg-alt);
  font-family: var(--font-heading);
  font-weight: 500;
}

.pricing-table tr:last-child td {
  border-bottom: none;
}

.consult-note {
  background-color: var(--color-bg-alt);
  border-left: 4px solid var(--color-primary);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-lg);
}

/* ---- Étapes (déroulé d'une séance — pages prestations) ---- */
.steps {
  display: grid;
  gap: var(--space-lg);
  counter-reset: step;
}

.step {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-md);
  align-items: start;
}

.step__num {
  counter-increment: step;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.5em;
  height: 2.5em;
  flex-shrink: 0;
  border-radius: 50%;
  background-color: var(--color-primary);
  color: var(--color-text-on-primary);
  font-family: var(--font-heading);
  font-size: var(--font-size-lg);
}

.step__num::before {
  content: counter(step);
}

.step__title {
  margin-bottom: 0.2em;
}

.step p:last-child {
  margin-bottom: 0;
}

@media (max-width: 900px) {
  .steps {
    gap: var(--space-md);
  }
}

/* ---- FAQ (accordéon natif <details>/<summary> — pages prestations) ----
   Ouverture/fermeture animées (2026-08-20, js/faq.js) : technique reprise
   d'obermann-webdesign.de (SvelteKit — leur FAQ utilise le système de
   transition intégré à Svelte, PAS une librairie externe séparément
   installable : compilé dans leur bundle, donc rien à ajouter ici non
   plus). L'algorithme équivalent est réimplémenté à la main : au clic, une
   seule boucle requestAnimationFrame anime .faq__content de 0 à sa hauteur
   naturelle (mesurée une fois via scrollHeight, pas remesurée à chaque
   frame) avec un easing cubique — tout se passe en JS, .faq__content n'a
   donc aucune transition CSS sur height (qui ferait double emploi et
   entrerait en conflit avec la boucle JS). Comme sur le site de référence
   (qui utilise aussi Lenis), js/faq.js met Lenis en pause le temps de
   l'animation pour qu'il ne recalcule pas ses bornes de scroll en pleine
   transition. */
.faq {
  display: grid;
  gap: var(--space-sm);
}

.faq__item {
  background-color: var(--color-surface);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-soft);
  padding: var(--space-sm) var(--space-md);
}

.faq__item summary {
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: var(--font-size-base);
  padding: var(--space-xs) 0;
}

.faq__item summary::-webkit-details-marker {
  display: none;
}

.faq__item summary::after {
  content: '+';
  font-family: var(--font-body);
  font-size: var(--font-size-xl);
  line-height: 1;
  color: var(--color-primary-dark);
  transition: transform var(--transition-base);
  flex-shrink: 0;
}

.faq__item.is-open summary::after {
  transform: rotate(45deg);
}

/* Hauteur pilotée entièrement par js/faq.js (pas de transition CSS ici,
   volontairement — voir commentaire plus haut). overflow: hidden reste
   permanent, y compris au repos, pour que la mesure scrollHeight soit
   toujours fiable indépendamment de la hauteur fixée par le JS. */
.faq__content {
  height: 0;
  overflow: hidden;
}

.faq__content > p {
  margin: var(--space-xs) 0 0;
  color: var(--color-text);
}

@media (prefers-reduced-motion: reduce) {
  .faq__item summary::after {
    transition: none;
  }
}

/* ---- Listes "à savoir" / précautions (pages prestations) ---- */
.precautions-list {
  display: grid;
  gap: var(--space-xs);
}

.precautions-list li {
  position: relative;
  padding-left: 1.4em;
}

.precautions-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: var(--color-primary);
}

@media (max-width: 900px) {
  .hero__inner,
  .a-propos__inner {
    grid-template-columns: 1fr;
  }
}

/* ---- Placeholder visuel (en attendant les photos définitives) ---- */
/* Thème sombre (2026-08-21) : le dégradé rose pâle → crème d'origine
   devenait un aplat très clair au milieu des sections sombres. Remplacé
   par un dégradé entre les deux fonds sombres, relevé d'un voile rose
   très dilué pour garder la teinte de la charte. */
.img-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Dégradé doux entre les deux tons pastel de la charte. En sombre il
     devient un aplat très clair au milieu des sections sombres : voir les
     surcharges en fin de fichier. */
  background: linear-gradient(135deg, var(--color-secondary), var(--color-bg-alt));
  border: 1px solid var(--color-border);
  color: var(--color-text-light);
  font-family: var(--font-body);
  font-size: var(--font-size-sm);
  border-radius: var(--radius-md);
  min-height: 220px;
  text-align: center;
  padding: var(--space-sm);
}

/* ==========================================================================
   SURCHARGES DU THÈME SOMBRE
   Voir l'explication en tête du même bloc dans home.css : ne vivent ici que
   les réglages qu'aucun token ne peut porter.
   ========================================================================== */

/* Le dégradé rose pâle → crème des visuels manquants deviendrait un aplat
   très clair au milieu des sections sombres : on le remplace par un
   dégradé entre les deux fonds du thème, relevé d'un voile rose très
   dilué pour garder la teinte de la charte. */
:root[data-theme="dark"] .img-placeholder {
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--color-primary) 12%, var(--color-surface)) 0%,
      var(--color-bg-alt) 100%);
}

/* ==========================================================================
   ÉCHELLE LAPTOP — SUR-TITRES MANUSCRITS (2026-08-27)
   ————————————————————————————————————————————————————————————————————
   Caveat porte de longues hampes : à l'interlignage hérité du corps de
   texte (1.6), un sur-titre de 33,6 px occupe 54 px de haut. Sur un écran
   de bureau c'est de la respiration ; sur un laptop, avec deux sur-titres
   empilés dans la section Réservation (un par carte) plus un par section
   ailleurs, c'est du vide qui s'additionne.
   1.35 laisse la place aux descendantes (le « j » de « Qui suis-je », le
   « p » de « Nos réponses ») — vérifié à 606 et 638 px de viewport. Ne pas
   descendre plus bas sans revérifier ces deux mots.
   Gardé sous min-width: 761px : le mobile a déjà son échelle.
   MARQUE : RESERVATION-LAPTOP-2026-08-27
   ========================================================================== */
@media (min-width: 761px) and (max-height: 760px) {
  .section-title__eyebrow { line-height: 1.35; }
}

/* Bouton présenté comme un lien dans le corps d'un texte (2026-09-06) —
   utilisé par « gérer mes cookies » dans confidentialite.html. Un <button>
   et non un <a href="#"> : il déclenche une action, il ne navigue pas. */
.lien-bouton {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  cursor: inherit;
  color: var(--color-primary-dark);
  text-decoration: underline;
  text-underline-offset: 0.18em;
}

.lien-bouton:hover { color: var(--color-primary); }
