/* ==========================================================================
   Styles pour les Web Components réutilisés sur toutes les pages
   (site-header, site-footer). Ces éléments sont en light DOM, donc stylés
   ici comme du CSS classique.
   ========================================================================== */

/* ---- site-header ---- */
.site-header {
  display: block;
  position: sticky;
  top: 0;
  /* 295 depuis le 2026-09-06 — CORRECTIF. Était à 100, donc SOUS
     .institut-video__pin (290) : pendant tout le couloir de « L'institut »,
     le bloc épinglé se peignait PAR-DESSUS la barre, qui paraissait
     transparente (on voyait la vidéo à travers). Le défaut était déjà
     documenté dans le commentaire de .institut-video__pin, mais seule
     l'interception des clics avait été corrigée le 2026-09-03
     (pointer-events), pas l'ordre d'affichage.
     LA FENÊTRE EST ÉTROITE, ne pas la changer à la légère :
       > 290  pour passer au-dessus du pin de « L'institut » ;
       < 300  pour rester SOUS le badge Instagram (.hero-x__badge) et la
              barre de progression, le badge devant rester au-dessus de la
              vidéo (décision du 2026-08-17) ;
       < 400  pour rester sous le grain, < 500 sous le curseur personnalisé.
     Tout nouvel élément flottant doit se placer par rapport à ces bornes. */
  z-index: 295;
}

.site-header__bar {
  background-color: var(--color-bg);
  border-bottom: 1px solid var(--color-border);
}

.site-header__inner {
  display: flex;
  align-items: center;
  /* Pas de justify-content ici : l'écartement est fait par le
     margin-right:auto du logo (voir plus bas). Une marge auto consomme
     toute la place libre avant que justify-content n'ait son mot à dire —
     en remettre un ne ferait qu'induire en erreur. Ordre dans la barre :
     interrupteur · logo — · · · — navigation (ou burger sur mobile). */
  padding-top: var(--space-sm);
  padding-bottom: var(--space-sm);
}

.site-header__logo {
  display: flex;
  align-items: center;
  /* C'est LE LOGO qui pousse tout le reste en bout de barre depuis que
     l'interrupteur est passé à sa gauche (2026-08-26) : la navigation sur
     grand écran, le burger sur mobile. Avant, ce rôle était tenu par le
     margin-left:auto de .theme-switch. */
  margin-right: auto;
}

.site-header__logo:hover {
  text-decoration: none;
}

/* ---- Le logo manuscrit dans la barre (2026-08-26, encre at-top inversée
   le 2026-09-03) ----
   Le mot "AlyCare" composé en Big Shoulders a été remplacé par le vrai
   logo de la cliente (assets/images/logo_top_black.webp). Il existe en
   deux fichiers jumeaux, encre sombre et encre claire — le trait est du
   noir pur sur transparence, le blanc a été fabriqué à partir du noir en
   ne touchant qu'aux canaux RVB, le canal alpha est identique au pixel
   près. Les deux <img> sont dans le DOM en permanence (voir la note dans
   site-header.js), le CSS ci-dessous n'en montre qu'une.

   QUELLE ENCRE, QUAND — trois cas et non deux :
   1. thème clair, barre décollée (body:not(.at-top)) → encre sombre ;
   2. thème sombre, barre décollée (body:not(.at-top)) → encre claire ;
   3. accueil, tout en haut (body.at-top)              → encre CLAIRE dans
      les deux thèmes depuis le 2026-09-03 (demande de Rom) : la barre y
      flotte au-dessus de la photo du hero, et le reste de son contenu
      (liens, "Découvrir") est passé en blanc à la même date — voir
      home.css §4 et §"Hero". Avant cette date c'était l'inverse (encre
      SOMBRE dans les deux thèmes, --color-on-light) : la photo du hero
      est claire quel que soit le thème, mais Rom a demandé du blanc pur
      malgré tout.
   D'où le `body:not(.at-top)` dans les deux premières règles : sans lui,
   l'encre sombre du thème clair (cas 1) s'appliquerait aussi en haut de
   l'accueil, par-dessus l'encre claire voulue pour ce cas.
   NEUTRALISÉ SOUS 760px (mobile) pour le cas 3 en thème clair — voir
   home.css, le bloc `@media (max-width: 760px)` qui neutralise les effets
   de .at-top : la barre y garde son fond opaque même en haut de page, donc
   l'encre sombre normale doit y rester. Le cas 3 en thème sombre, lui, était
   déjà neutralisé là (5ᵉ effet listé). */
.site-header__logo-img {
  display: block;
  /* C'est CE RÉGLAGE qui décide de la hauteur de toute la barre (le logo
     est son élément le plus haut). Un mot manuscrit à l'anglaise a une
     hauteur d'x minuscule par rapport à ses boucles : sous ~45px il ne se
     lit plus, il devient un gribouillis — d'où une barre un peu plus haute
     qu'au temps du mot composé en Big Shoulders (85px contre 73px sur
     grand écran). Le clamp rend la barre à sa hauteur d'origine sur les
     petits écrans, là où la place manque vraiment.
     Pour agrandir ou réduire le logo, il n'y a que ces trois valeurs à
     toucher — le reste de la barre suit. */
  height: clamp(38px, 3.6vw, 52px);
  width: auto;
}

.site-header__logo-img--ink-light {
  display: none;
}

:root[data-theme="dark"] body:not(.at-top) .site-header__logo-img--ink-dark,
body.at-top .site-header__logo-img--ink-dark {
  display: none;
}

:root[data-theme="dark"] body:not(.at-top) .site-header__logo-img--ink-light,
body.at-top .site-header__logo-img--ink-light {
  display: block;
}

