/* ==========================================================================
   BASE - reset, typographie, primitives réutilisées par toutes les sections.
   Rien d'ici n'est spécifique à un bloc : si un style ne sert qu'une section,
   il vit dans sections.css.
   ========================================================================== */

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

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

/* Respecter le réglage système : une page pleine d'animations d'entrée est
   inutilisable pour qui souffre du mouvement. */
@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;
    scroll-behavior: auto !important;
  }
  .entete-site,
  .hero__visuel {
    animation: none !important;
  }

  /* Le défilement lent des bénéfices continue : un mouvement linéaire et
     lent n'est pas ce que le réglage vise, et sans lui la section se fige
     sur une moitié de contenu. */
  .benefices__piste {
    animation-duration: 95s !important;
    animation-iteration-count: infinite !important;
  }

  /* Même raison pour le rail des planètes : sans lui il se fige, et comme il
     ne se fait pas défiler au doigt, quatre planètes sur sept restaient
     invisibles aux iPhone réglés sur « Réduire les animations ». */
  .rail__piste {
    animation-duration: 120s !important;
    animation-iteration-count: infinite !important;
  }
}

body {
  margin: 0;
  background: var(--fond);
  color: var(--texte);
  font-family: var(--police-corps);
  font-size: var(--t-corps);
  line-height: var(--lh-corps);
  font-weight: 500;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

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

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

:focus-visible {
  outline: 2px solid var(--accent-vif);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ---------------------------------------------------------------- titres */

h1,
h2,
h3 {
  font-family: var(--police-titre);
  font-weight: 700;
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre);
  margin: 0;
  text-wrap: balance;
}

h1 {
  font-size: var(--t-h1);
}
h2 {
  font-size: var(--t-h2);
}
h3 {
  font-size: var(--t-h3);
}

/* Composition. Un paragraphe ne laisse pas un mot orphelin sur sa dernière
   ligne (pretty) ; un texte centré se répartit en lignes de longueur égale
   (balance), comme les titres plus haut. Améliorations progressives : là où
   le navigateur ne les connaît pas, la césure reste celle d'aujourd'hui. */
p,
li,
dd,
figcaption,
summary {
  margin: 0;
  text-wrap: pretty;
}

.centre :is(p, li),
.cta-final p,
.hero__chapeau,
.stats__label,
.benefices__texte .chapeau {
  text-wrap: balance;
}

/* ---------------------------------------------------------------- gabarit */

.enveloppe {
  width: 100%;
  max-width: var(--largeur);
  margin-inline: auto;
  padding-inline: var(--gouttiere);
}

.section {
  padding-block: var(--sect-y);
  position: relative;
  /* La barre flottante mesure ~65px : sans cette marge, une navigation par
     ancre (#faq, #guide…) glisse le titre de section sous la barre. */
  scroll-margin-top: 5.5rem;
}

/* Un titre de section centré avec son eyebrow et son chapeau : ce trio
   revient six fois dans la page, il mérite sa propre primitive. */
.entete {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.25rem;
  text-align: center;
  margin-bottom: clamp(2.5rem, 1rem + 5vw, 4.5rem);
}

.entete h2 {
  max-width: 18ch;
}

.entete .chapeau {
  color: var(--texte-doux);
  max-width: 60ch;
}

/* ---------------------------------------------------------------- eyebrow */

.eyebrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.7rem 1.6rem;
  border-radius: var(--r-pill);
  background-color: var(--verre-fond);
  background-image: var(--verre-spec);
  -webkit-backdrop-filter: blur(var(--verre-flou-leger));
  backdrop-filter: blur(var(--verre-flou-leger));
  border: 1px solid var(--verre-rim);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16);
  font-size: var(--t-eyebrow);
  font-weight: 500;
  color: var(--texte);
  white-space: nowrap;
}

/* ---------------------------------------------------------------- boutons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  padding: 1.05rem 2.6rem;
  border-radius: var(--r-pill);
  font-family: var(--police-corps);
  font-weight: 700;
  font-size: 1rem;
  line-height: 1;
  transition: transform var(--d-court) var(--courbe),
    box-shadow var(--d-court) var(--courbe), background-color var(--d-court);
  will-change: transform;
}

.btn:hover {
  transform: translateY(-2px);
}

.btn:active {
  transform: translateY(0);
}

.btn--accent {
  background-color: var(--accent);
  /* GlassSkin : le reflet et l'arête, sans le flou - la surface est opaque. */
  background-image: linear-gradient(
    rgba(255, 255, 255, 0.3) 0%,
    rgba(255, 255, 255, 0.07) 45%,
    transparent 60%
  );
  color: var(--sur-accent);
  box-shadow: var(--glow-accent), inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 0 0 1px rgba(255, 255, 255, 0.16);
}