.site-header__nav {
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

.site-header__list {
  display: flex;
  gap: var(--space-md);
}

.site-header__link {
  color: var(--color-text);
  font-size: var(--font-size-sm);
  padding: 0.4em 0;
  border-bottom: 2px solid transparent;
}

.site-header__link:hover,
.site-header__link.is-active {
  color: var(--color-primary-dark);
  border-bottom-color: var(--color-primary);
  text-decoration: none;
}

.site-header__cta {
  font-size: var(--font-size-sm);
  padding: 0.6em 1.4em;
}

/* ---- « Nous suivre sur Instagram » : MENU BURGER UNIQUEMENT (2026-08-26) ----
   Masqué par défaut, donc absent de la barre horizontale du desktop, où le
   compte est déjà présenté par le badge circulaire du hero (.hero-x__badge,
   home.css) — lequel est justement caché sous 640 px. Les deux se relaient,
   ils ne coexistent jamais. Le lien porte aussi .site-header__link : il
   hérite donc de tout le traitement du menu mobile (bande de 48 px, filet,
   liseré à gauche au survol) sans une ligne de plus. */
.site-header__social-item {
  display: none;
}

.site-header__social-icon {
  width: 1.15em;
  height: 1.15em;
  /* Ne rétrécit pas si le libellé passe sur deux lignes. */
  flex: 0 0 auto;
  margin-right: 0.65em;
  /* Le tracé est en currentColor : c'est donc cette couleur-ci qu'il suit,
     et non celle du texte du lien. Rose de la charte, pour distinguer d'un
     coup d'œil ce lien SORTANT des quatre onglets de navigation. */
  color: var(--color-primary);
}

/* ---- Interrupteur clair / sombre ----
   Fabriqué par js/components/site-header.js, câblé par js/theme.js.
   Son état visuel ne dépend PAS d'une classe posée en JS mais de
   data-theme sur <html> : le curseur est donc toujours du bon côté, même
   si le JS tarde ou échoue. Le rail est un vrai bouton (focus clavier,
   role="switch"), volontairement discret : il ne doit pas concurrencer le
   CTA "Prendre rendez-vous" à l'autre bout de la barre.
   PLACÉ TOUT À GAUCHE, AVANT LE LOGO, depuis le 2026-08-26 (demande de
   Rom). C'est l'ordre du DOM qui le décide (voir site-header.js) : il n'a
   donc plus de margin-left:auto, seulement de quoi respirer avant le
   logo. Le calage du reste de la barre est passé sur .site-header__logo. */
.theme-switch {
  margin-right: var(--space-sm);
  padding: 6px;
  background: none;
  border: 0;
  display: flex;
  align-items: center;
  border-radius: 999px;
}

.theme-switch__track {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  width: 58px;
  height: 28px;
  padding: 0 7px;
  border-radius: 999px;
  border: 1px solid var(--color-border);
  background: color-mix(in srgb, var(--color-text) 6%, transparent);
  transition: background-color var(--transition-base), border-color var(--transition-base);
}

.theme-switch:hover .theme-switch__track { border-color: var(--color-primary); }

/* Anneau de focus visible au clavier uniquement (pas au clic souris). */
.theme-switch:focus-visible .theme-switch__track {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
}

/* Les deux icônes sont posées AU-DESSUS de la pastille (z-index) : celle du
   thème en cours se retrouve donc sur la pastille colorée et prend son
   encre de contraste ; l'autre reste sur le rail, atténuée. On lit d'un
   coup d'œil où l'on est, et où l'on peut aller. */
.theme-switch__icon {
  position: relative;
  z-index: 1;
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  opacity: 0.5;
  transition: opacity var(--transition-base), stroke var(--transition-base), fill var(--transition-base);
}

.theme-switch__icon--moon { fill: currentColor; stroke: none; }

/* Thème clair : la pastille est rose vif, à gauche, sous le soleil. */
:root:not([data-theme="dark"]) .theme-switch__icon--sun {
  opacity: 1;
  stroke: var(--color-text-on-primary);
}

/* Thème sombre : la pastille est rose pâle, à droite, sous la lune. */
:root[data-theme="dark"] .theme-switch__icon--moon {
  opacity: 1;
  fill: var(--color-text-on-secondary);
}

/* La pastille. Elle se cale sous l'icône du thème EN COURS (soleil à
   gauche en clair, lune à droite en sombre) — c'est l'indicateur d'état,
   quand les icônes, elles, indiquent la destination. */
.theme-switch__knob {
  position: absolute;
  top: 50%;
  left: 3px;
  width: 20px;
  height: 20px;
  margin-top: -10px;
  border-radius: 50%;
  background: var(--color-primary);
  transition: transform 0.32s var(--ease-out, cubic-bezier(0.22, 0.7, 0.24, 1)),
              background-color var(--transition-base);
}

:root[data-theme="dark"] .theme-switch__knob {
  transform: translateX(28px);
  background: var(--color-secondary);
}

@media (prefers-reduced-motion: reduce) {
  .theme-switch__knob { transition: none; }
}

/* Sur la photo du hero (en-tête transparent), l'interrupteur prend la même
   encre que le reste de la barre : --color-on-light, sombre depuis que la
   photo du hero est claire et sans voile (2026-08-25). */
body.at-top .site-header .theme-switch { color: var(--color-on-light); }
body.at-top .site-header .theme-switch__track {
  border-color: color-mix(in srgb, var(--color-on-light) 45%, transparent);
  background: color-mix(in srgb, var(--color-on-light) 12%, transparent);
}

.site-header__toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  padding: 8px;
}

.site-header__toggle span {
  width: 24px;
  height: 2px;
  background-color: var(--color-text);
  display: block;
}