.btn--accent:hover {
  box-shadow: var(--glow-accent-fort), inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 0 0 1px rgba(255, 255, 255, 0.16);
}

.btn--blanc {
  background: #fff;
  color: #000;
}

.btn--fantome {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--trait-fort);
  color: var(--texte);
}

/* Tant que l'app n'est sur aucun magasin, le badge annonce « bientôt » et
   n'est pas un lien : même dessin, sans promesse. */
.store--bientot {
  cursor: default;
  opacity: 0.92;
}

.store--bientot:hover {
  transform: none;
  border-color: var(--trait-fort);
}

/* Boutons stores : le badge officiel est une image, mais on le reconstruit
   en HTML pour rester net à tout facteur de zoom et éviter 2 requêtes. */
.store {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  padding: 0.7rem 1.4rem;
  border-radius: 12px;
  background: #000;
  border: 1px solid var(--trait-fort);
  transition: transform var(--d-court) var(--courbe),
    border-color var(--d-court);
}

.store:hover {
  transform: translateY(-2px);
  border-color: var(--trait-accent);
}

.store svg {
  width: 30px;
  height: 30px;
  flex: none;
}

.store span {
  display: block;
  font-family: var(--police-corps);
  color: #fff;
}

.store .store__sur {
  font-size: 0.66rem;
  line-height: 1.1;
  letter-spacing: 0.02em;
}

.store .store__nom {
  font-size: 1.15rem;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.01em;
}

/* ---------------------------------------------------------------- cartes */

/* Une pièce de verre : flou qui réfracte les nappes du .ciel, teinte,
   reflet spéculaire, rebord réel, arête haute. Les cinq couches de
   src/ui/Glass.tsx - en retirer une fait retomber la carte en aplat. */
.carte {
  position: relative;
  background-color: var(--verre-fond);
  background-image: var(--verre-spec),
    linear-gradient(var(--verre-teinte), var(--verre-teinte));
  /* Le reflet est une lame de ~170px en haut de carte, pas un pourcentage :
     sur une carte de 800px, 55% lavaient de gris la moitié de la pièce. */
  background-repeat: no-repeat;
  background-size: 100% 170px, 100% 100%;
  -webkit-backdrop-filter: blur(var(--verre-flou));
  backdrop-filter: blur(var(--verre-flou));
  border: 1px solid var(--verre-rim);
  border-radius: var(--r-carte);
  box-shadow: var(--ombre-carte);
}

/* L'arête haute : filet clair, plus lumineux au centre, éteint aux angles.
   C'est le détail qui donne l'épaisseur du verre. */
.carte::before {
  content: "";
  position: absolute;
  top: 0;
  left: 10%;
  right: 10%;
  height: 1px;
  background: var(--verre-arete);
  z-index: 3;
  pointer-events: none;
}

/* ---------------------------------------------------------------- halos
   Les taches de lumière derrière les sections. Purement décoratives, donc
   aria-hidden côté HTML et pointer-events none ici. */

.halo {
  position: absolute;
  border-radius: 50%;
  filter: blur(90px);
  pointer-events: none;
  z-index: 0;
}

/* ---------------------------------------------------------------- révélation
   Animation d'entrée pilotée par IntersectionObserver (js/app.js). L'état
   initial n'est appliqué que si JS est présent : sans JS, `.js` est absent de
   <html> et tout reste visible. */

.js [data-reveal] {
  opacity: 0;
  transform: translateY(28px) scale(0.985);
  filter: blur(6px);
  transition: opacity var(--d-entree) var(--courbe-sortie),
    transform var(--d-entree) var(--courbe-sortie),
    filter var(--d-entree) var(--courbe-sortie);
  transition-delay: var(--retard, 0ms);
}

.js [data-reveal].est-visible {
  opacity: 1;
  transform: none;
  filter: none;
}

/* Sur mobile, l'entrée se passe du flou : un filtre animé sur une section
   entière coûte cher aux GPU modestes, et l'effet se voit peu à cette taille. */
@media (max-width: 700px) {
  .js [data-reveal] {
    filter: none;
    transform: translateY(20px);
  }
}