@media (max-width: 900px) {
  /* Logo CENTRÉ dans la barre dès que la navigation se replie derrière le
     burger (2026-08-26, demande de Rom). En grille à trois colonnes plutôt
     qu'en marges automatiques : `margin-inline: auto` centrerait le logo
     dans la place LIBRE, donc décalé de 13 px vers la droite, l'interrupteur
     (70 px) étant plus large que le burger (44 px). Deux colonnes latérales
     en 1fr donnent au contraire un vrai centre de barre, quelles que soient
     les largeurs de part et d'autre.
     Grille et non position: absolute, exprès : le logo reste DANS le flux,
     donc il continue de commander la hauteur de la barre (voir
     .site-header__logo-img, c'est l'élément le plus haut). En le sortant du
     flux, la barre se serait recalée sur l'interrupteur.
     La navigation, elle, est en position: absolute juste en dessous : elle
     ne consomme donc aucune cellule de cette grille (un élément hors flux
     n'est pas placé par la grille), et son panneau reste pleine largeur —
     il se cale sur <site-header>, pas sur cette boîte. */
  .site-header__inner {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
  }

  .site-header__logo {
    /* Le margin-right: auto du desktop (c'est lui qui pousse la navigation
       en bout de barre) n'a plus lieu d'être : ici c'est la grille qui
       place les trois éléments. */
    margin-right: 0;
    justify-self: center;
  }

  /* justify-self OBLIGATOIRE ici, ce n'est pas de la coquetterie : un
     élément de grille s'étire par défaut sur toute sa colonne. Sans cette
     ligne, le <button> de l'interrupteur mesurait 354 px de large sur un
     écran de 880 (au lieu de 70) — le rail restait à sa place, mais TOUTE
     la moitié gauche de la barre devenait cliquable et basculait le thème.
     Mesuré, pas supposé. */
  .theme-switch {
    justify-self: start;
  }

  .site-header__toggle {
    display: flex;
    justify-self: end;
  }

  .site-header__nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background-color: var(--color-bg);
    border-bottom: 1px solid var(--color-border);
    flex-direction: column;
    align-items: flex-start;
    padding: var(--space-md);
    display: none;
  }

  .site-header__nav.is-open {
    display: flex;
  }

  .site-header__list {
    flex-direction: column;
    gap: 0;
    width: 100%;
  }

  /* Cibles tactiles (2026-08-26, passe « version mobile ») : les liens du
     menu mesuraient 29 px de haut pour 45 px de large — on visait le MOT,
     pas une zone. Chaque lien devient une bande pleine largeur d'au moins
     48 px (recommandation WCAG 2.2 : 44 px minimum), séparée d'un filet.
     Le soulignement de la page courante (border-bottom sur le lien, hérité
     du desktop) ferait doublon avec ces filets : on le déplace sur un
     liseré rose à GAUCHE, qui se lit comme un marqueur de position dans
     une liste verticale. */
  .site-header__list li + li {
    border-top: 1px solid var(--color-border);
  }

  .site-header__link {
    display: flex;
    align-items: center;
    min-height: 48px;
    width: 100%;
    padding: 0.2rem 0 0.2rem var(--space-sm);
    font-size: var(--font-size-base);
    border-bottom: 0;
    border-left: 2px solid transparent;
  }

  .site-header__link:hover,
  .site-header__link.is-active {
    border-bottom-color: transparent;
    border-left-color: var(--color-primary);
  }

  .site-header__social-item {
    display: block;
  }

  /* Le CTA prend toute la largeur, détaché de la liste : c'est l'action
     de la page, pas un cinquième lien de navigation. */
  .site-header__cta {
    width: 100%;
    margin-top: var(--space-md);
    text-align: center;
    padding: 0.85em 1.4em;
  }

  /* Le bouton burger visait 40 x 32 px : on l'amène à 44 x 44 sans changer
     le dessin des trois traits (c'est le padding qui grandit). */
  .site-header__toggle {
    padding: 12px 10px;
  }
}

/* ---- site-footer ---- */
.site-footer {
  display: block;
  position: relative;
  overflow: hidden;
  /* Repassé en clair/pastel (2026-08-21, demande de Rom, suite du même
     geste que .stats dans home.css) : dégradé doux entre les deux tons
     pastel de la charte plutôt que l'ancien dégradé sombre
     primary-dark → color-text. Le logo en filigrane (voir plus bas,
     assets/images/logo_footer.webp) est un trait NOIR sur transparent :
     il n'a jamais eu besoin d'inversion, il se lit d'ailleurs mieux
     maintenant qu'avant (à peine visible en noir sur noir du temps du
     fond sombre).
     Repassé en blanc plat, sans dégradé (même jour, demande de Rom :
     "footer en blanc sans blop") — background uni var(--color-surface). */
  background: var(--color-surface);
  color: var(--color-text);
  /* Pas de margin-top (retiré le 2026-08-13) : chaque section a déjà son
     propre padding-bottom (voir .section dans main.css) — cette marge en
     plus créait un bandeau visible au fond de page (var(--color-bg))
     entre la dernière section et le footer, au lieu d'un enchaînement
     direct (ex. la section "Réservation", au fond sombre, se retrouvait
     décollée du footer, également sombre). */
}

.site-footer__logo-wrap,
.site-footer__inner,
.site-footer__bottom {
  position: relative;
  z-index: 1;
}

/* Logo en filigrane, centré et calé derrière le texte du footer (z-index 0,
   sous .site-footer__inner/.site-footer__bottom qui sont en z-index 1). */
.site-footer__logo-wrap {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
  max-width: 420px;
  z-index: 0;
  pointer-events: none;
}

.site-footer__logo {
  display: block;
  width: 100%;
  height: auto;
  /* assets/images/logo_footer.webp est un trait NOIR sur fond transparent :
     il se lit tel quel sur le footer clair, et disparaît (noir sur noir) en
     thème sombre, où il est inversé — voir les surcharges en fin de
     fichier. Le fichier n'a jamais été retouché, uniquement son rendu ; si
     Aly fournit un jour une version blanche du logo, revoir les deux. */
}

.site-footer__logo--base {
  opacity: 0.12;
}

/* Calque dupliqué en pleine couleur, révélé uniquement dans un cercle
   autour du curseur via un masque radial (voir js/components/site-footer.js
   pour le suivi de --mx/--my). is-active pilote le fondu d'entrée/sortie
   du spot ; inspiré de l'effet cursor-follow sur la vidéo de
   nodmarketing.se. */
.site-footer__logo--spot {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.35s ease;
  -webkit-mask-image: radial-gradient(circle 140px at var(--mx, 50%) var(--my, 50%), #000 0%, #000 55%, transparent 100%);
  mask-image: radial-gradient(circle 140px at var(--mx, 50%) var(--my, 50%), #000 0%, #000 55%, transparent 100%);
}

.site-footer__logo--spot.is-active {
  opacity: 1;
}

.site-footer__inner {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-lg);
  padding: var(--space-xl) var(--space-md) var(--space-lg);
}

.site-footer__brand {
  font-family: var(--font-heading);
  font-size: var(--font-size-lg);
  /* Vert, comme les autres titres du site (2026-08-21). */
  color: var(--color-heading);
  margin-bottom: var(--space-xs);
}

.site-footer__heading {
  font-family: var(--font-body);
  font-size: var(--font-size-sm);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-primary-dark);
  margin-bottom: var(--space-sm);
}

.site-footer a {
  color: var(--color-text);
}

.site-footer a:hover {
  color: var(--color-primary-dark);
}

/* 2 colonnes de 3 lignes : grid-auto-flow: column remplit la 1re colonne
   du haut vers le bas avant de passer à la 2e — avec 6 <li> dans l'ordre
   du HTML (lundi → dimanche), ça donne lundi/mardi/mercredi en colonne 1
   et jeudi/vendredi/samedi & dimanche en colonne 2. */
.site-footer__hours {
  display: grid;
  grid-template-columns: repeat(2, max-content);
  grid-auto-flow: column;
  grid-template-rows: repeat(3, auto);
  column-gap: var(--space-xl);
  row-gap: 4px;
  font-size: var(--font-size-sm);
}

.site-footer__hours li {
  display: flex;
  justify-content: space-between;
  gap: var(--space-md);
}

.site-footer__hours li span:first-child {
  min-width: 6em;
}

.site-footer__bottom {
  border-top: 1px solid var(--color-border);
}

.site-footer__bottom-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  font-size: var(--font-size-sm);
}

.site-footer__legal {
  display: flex;
  gap: var(--space-md);
}

@media (max-width: 900px) {
  .site-footer__inner {
    grid-template-columns: 1fr 1fr;
  }
}

@media (max-width: 600px) {
  /* minmax(0, 1fr) et non 1fr (2026-08-26, passe « version mobile ») :
     une piste `1fr` vaut minmax(auto, 1fr) et ne descend JAMAIS sous la
     largeur minimale de son contenu. Les horaires (deux colonnes de
     max-content + libellés en min-width: 6em) mesuraient 424 px de
     min-content : toute la grille du footer faisait donc 424 px de large
     dans un écran de 390, et le texte des trois colonnes filait sous le
     bord droit — masqué par l'overflow: hidden de <site-footer>, donc
     silencieusement illisible (« INDIBA®, beauté du regard » coupé, la
     colonne jeudi-dimanche entièrement hors écran).
     Les deux règles qui suivent (min-width: 0 sur les colonnes, horaires
     sur une seule colonne) sont la vraie correction : sans elles, changer
     le nombre de colonnes ne sert à rien. */
  .site-footer__inner {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-md);
  }

  .site-footer__col {
    min-width: 0;
  }

  .site-footer__hours {
    grid-auto-flow: row;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
    row-gap: 6px;
    /* Sans borne, "Lundi ......... 10h – 19h" s'étirerait sur toute la
       largeur de l'écran et les deux bouts n'auraient plus l'air liés. */
    max-width: 21rem;
  }

  /* Mentions légales sous le copyright plutôt qu'à côté : à deux, ils se
     serraient sur une ligne de 390 px. */
  .site-footer__bottom-inner {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-xs);
  }

  /* Le filigrane (420 px de large) recouvrait les horaires et l'adresse.
     Réduit et remonté au niveau du bloc de marque, où il n'y a pas de
     texte fin à traverser. */
  .site-footer__logo-wrap {
    max-width: 260px;
    opacity: 0.75;
  }
}

/* Séparateur section/footer — déplacé du HTML de l'accueil vers ce
   composant (voir js/components/site-footer.js, initDivider()) le
   2026-08-19 pour être présent sur TOUTES les pages, pas seulement
   l'accueil. Deux traits qui grandissent depuis chaque bord au scroll
   jusqu'à se rejoindre au milieu (scrub piloté en JS). Fond calé sur le
   haut du footer juste en dessous, pour que seul le trait rose soit visible
   dans cette bande fine, en continuité avec lui. */
.site-footer__divider {
  position: relative;
  height: 3px;
  background: var(--color-surface);
  overflow: hidden;
}

.site-footer__divider-line {
  position: absolute;
  top: 0;
  height: 100%;
  width: 50%;
  background: var(--color-primary);
  transform: scaleX(0);
  /* will-change plutôt qu'une transition CSS : la valeur est recalculée à
     chaque frame de scroll en JS (effet "scrub", pas une simple apparition
     déclenchée une fois). */
  will-change: transform;
}

.site-footer__divider-line--left {
  left: 0;
  transform-origin: left center;
}

.site-footer__divider-line--right {
  right: 0;
  transform-origin: right center;
}

@media (prefers-reduced-motion: reduce) {
  /* Ligne complète d'emblée, pas de scrub lié au scroll (voir
     js/components/site-footer.js, initDivider(), qui ne branche pas
     l'effet ici). */
  .site-footer__divider-line {
    transform: scaleX(1);
  }
}

/* Curseur natif restauré au-dessus de l'iframe Google Maps (2026-08-17) :
   l'iframe (contenu Google,
   autre origine) affiche de toute façon son propre curseur natif quoi
   qu'on fasse — mais SANS ce cursor: auto, notre `cursor: none` global
   (main.css) s'appliquerait quand même au conteneur .booking-x__map
   lui-même (juste avant d'entrer dans l'iframe), ce qui ferait clignoter
   le curseur. is-over-iframe (posé par js/components/cursor.js via
   mouseenter/mouseleave sur .booking-x__map, les seuls événements fiables
   par-dessus un iframe cross-origin) masque en plus le curseur custom, qui
   resterait sinon figé à la dernière position connue pendant que le
   curseur natif de la carte est déjà visible. */
.booking-x__map,
.booking-x__map * {
  cursor: auto;
}

/* html.has-pointer EN PLUS de .is-over-iframe (2026-08-17) : sans
   .has-pointer ici, "html.has-pointer .cursor-ring { display: block; }" plus bas dans ce
   fichier (spécificité identique, mais après dans le fichier) gagnait
   quand même, et le curseur custom restait visible au-dessus de l'iframe
   au lieu de disparaître. */
html.has-pointer.is-over-iframe .cursor,
html.has-pointer.is-over-iframe .cursor-ring,
html.has-pointer.is-over-iframe .cursor-hand,
html.has-pointer.is-over-iframe .cursor-link,
html.has-pointer.is-over-iframe .cursor-idle-text-wrap {
  display: none;
}

/* ==========================================================================
   Curseur personnalisé (.cursor / .cursor-ring)
   Déplacé depuis home.css le 2026-08-13 : main.css met `cursor: none` sur
   TOUT le site (voir §1, sélecteur universel), donc ce remplacement visuel
   doit être chargé sur toutes les pages, pas uniquement l'accueil. Chaque
   page a besoin du markup `<div class="cursor">`/`<div class="cursor-ring">`
   (voir index.html) et de js/components/cursor.js.
   ========================================================================== */
.cursor,
.cursor-ring {
  position: fixed;
  top: 0;
  left: 0;
  /* 950 (2026-09-09) : au-dessus du préloader (.preloader, z-index 900,
     voir home.css) pour que le curseur custom reste visible pendant le
     chargement, plutôt que masqué dessous — c'est le z-index le plus haut
     du site. */
  z-index: 950;
  pointer-events: none;
  border-radius: 50%;
  transform: translate3d(-50%, -50%, 0);
  display: none;
}