/* Le titre du hero se compose mot à mot (js/app.js enveloppe chaque mot).
   Sans JS, il n'y a pas de span : le titre est simplement là. */
.js .hero h1 .mot {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.55em) rotate(1.5deg);
  filter: blur(7px);
  animation: mot-entre 1s var(--courbe-sortie) forwards;
  animation-delay: calc(140ms + var(--i, 0) * 60ms);
}

@keyframes mot-entre {
  to {
    opacity: 1;
    transform: none;
    filter: none;
  }
}

/* ---------------------------------------------------------------- morphing
   Une seule variable, --k : 0 en haut de page, 1 une fois défilé. Tout ce
   qui change dans la barre en dérive (largeur, hauteur, teinte, ombre, gap,
   tailles). Là où le navigateur sait lier une animation au défilement, --k
   suit le scroll en continu ; ailleurs, la classe .est-defilee la fait
   transiter. Une seule source, deux moteurs. */
@property --k {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

/* ---------------------------------------------------------------- utilitaires */

.centre {
  text-align: center;
}

.visuellement-cache {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.visuellement-cache:focus-visible {
  position: fixed;
  inset: 1rem auto auto 1rem;
  z-index: 200;
  width: auto;
  height: auto;
  margin: 0;
  padding: 0.8rem 1.4rem;
  clip-path: none;
  background: var(--fond-carte);
  border: 1px solid var(--trait-fort);
  border-radius: var(--r-pill);
}

/* ---------------------------------------------------------------- le ciel
   Posé derrière tout (z-index négatif, body sans stacking context). Trois
   nappes BLANCHES très diffuses qui dérivent - jamais colorées : la couleur
   n'appartient qu'aux composants - et deux couches d'étoiles qui scintillent
   en alternance. Valeurs reprises d'Ambient.tsx et StarField.tsx. */

.ciel {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

.ciel__nappe {
  position: absolute;
  aspect-ratio: 1;
  border-radius: 50%;
  will-change: transform;
}

/* L'aube qui monte. Les nappes prennent une couleur qui suit la page : nuit
   émeraude en haut, ciel neutre au milieu, aube pâle en bas, à l'approche du
   téléchargement - Aube, le premier palier de l'app. Trois couleurs
   enregistrées, animées par le défilement de la page (même mécanique que la
   barre). Sans timeline de défilement, app.js fait le même calcul au
   défilement ; et sans rien du tout, le ciel reste sur la nuit émeraude.
   L'or du bas reste pâle et peu saturé : le bouton accent doit rester la
   chose la plus lumineuse de l'écran. */
@property --teinte-a {
  syntax: "<color>";
  inherits: true;
  initial-value: #1f8a6e;
}

@property --teinte-b {
  syntax: "<color>";
  inherits: true;
  initial-value: #2fb59a;
}

@property --teinte-c {
  syntax: "<color>";
  inherits: true;
  initial-value: #12604c;
}

@supports (animation-timeline: scroll()) {
  .ciel {
    animation: aube linear both;
    animation-timeline: scroll(root);
  }
}

@keyframes aube {
  0% {
    --teinte-a: #1f8a6e;
    --teinte-b: #2fb59a;
    --teinte-c: #12604c;
  }
  50% {
    --teinte-a: #c9d0d8;
    --teinte-b: #9aa4b0;
    --teinte-c: #8a929c;
  }
  100% {
    --teinte-a: #f2c48a;
    --teinte-b: #ffd9a0;
    --teinte-c: #f0aa8a;
  }
}

.ciel__nappe--a {
  top: -28vw;
  left: -22vw;
  width: 85vw;
  background: radial-gradient(circle, color-mix(in srgb, var(--teinte-a) 13%, transparent), transparent 68%);
  animation: derive-a 26s ease-in-out infinite alternate;
}

.ciel__nappe--b {
  top: 22vh;
  right: -30vw;
  width: 72vw;
  background: radial-gradient(circle, color-mix(in srgb, var(--teinte-b) 10%, transparent), transparent 68%);
  animation: derive-b 32s ease-in-out infinite alternate;
}

.ciel__nappe--c {
  bottom: -38vw;
  left: 4vw;
  width: 90vw;
  background: radial-gradient(circle, color-mix(in srgb, var(--teinte-c) 9%, transparent), transparent 68%);
  animation: derive-c 38s ease-in-out infinite alternate;
}

/* Le grain de film : un bruit fin, tuilé, presque invisible, qui donne de la
   matière aux aplats sombres. Une image, aucun calcul par image. */
.ciel__grain {
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/><feComponentTransfer><feFuncA type='table' tableValues='0 0.55'/></feComponentTransfer></filter><rect width='180' height='180' filter='url(%23n)'/></svg>");
  background-size: 180px;
  opacity: 0.07;
  pointer-events: none;
}

@keyframes derive-a {
  to { transform: translate(40px, 26px); }
}
@keyframes derive-b {
  to { transform: translate(-34px, 40px); }
}
@keyframes derive-c {
  to { transform: translate(28px, -30px); }
}

/* Deux couches d'étoiles en box-shadow : zéro image, positions en vw/vh
   donc responsives. Le scintillement est porté par la couche entière,
   les deux couches sont déphasées. */
.ciel__etoiles,
.ciel__etoiles--b {
  position: absolute;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  animation: scintille 5.2s ease-in-out infinite alternate;
}

.ciel__etoiles {
  box-shadow:
    7vw 12vh 0 0 rgba(255, 255, 255, 0.3),
    16vw 34vh 0 0 rgba(255, 255, 255, 0.2),
    24vw 8vh 0 0 rgba(255, 255, 255, 0.34),
    31vw 55vh 0 0 rgba(255, 255, 255, 0.16),
    38vw 22vh 0 0 rgba(255, 255, 255, 0.28),
    44vw 76vh 0 0 rgba(255, 255, 255, 0.2),
    52vw 14vh 0 0 rgba(255, 255, 255, 0.32),
    58vw 44vh 0 0 rgba(255, 255, 255, 0.18),
    66vw 68vh 0 0 rgba(255, 255, 255, 0.26),
    72vw 28vh 0 0 rgba(255, 255, 255, 0.34),
    79vw 84vh 0 0 rgba(255, 255, 255, 0.16),
    85vw 10vh 0 0 rgba(255, 255, 255, 0.3),
    91vw 48vh 0 0 rgba(255, 255, 255, 0.22),
    12vw 64vh 0 0 rgba(255, 255, 255, 0.24),
    27vw 90vh 0 0 rgba(255, 255, 255, 0.18),
    48vw 92vh 0 0 rgba(255, 255, 255, 0.22),
    63vw 6vh 0 0 rgba(255, 255, 255, 0.2),
    88vw 70vh 0 0 rgba(255, 255, 255, 0.26),
    4vw 42vh 0 0 rgba(255, 255, 255, 0.18),
    96vw 26vh 0 0 rgba(255, 255, 255, 0.24);
}

.ciel__etoiles--b {
  width: 3px;
  height: 3px;
  animation-duration: 6.8s;
  animation-delay: -3s;
  box-shadow:
    10vw 78vh 1px 0 rgba(255, 255, 255, 0.4),
    21vw 18vh 1px 0 rgba(255, 255, 255, 0.5),
    35vw 40vh 1px 0 rgba(255, 255, 255, 0.34),
    55vw 60vh 1px 0 rgba(255, 255, 255, 0.44),
    69vw 12vh 1px 0 rgba(255, 255, 255, 0.5),
    82vw 52vh 1px 0 rgba(255, 255, 255, 0.36),
    93vw 88vh 1px 0 rgba(255, 255, 255, 0.42),
    42vw 4vh 1px 0 rgba(255, 255, 255, 0.38);
}

/* Étoiles filantes : une tête vive et une traîne qui s'étire derrière elle,
   puis se dissout. La tête est à l'extrémité droite ; on pivote autour
   d'elle et on avance le long de l'axe pivoté. Le JS les lance une à une, à
   intervalles irréguliers, avec un angle, une longueur et une vitesse qui
   varient un peu : c'est ce qui les rend crédibles. */
.ciel__meteore {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--longueur, 180px);
  height: 2px;
  border-radius: 999px;
  transform-origin: 100% 50%;
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.25) 45%,
    rgba(255, 255, 255, 0.95) 100%
  );
  filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
  opacity: 0;
  pointer-events: none;
  will-change: transform, opacity;
}

.ciel__meteore::after {
  content: "";
  position: absolute;
  top: 50%;
  right: -2px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  translate: 0 -50%;
  background: #fff;
  box-shadow:
    0 0 10px 2px rgba(255, 255, 255, 0.9),
    0 0 26px 6px rgba(170, 205, 255, 0.35);
}

@keyframes scintille {
  from { opacity: 0.55; }
  to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .ciel__nappe,
  .ciel__etoiles,
  .ciel__etoiles--b {
    animation: none;
  }
}