html.has-pointer .cursor,
html.has-pointer .cursor-ring { display: block; }

/* Tant qu'on ne SAIT pas où est la souris (première page d'une visite :
   aucun navigateur ne donne la position avant le premier mouvement), le
   curseur reste invisible plutôt que de s'afficher au centre de l'écran et
   de rejoindre la souris ensuite. La classe est retirée par
   js/components/cursor.js dès la position connue — reprise de la page
   précédente ou premier mousemove — et la transition d'opacité déjà posée
   sur .cursor / .cursor-ring le fait apparaître en fondu, au bon endroit.
   Le sélecteur passe par html pour battre .cursor.is-hidden et
   .cursor-ring.is-active, qui pilotent aussi l'opacité. */
html.cursor-unknown .cursor,
html.cursor-unknown .cursor-ring,
html.cursor-unknown .cursor-idle-text-wrap { opacity: 0; }

.cursor {
  width: 7px;
  height: 7px;
  background: var(--color-primary-dark);
  transition: width 0.3s var(--ease-out), height 0.3s var(--ease-out), opacity 0.3s ease;
}

.cursor-ring {
  width: 38px;
  height: 38px;
  border: 1px solid var(--color-primary);
  transition: width 0.3s var(--ease-out), height 0.3s var(--ease-out),
              background-color 0.3s ease, border-color 0.3s ease, opacity 0.3s ease;
}

/* Curseur au repos (souris immobile 5s, voir js/components/cursor.js) :
   +50% de taille sur le point et l'anneau. */
.cursor.is-idle {
  width: 10.5px;
  height: 10.5px;
}

.cursor-ring.is-idle {
  width: 57px;
  height: 57px;
}

/* Texte "AlyCare" courbé en cercle autour du curseur au repos (voir
   js/components/cursor.js) — même principe que le badge hero
   (.hero-x__badge, texte + textPath SVG, voir home.css) : le wrapper
   positionne (suivi du curseur, comme .cursor-ring), le <svg> tourne en
   continu via l'animation ci-dessous. Deux éléments séparés pour ne pas
   mélanger la transform de position (posée à chaque frame en JS) et celle
   de rotation (animation CSS), exactement comme .hero-x__badge / svg. */
.cursor-idle-text-wrap {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 499;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease;
}

.cursor-idle-text-wrap.is-idle {
  opacity: 1;
}

.cursor-idle-text {
  display: block;
  animation: cursor-idle-text-spin 14s linear infinite;
}

.cursor-idle-text text {
  fill: var(--color-primary-dark);
  font-family: var(--font-body);
  font-size: 7.5px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  font-weight: 700;
}

@keyframes cursor-idle-text-spin {
  to { transform: rotate(360deg); }
}

.cursor-ring.is-active {
  width: 76px;
  height: 76px;
  background: var(--color-primary);
  border-color: transparent;
  opacity: 0.28;
}

/* Inversion des couleurs au survol de texte : cercle plein blanc en
   mix-blend-mode: difference — la différence entre blanc et n'importe
   quelle couleur en dessous donne exactement son inverse, où qu'on soit
   sur la page. Placée après .is-active pour prendre le dessus si jamais
   les deux classes se retrouvent ensemble. Limitée en pratique à l'accueil
   (.hero-x), voir js/components/cursor.js — mais la règle CSS reste
   générique/réutilisable partout où data-cursor-invert serait ajouté.
   ---------------------------------------------------------------------- */
.cursor.is-hidden {
  opacity: 0;
}

.cursor-ring.is-inverting {
  width: 54px;
  height: 54px;
  background: #ffffff;
  border-color: transparent;
  opacity: 1;
  mix-blend-mode: difference;
}

/* Icône "grab" custom (survol des cards avis) — remplace le curseur natif
   `cursor: grab`, non stylable, par une icône colorée en accord avec le
   curseur du site (même couleur que .cursor). Créée dynamiquement par
   js/components/cursor.js. Masque SVG (main ouverte / poing fermé pendant
   le drag) peint via background-color, comme .booking-x__blob ailleurs. */
.cursor-hand {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 501;
  width: 30px;
  height: 30px;
  pointer-events: none;
  background-color: var(--color-primary-dark);
  opacity: 0;
  transition: opacity 0.2s ease;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 12.5V6a1.5 1.5 0 0 1 3 0v5.5'/%3E%3Cpath d='M11 11.5V4.5a1.5 1.5 0 0 1 3 0V11'/%3E%3Cpath d='M14 11V6.5a1.5 1.5 0 0 1 3 0V14'/%3E%3Cpath d='M17 12v-.5a1.5 1.5 0 0 1 3 0V16c0 3.3-2 6-6 6h-1.5c-2.8 0-4.2-1-5.7-2.8l-2.5-3a1.3 1.3 0 0 1 2-1.7L8 12'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 12.5V6a1.5 1.5 0 0 1 3 0v5.5'/%3E%3Cpath d='M11 11.5V4.5a1.5 1.5 0 0 1 3 0V11'/%3E%3Cpath d='M14 11V6.5a1.5 1.5 0 0 1 3 0V14'/%3E%3Cpath d='M17 12v-.5a1.5 1.5 0 0 1 3 0V16c0 3.3-2 6-6 6h-1.5c-2.8 0-4.2-1-5.7-2.8l-2.5-3a1.3 1.3 0 0 1 2-1.7L8 12'/%3E%3C/svg%3E");
}

.cursor-hand.is-active {
  opacity: 1;
}

/* Icône "lien cliquable" custom (survol de tout <a>) — même principe que
   .cursor-hand ci-dessus : masque SVG inline en data URI (aucun CDN,
   aucune requête réseau), couleur --color-primary-dark. is-pressed réduit
   légèrement l'échelle tant que le clic est maintenu (retour tactile). */
.cursor-link {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 501;
  width: 28px;
  height: 28px;
  pointer-events: none;
  background-color: var(--color-primary-dark);
  opacity: 0;
  transition: opacity 0.2s ease, transform 0.15s ease;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 14a8 8 0 0 1-8 8'/%3E%3Cpath d='M18 11v-1a2 2 0 0 0-4 0'/%3E%3Cpath d='M14 10V9a2 2 0 0 0-4 0v1'/%3E%3Cpath d='M10 9.5V4a2 2 0 0 0-4 0v10'/%3E%3Cpath d='M18 11a2 2 0 1 1 4 0v3a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-6-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 14a8 8 0 0 1-8 8'/%3E%3Cpath d='M18 11v-1a2 2 0 0 0-4 0'/%3E%3Cpath d='M14 10V9a2 2 0 0 0-4 0v1'/%3E%3Cpath d='M10 9.5V4a2 2 0 0 0-4 0v10'/%3E%3Cpath d='M18 11a2 2 0 1 1 4 0v3a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-6-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15'/%3E%3C/svg%3E");
}

.cursor-link.is-active {
  opacity: 1;
}

/* Contraste auto (voir js/components/cursor.js) : sur un bouton/lien dont
   le fond est trop proche de --color-primary-dark (contraste WCAG < 1.8),
   l'icône passe en blanc pour rester lisible. */
.cursor-link.is-on-primary {
  background-color: var(--color-text-on-primary);
}

.cursor-hand.is-grabbing {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 12.5v-2a2 2 0 0 0-3.6-1.2'/%3E%3Cpath d='M14 10.3V8a2 2 0 0 0-4 0v2.7'/%3E%3Cpath d='M10 9.8V9a2 2 0 0 0-4 0v5'/%3E%3Cpath d='M6 14v-1a2 2 0 0 0-4 0v0'/%3E%3Cpath d='M18 11.5a2 2 0 1 1 4 0V15a8 8 0 0 1-8 8h-1.5a8 8 0 0 1-6.5-3.4L3 16a2 2 0 0 1 3.2-2.4L8 16'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 12.5v-2a2 2 0 0 0-3.6-1.2'/%3E%3Cpath d='M14 10.3V8a2 2 0 0 0-4 0v2.7'/%3E%3Cpath d='M10 9.8V9a2 2 0 0 0-4 0v5'/%3E%3Cpath d='M6 14v-1a2 2 0 0 0-4 0v0'/%3E%3Cpath d='M18 11.5a2 2 0 1 1 4 0V15a8 8 0 0 1-8 8h-1.5a8 8 0 0 1-6.5-3.4L3 16a2 2 0 0 1 3.2-2.4L8 16'/%3E%3C/svg%3E");
}

/* ==========================================================================
   SURCHARGES DU THÈME SOMBRE
   Voir l'explication en tête du même bloc dans home.css.
   ========================================================================== */

/* Le filigrane du footer est un trait NOIR sur transparent : sur le fond
   sombre il disparaît, purement et simplement. On l'inverse au rendu — le
   fichier, lui, n'est pas touché. À retirer le jour où Aly fournit une
   version claire du logo (il faudra alors inverser la logique). */
:root[data-theme="dark"] .site-footer__logo { filter: invert(1); }


/* ==========================================================================
   APERÇUS DE CHARGEMENT DES MÉDIAS — « blur-up » (2026-08-26)
   ————————————————————————————————————————————————————————————————————
   Demande de Rom : un aperçu pendant le chargement de chaque image et
   vidéo, sur tout le site (pas seulement sur mobile).

   LE PRINCIPE, en une phrase : chaque photo porte en fond une version
   MINUSCULE d'elle-même (24 px de large, ~200 octets, embarquée en base64
   ci-dessous). Le navigateur l'agrandit — donc la floute — et la vraie
   photo se peint par-dessus dès qu'elle est décodée. On passe ainsi de
   « rectangle vide » à « photo floue aux bonnes couleurs » puis à la photo
   nette, au même cadrage, sans saut.

   POURQUOI CETTE TECHNIQUE plutôt qu'un squelette animé :
   - zéro JavaScript, zéro requête, zéro balise ajoutée dans le HTML : ce
     fichier suffit, et les six pages en profitent sans être touchées ;
   - rien à retirer une fois l'image chargée — elle recouvre son propre
     fond, il n'y a pas d'état à nettoyer ni de risque de scintillement ;
   - si une image manque (404, chemin cassé), l'aperçu reste : on voit une
     tache colorée à la bonne place plutôt qu'une icône d'image brisée ;
   - le total des neuf aperçus fait ~2 Ko, soit moins qu'une seule
     requête réseau supplémentaire.

   ⚠ À SAVOIR SI ON REMPLACE UNE PHOTO : l'aperçu est figé ici. Changer le
   CONTENU d'un fichier sans changer son nom laisserait l'ancien aperçu —
   on verrait un flou d'une autre photo pendant une seconde. Il faut alors
   régénérer la règle correspondante. Recette (Python + Pillow) :
       im = Image.open(f).convert('RGB')
       h = round(im.height * 24 / im.width)
       im.resize((24, h), Image.LANCZOS) \
         .filter(ImageFilter.GaussianBlur(0.6)) \
         .save(buf, 'WEBP', quality=55, method=6)
       uri = 'data:image/webp;base64,' + base64.b64encode(buf.getvalue()).decode()
   Un fichier RENOMMÉ, lui, ne casse rien : le sélecteur ne correspond plus,
   l'image se charge simplement sans aperçu.

   Les sélecteurs visent la FIN du src : ils marchent donc quel que soit le
   chemin relatif de la page. background-size: cover + position centrée
   pour épouser exactement le cadrage de la photo (toutes sont en
   object-fit: cover dans leur conteneur).
   ========================================================================== */
img[src$="hero_wide.webp"],
img[src$="alyssa.webp"],
img[src$="laser.webp"],
img[src$="laser_back.webp"],
img[src$="indiba.webp"],
img[src$="indiba_back.webp"],
img[src$="regard.webp"],
img[src$="regard_back.webp"],
img[src$="laser_hero.png"],
img[src$="indiba_hero.webp"],
img[src$="laser_hero.webp"],
img[src$="regard_hero.webp"] {
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

img[src$="hero_wide.webp"] {
  background-image: url(data:image/webp;base64,UklGRm4AAABXRUJQVlA4IGIAAADwAwCdASoYAA8APsFOoUqnpCMhsAgA8BgJQBWABF8MIap4R2/fv07IAP6virgVlFSIXH4hWWzjxNw+N3G136fTjAoQY5NdpqutUrTGzHoHqy1EMSaM7k49w/W6GyYheldEAA==);
}

img[src$="alyssa.webp"] {
  background-image: url(data:image/webp;base64,UklGRuoAAABXRUJQVlA4IN4AAAAwBgCdASoYACsAPsFYpE0npSOiKqgA8BgJQBhV/+AmkS6FqLbnJ6TMCBNbyrgiNhI5bcK0imB9Ly/wAP7jC/hI47xT0vokXpWnWyZs/J/CJWjS3/ttsVdRxHMxdiABlXmXpUV7sQ9uhzMyxugR4UgSTjEsUEJE6c90e0lbujSFDGNKPlWAV9KFf99H1OPJDl+bQ08rqECZ+HaAt9Kf/HDOa7MUQk9nHeAKBjNY9LfnNNwfPvPRRKRS/x+KFNLlGM5Pe3ixLmTgp94LIF0LJ3wMjTL08MzkPmv2PyUAAAA=);
}

img[src$="laser.webp"] {
  background-image: url(data:image/webp;base64,UklGRqoAAABXRUJQVlA4IJ4AAABwBQCdASoYABIAPsFYpU6npKMiKAgA8BgJQBdmZl+VA71fUCrslbdxi4RqRAeLXSSfk/gAAP7mO6pACStUiC2pDHysB7eL2IyM7MxnoOQpy/Al43jNqHKLTPIcFjc4sVo11nSNG3pXTzb8ZNI4pg5UpjGLCS1hbAUnT77QcDouWia491N3N56eWnKucMSaC3XL5fCtSXyAZyT81gAAAA==);
}

img[src$="laser_back.webp"] {
  background-image: url(data:image/webp;base64,UklGRqIAAABXRUJQVlA4IJYAAABwBQCdASoYAB4APsFQoEqnpKMhsAgA8BgJYgCxH0xDTq+QJDgT6R6zWYa6jwS983bB3UAAAP7ewNs/Mn3zBrhTyx8mw23RHHnncxCPOP18vVDR/eLFEmsN7d+kq6VbqPdEeHGrXQz62sAHxa7UE31dlqgTSQIWDqiP/thCAL0uRmrhW+fajylhmhwvYlK/iTfQAf0AAAA=);
}

img[src$="indiba.webp"] {
  background-image: url(data:image/webp;base64,UklGRqwAAABXRUJQVlA4IKAAAAAwBQCdASoYABIAPsFWpE0npKOiKAqo8BgJQBOmUAAvPrbxlklflBJGcGQXnt3mAokKQAD+uk+YJnjV0WsB/CcJmHWH5EEGDgNt7Ixoo8o5f1UNbazVw6BjN0iRgJ7+Rx9NUr6/JBxMI1hsADU/GIofuO17CYhvndAZZojQ9hvm2MOQsVRY1OSSVx6qXN07XvphCu9PtDyfshFNI5A4gSAA);
}

/* indiba_back.webp (2026-09-02, REMPLACÉE le même jour — nouvelle photo
   sous le même nom : cf. commentaire dans index.html. Aperçu RÉGÉNÉRÉ ici,
   sinon l'ancien flou (produits/flacons) clignoterait avant de laisser
   place au nouveau visuel (main gantée + pièce à main INDIBA). */
img[src$="indiba_back.webp"] {
  background-image: url(data:image/webp;base64,UklGRoAAAABXRUJQVlA4IHQAAACwBACdASoYAB4APsFQoUqnpKMhsAgA8BgJQBkTA2a46eAQjTMIfL6ZdTiZsg/gAPYsnHTUjyP1Ra8+TaBdl2r+iQFB8XL/I7fromUvV+rvyFzrPOK+an1YerxtESdI2zpvBH+8aIrDGyqObd5Wr6M3d5AAAA==);
}

img[src$="regard.webp"] {
  background-image: url(data:image/webp;base64,UklGRqwAAABXRUJQVlA4IKAAAADwBACdASoYABIAPsFUpU0npKOiKAqo8BgJQBdgAUBc9lqoViOtAK7RHEtO6jwOi2AA9x8NqMmWe1mNjCtQxbWI4q5Aqpaxqcbt+M2L58Ask377eLl/i7aATWpvcrBOM18PewQ3Mp47G8oF8LZw6Oje7TZjCpuQU120pwcFFBex3oOUGbHvL7Q+A6qYLEYCl/8HV8rb00vNwlfI3pfGpgAA);
}

img[src$="regard_back.webp"] {
  background-image: url(data:image/webp;base64,UklGRoQAAABXRUJQVlA4IHgAAADwBACdASoYAB4APrFGnUqnI6Kht/qoAOAWCUAACQqiwhT7vteYA1OZOaudDHJYD+gA/c7n/xjFAcpw8osAU7loXDeQaPuT80v/UrfrohJ7S/s+c/JlMYUG2UlgRQZnNy8jLKS8z47XR9pD+5QbBbigx+e1xQAAAAA=);
}

img[src$="laser_hero.png"] {
  background-image: url(data:image/webp;base64,UklGRqAAAABXRUJQVlA4IJQAAADwBACdASoYABIAPsFSo0wnpKMiMBgIAPAYCUAVgAFrMI8TevUYQ+7VqkQp6qixcwAA/hnWpQJi+yxiqJSvJNsvqcNgIvoY60rhIOwfvXMi0eUh++GD621IbguHSkd9/yhqpfshM8VQl0nT5B8b1rkt3LQ8VJqa0OBKZTnr1/ZiFtoX+TWOe0EBi+96WMessh8xqwAA);
}

/* indiba_hero.webp (2026-09-02) — photo définitive de la page Soins
   INDIBA, remplace le placeholder. Recadrage géré côté object-position
   (voir css/main.css, override spécifique à cette image juste après la
   règle générale .page-hero__media--photo img). */
img[src$="indiba_hero.webp"] {
  background-image: url(data:image/webp;base64,UklGRr4AAABXRUJQVlA4ILIAAAAQBQCdASoYABMAPsFYpk4npSOiKAgA8BgJQBajZfg08TaBEiaV+QoVHOL0344VC5EIAP600pwXEp+E+7gl+VqnjdRCaHNtCWNFc1A9y0z/xfVtD0nn08s/q8F0djeoCXVLNdoON+eQ5n/zScB+mTauWJiCnR3M7Me6jitCw3kphHc9nHIUuEA0hvSn4TSfWr84sKL1e+1hqyrb+pqJKOZhKTNpp0H+2p8H4J6UTQTOwUAA);
}

/* laser_hero.webp (2026-09-02, REMPLACÉE le même jour — nouvelle photo
   sous le même nom, voir le commentaire détaillé dans css/main.css). Aperçu
   RÉGÉNÉRÉ ici : le contenu du fichier a changé sans changer de nom, donc
   l'ancien aperçu flou (celui de la photo "pointée vers l'objectif")
   aurait clignoté une seconde à chaque chargement — piège documenté dans
   [[project_alycare_recadrage_photos]]. */
img[src$="laser_hero.webp"] {
  background-image: url(data:image/webp;base64,UklGRuoAAABXRUJQVlA4IN4AAACwBQCdASoYAB4APsFWok0npKMiKAqo8BgJQBOmW1TgAe4775IXxhmWYFvBxjjLTul3qGIj0zAA/p6vKXEOBOvEvYVOK3cN+heXZizhb9G4kRxPDHHqtolu/Vr3qS8ouzxPvcQtwM7SPPg0xsNgnX1WFlqDak5f9hY67AGscn/z1NUotQVrElyEnjyZUBoR9xDuRnWfQfRM/H7SVhvBEkrz1fFag9G8aM9LLmfONM8OBIsTdzHx2wtLgCE6ADIdo4x4wIfvqc/FqU7O2CWl0bYLVD2ifIGKOnDCT80AAAA=);
}

/* regard_hero.webp (2026-09-02) — voir le commentaire détaillé dans
   css/main.css et beaute-du-regard.html. */
img[src$="regard_hero.webp"] {
  background-image: url(data:image/webp;base64,UklGRqQAAABXRUJQVlA4IJgAAAAwBQCdASoYABMAPrFGnEqnI6Kht/qoAOAWCUAADC6Oj4/9zimIEFXbVI/p37+zfFzEAAD+wGKxTF54m6Zt+eh4iiVmgMwRP/Waezf+R2EN1r/SAXYIcGomYlMQYHhGLq0uWRAAzZuYpZ0fzmJJwnexfBSr6mI/yz4CHlAyJ2umrdrbHfsdE9I7k9SuU4fwVbIS07SSn4gAAA==);
}

/* ---- Vidéo et iframes : même geste, sans aperçu du contenu ----
   La vidéo de « L'institut » (Pexels), la carte Google et le module
   Planity viennent de serveurs tiers : impossible d'en fabriquer une
   miniature ici. On pose donc le fond flou générique --media-skeleton
   (voir variables.css) sur leur CONTENEUR — l'élément se peint par-dessus
   dès qu'il est prêt et le recouvre, il n'y a rien à retirer.
   Deux notes :
   - la vidéo est le plus long à venir de tout le site sur une connexion
     mobile (fichier distant), c'est là que l'aperçu sert le plus ;
   - `#planity-container:empty` : le module Planity n'a pas encore de clé
     d'API, donc js/planity-widget.js remplit le conteneur d'un bouton de
     repli dès le chargement. Le `:empty` fait que l'aperçu ne s'affiche
     QUE tant que rien n'a été inséré — le jour où le vrai widget arrive
     (chargement plus long), il jouera son rôle, et jamais derrière un
     contenu déjà en place. La hauteur minimale ne vaut, elle aussi, que
     dans cet état : elle réserve la place au lieu de laisser la page
     sauter à l'arrivée du module. */
.institut-video__media,
.booking-x__map {
  background-image: var(--media-skeleton);
}

#planity-container:empty {
  display: block;
  min-height: 180px;
  border-radius: var(--radius-md);
  background-image: var(--media-skeleton);
}

/* ==========================================================================
   BANDEAU DE CONSENTEMENT AUX COOKIES (2026-09-06)
   Voir js/consent.js pour ce qu'il gouverne. Deux points de conception :
   - les deux boutons sont STRICTEMENT IDENTIQUES — même classe, même taille,
     même traitement. La CNIL regarde la conception de l'interface : un bouton
     « Accepter » plein face à un « Refuser » en simple contour est le motif
     qu'elle épingle. Ne jamais donner plus de poids visuel à l'un des deux,
     et ne jamais éloigner « Refuser » du regard ;
   - z-index 450 : au-dessus du bandeau du haut, mais SOUS le curseur
     personnalisé (500/501) et SOUS le préloader (900), pour qu'il n'apparaisse
     pas par-dessus l'écran de chargement de l'accueil.
   ========================================================================== */

.consent {
  position: fixed;
  z-index: 450;
  left: 0;
  right: 0;
  bottom: 0;
  padding: var(--space-sm);
  background-color: var(--color-surface);
  border-top: 1px solid var(--color-border);
  box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.07);
  animation: consent-entree 0.45s ease both;
}

@keyframes consent-entree {
  from { transform: translateY(100%); }
  to { transform: translateY(0); }
}

.consent__inner {
  max-width: var(--container-max, 1200px);
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: var(--space-md);
  flex-wrap: wrap;
  justify-content: center;
}

.consent__text {
  margin: 0;
  flex: 1 1 20rem;
  font-size: 0.86rem;
  line-height: 1.5;
  color: var(--color-text-light);
}

.consent__text a {
  color: var(--color-primary-dark);
  text-underline-offset: 0.18em;
}

.consent__actions {
  display: flex;
  gap: var(--space-sm);
  flex: 0 0 auto;
}

.consent__btn {
  /* Taille resserrée : c'est une bande de service, pas un appel à l'action.
     Les deux boutons gardent la même taille l'un que l'autre. */
  padding: 0.55rem 1.4rem;
  font-size: 0.82rem;
  min-width: 7.5rem;   /* largeur commune : ni l'un ni l'autre ne domine */
  text-align: center;
}

@media (max-width: 560px) {
  .consent__inner { flex-direction: column; align-items: stretch; }
  .consent__text { flex: 1 1 auto; }
  .consent__actions { justify-content: stretch; }
  .consent__btn { flex: 1 1 0; text-align: center; }
}

@media (prefers-reduced-motion: reduce) {
  .consent { animation: none; }
}

/* Le lien « Cookies » du pied de page est un <button> et non un <a> : il
   déclenche une action (rouvrir le bandeau), il ne navigue pas. Un <a href="#">
   serait un lien mort sans JavaScript. Ces quelques lignes lui rendent
   l'apparence de ses voisins. Voir js/consent.js, [data-consent-open]. */
.site-footer__legal-btn {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  cursor: inherit;
  text-decoration: none;
  transition: color 0.3s ease;
}

.site-footer__legal-btn:hover { color: var(--color-primary-dark); }
