/* =========================================================
   HEXORA — Feuille de style
   Palette de marque (5 couleurs) :
   #120928 bleu nuit · #E1DDD8 beige clair · #7542EF violet (accent
   principal) · #EAAC43 doré (accent ponctuel) · #5AC1E9 bleu ciel
   (accent d'aération)

   --c-900…--c-100 restent des nuances NEUTRES, mais ne sont plus des
   gris : ce sont des mélanges calculés entre le bleu nuit et le beige
   (ex. --c-700 = bleu nuit + 35 % de beige). Même logique côté accent :
   --accent-violet-strong et --accent-violet-light sont le violet de
   marque assombri ou éclairci pour rester lisible selon qu'il porte du
   texte sur fond clair ou sur fond sombre — voir le calcul en commentaire
   à chaque usage sensible. Aucune teinte étrangère à ces 5 couleurs
   n'est introduite : tout le reste n'est qu'un mélange entre elles.
   ========================================================= */

:root {
  --c-900: #120928;
  --c-800: #312942;
  --c-700: #5A5366;
  --c-600: #847E89;
  --c-500: #A7A2A7;
  --c-300: #C2BDBE;
  --c-100: #E1DDD8;

  --bg-page: var(--c-100);
  --bg-alt: var(--c-300);
  --bg-dark: var(--c-900);
  --text-main: var(--c-900);
  --text-muted: var(--c-700);
  --text-on-dark: var(--c-100);
  --text-on-dark-muted: var(--c-500);
  /* Bordures discrètes sur fond clair : --c-500 n'y tient pas les 3:1
     nécessaires à une bordure (1,86:1, quasi invisible). --c-700 y tient
     5,4:1 — visible sans dominer. --c-500 reste la bonne valeur pour une
     bordure sur fond SOMBRE (7,6:1 sur --c-900), utilisée telle quelle
     directement là où c'est le cas. */
  --border-soft: var(--c-700);

  /* Violet — accent principal : boutons, CTA, liens actifs. Décliné en
     3 tons car le violet pur ne tient le 4,5:1 du texte normal sur
     AUCUN des deux neutres de base (4,11:1 sur le beige, 3,46:1 sur le
     bleu nuit) :
       - pur       : bordures/anneaux de focus/soulignés (seuil 3:1, non
                     du texte) — 4,11:1 sur beige, largement suffisant ;
       - strong    : violet + 15 % de bleu nuit — pour du TEXTE ou un
                     aplat plein sur fond clair (5,06:1 sur le beige) ;
       - light     : violet + 30 % de beige — pour du TEXTE sur fond
                     sombre (5,30:1 sur le bleu nuit). */
  --accent-violet: #7542EF;
  --accent-violet-strong: #6639D1;
  --accent-violet-strong-hover: #5731B3;
  --accent-violet-light: #9571E8;

  /* Doré — accent secondaire ponctuel. Contraste insuffisant sur le
     beige (1,48:1) : réservé aux fonds sombres (9,57:1 sur le bleu
     nuit), jamais utilisé comme texte sur fond clair. Depuis que les
     sections centrales sont passées en clair, son seul usage est celui
     des titres du pied de page. */
  --accent-gold: #EAAC43;

  /* Bleu ciel — accent secondaire de variation/aération : les lignes du
     fond animé. Même limite que le doré (1,52:1 sur le beige) : jamais
     en texte ni en bordure sur fond clair, seulement en décor à faible
     opacité. */
  --accent-sky: #5AC1E9;

  --font-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --container: 1160px;
  --radius: 4px;
  /* Rayon plus prononcé, réservé aux grands cadres de mise en avant
     (le cadre-navigateur du hero, les cartes de forfait — dont la
     plaquette tarifaire utilise déjà ces coins arrondis) — les
     boutons/champs/petites cartes restent sur --radius. */
  --radius-lg: 20px;
  --gap: 24px;
  --header-h: 68px;

  /* Courbe commune à toutes les animations d'interface : départ vif,
     arrivée très douce (la même que le mur de projets utilise sous le
     nom --ease). */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
}

* { box-sizing: border-box; }
html {
  scroll-behavior: smooth;
  /* Pas de scroll-snap natif ici : la section Projets fait défiler ses
     tableaux à partir d'un bloc accroché (position:sticky), dont la
     progression est lue directement sur le scroll de la page — voir
     js/projects-wall.js. Un scroll-snap par-dessus recadrerait la page
     pendant que le mur avance, et les deux se contrediraient. */
}

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

/* ---------- Écran de chargement ---------- */

/* Rien n'est visible par défaut : c'est un script en tête de index.html qui
   pose la classe « chargement » sur <html>, et seulement s'il y a du
   JavaScript pour la retirer ensuite. Sans JS, l'écran n'apparaît jamais et
   le site reste consultable — un voile qu'on ne peut pas lever serait pire
   que pas de voile du tout. */
.loader { display: none; }

html.chargement,
html.chargement body { overflow: hidden; }

html.chargement .loader {
  position: fixed;
  inset: 0;
  /* Au-dessus de tout : l'en-tête est à 100, le bandeau cookies à 60. */
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-page);
}

.loader--parti {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.45s ease;
}

/* Le masque découpe l'élément ET tout ce qu'il contient à la forme du sigle :
   le niveau qui monte à l'intérieur est donc contenu dans le logo sans aucun
   calcul de forme. Le fichier est une silhouette pleine tirée du PNG de
   marque, débarrassée de son ombre portée et de son dégradé. */
.loader__mark {
  position: relative;
  width: clamp(110px, 16vw, 168px);
  aspect-ratio: 505 / 530;
  background: var(--c-900);
  -webkit-mask: url("../image/logo-masque.png") center / contain no-repeat;
          mask: url("../image/logo-masque.png") center / contain no-repeat;
  animation: loader-apparition 0.35s ease both;
}

@keyframes loader-apparition {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Le niveau. Sa hauteur est pilotée par js/loader.js ; la transition fait le
   reste, si bien que la montée reste régulière même quand les ressources
   arrivent par à-coups. */
.loader__fill {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 0;
  background: var(--accent-violet);
  transition: height 0.4s linear;
}

/* La crête. Une surface d'eau n'est pas une arête droite : une ondulation
   précède le niveau. Elle est dessinée en masque et non en image colorée,
   pour que la couleur reste celle du jeton de palette juste au-dessus. */
.loader__fill::before {
  content: '';
  position: absolute;
  bottom: 100%;
  left: 0;
  width: 200%;
  height: 14px;
  /* « inherit » et non le jeton de palette : la crête prend toujours la
     couleur du niveau. */
  background-color: inherit;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 14' preserveAspectRatio='none'%3E%3Cpath d='M0 7 C12 1 22 1 34 6.5 C46 12 56 12 68 6.5 C80 1 90 1 100 7 L100 14 L0 14 Z' fill='%23fff'/%3E%3C/svg%3E") repeat-x;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 14' preserveAspectRatio='none'%3E%3Cpath d='M0 7 C12 1 22 1 34 6.5 C46 12 56 12 68 6.5 C80 1 90 1 100 7 L100 14 L0 14 Z' fill='%23fff'/%3E%3C/svg%3E") repeat-x;
  /* Le motif fait 25 % d'un élément large de 200 %, soit une demi-largeur de
     logo : deux ondulations traversent le sigle. Le décalage de 50 % déplace
     exactement deux motifs entiers, donc la boucle est invisible. */
  -webkit-mask-size: 25% 100%;
          mask-size: 25% 100%;
  animation: loader-vague 2.4s linear infinite;
}

@keyframes loader-vague {
  to { transform: translateX(-50%); }
}

/* Texte réservé aux lecteurs d'écran : l'écran de chargement est purement
   visuel, il faut bien annoncer ce qui se passe. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

@media (prefers-reduced-motion: reduce) {
  /* Le niveau monte toujours — c'est l'information —, mais sans ondulation
     ni transition prolongée. */
  .loader__fill::before { animation: none; }
  .loader__fill { transition-duration: 0.15s; }
  .loader__mark { animation: none; }
}

/* ---------- Motif de fond animé (commun à toute la page) ---------- */

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

.bg-pattern svg {
  width: 100%;
  height: 100%;
  display: block;
}

.bg-line {
  fill: none;
  stroke-width: 5.5;
  stroke-linecap: round;
}

/* 1 et 3 sur un gris neutre, 2 et 4 en bleu ciel pour « varier » le motif.
   Les lignes passent SOUS le voile beige des sections (.section, 60 %) :
   leur opacité réelle est donc 40 % de celle indiquée ici.
   Réglage : le texte courant (--c-700) doit garder 4,5:1 même là où une
   ligne le croise. Gris --c-600 à 0,5 → 4,51:1 ; bleu ciel à 0,6 → 4,86:1
   (5,42:1 sans ligne). Le gris --c-700 d'avant tomberait à 4,12:1 à cette
   intensité : d'où le --c-600, un cran plus clair. Toute hausse de ces
   opacités ou baisse du voile est à revérifier avec ce calcul. */
.bg-line--1 { stroke: var(--c-600); stroke-opacity: 0.5; animation: bg-drift-1 6.5s ease-in-out infinite alternate; }
.bg-line--2 { stroke: var(--accent-sky); stroke-opacity: 0.6; animation: bg-drift-2 8s ease-in-out infinite alternate; }
.bg-line--3 { stroke: var(--c-600); stroke-opacity: 0.5; animation: bg-drift-3 7.2s ease-in-out infinite alternate; }
.bg-line--4 { stroke: var(--accent-sky); stroke-opacity: 0.6; animation: bg-drift-4 9s ease-in-out infinite alternate; }

@keyframes bg-drift-1 {
  from { transform: translate3d(-4%, -3%, 0) rotate(0deg); }
  to   { transform: translate3d(4%, 4%, 0) rotate(2deg); }
}
@keyframes bg-drift-2 {
  from { transform: translate3d(4%, 3%, 0) rotate(0deg); }
  to   { transform: translate3d(-4%, -3.5%, 0) rotate(-1.8deg); }
}
@keyframes bg-drift-3 {
  from { transform: translate3d(-3%, 4%, 0) rotate(0deg); }
  to   { transform: translate3d(5%, -3%, 0) rotate(1.5deg); }
}
@keyframes bg-drift-4 {
  from { transform: translate3d(3.5%, -4%, 0) rotate(0deg); }
  to   { transform: translate3d(-4.5%, 3%, 0) rotate(-2deg); }
}

@media (prefers-reduced-motion: reduce) {
  .bg-line { animation: none !important; }
}

@media (max-width: 720px) {
  .bg-pattern { opacity: 0.85; }
  .bg-line { animation: none !important; }
}

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

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

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

.container {
  width: 100%;
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 24px;
}

.kicker {
  display: inline-block;
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  /* --c-800 tient 10,2:1 sur le beige de .section, le fond de toutes
     les sections de la page. */
  color: var(--c-800);
  margin-bottom: 12px;
  font-weight: 600;
}

.section-title {
  font-size: clamp(28px, 4vw, 40px);
  font-weight: 700;
  margin: 0 0 20px;
  color: var(--text-main);
}

.section {
  padding: 96px 0;
  position: relative;
  /* Voile beige (--c-100) posé sur les lignes du fond animé : à 60 %, elles
     restent bien visibles tout en gardant le texte lisible là où une ligne
     le croise (calcul détaillé au-dessus de .bg-line--1). */
  background: rgba(225, 221, 216, 0.6);
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 14px 28px;
  border-radius: var(--radius);
  font-weight: 600;
  font-size: 15px;
  border: 1px solid transparent;
  cursor: pointer;
  transition:
    background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease,
    transform 0.2s var(--ease-out), box-shadow 0.25s ease;
}
/* Survol : le bouton se soulève légèrement ; clic : il s'enfonce. Sur
   transform uniquement, donc sans aucun recalcul de mise en page. */
.btn:hover { transform: translateY(-2px) scale(1.03); }
.btn:active { transform: translateY(0) scale(0.97); transition-duration: 0.08s; }
.btn:disabled { transform: none; }

/* Flèche du CTA du hero : en pseudo-élément, indépendante du texte du
   bouton. Elle se décale au survol, comme l'icône des boutons du site de
   référence. */
.btn--arrow::after {
  content: '→';
  /* Texte alternatif vide : la flèche est décorative, elle ne doit pas
     s'ajouter au nom du lien lu par un lecteur d'écran (« … projet
     flèche vers la droite »). La ligne précédente sert de repli pour les
     navigateurs qui ignorent cette syntaxe. */
  content: '→' / '';
  display: inline-block;
  transition: transform 0.25s var(--ease-out);
}
.btn--arrow:hover::after { transform: translateX(4px); }

/* Les 2 CTA principaux du site (hero + envoi du formulaire) passent au
   violet de marque — c'est littéralement la consigne « boutons, CTA ».
   --accent-violet-strong (et non le violet pur) car c'est ici du TEXTE
   normal sur un aplat : 5,06:1, l'aplat pur ne tenait que 4,11:1. */
.btn--primary {
  background: var(--accent-violet-strong);
  color: var(--c-100);
}
.btn--primary:hover {
  background: var(--accent-violet-strong-hover);
  box-shadow: 0 10px 24px -10px rgba(102, 57, 209, 0.65); /* halo violet-strong */
}

.btn--outline {
  background: transparent;
  color: var(--accent-violet-strong);
  border-color: var(--accent-violet-strong);
}
.btn--outline:hover { background: var(--accent-violet-strong); color: var(--c-100); }

/* Boutons arrondis du header et du hero (maquette « Accueil — Hero étapes »).
   Le reste du site garde ses boutons à coins droits (--radius). */
.btn--pilule { border-radius: 999px; }

/* Bouton plein bleu nuit : texte beige à 14,2:1. */
.btn--sombre {
  background: var(--c-900);
  color: var(--c-100);
}
.btn--sombre:hover {
  background: var(--c-800);
  box-shadow: 0 10px 24px -10px rgba(18, 9, 40, 0.6);
}

/* Bouton contour bleu nuit, pour le choix secondaire. */
.btn--contour {
  background: transparent;
  color: var(--c-900);
  border: 1.5px solid var(--c-900);
}
.btn--contour:hover { background: var(--c-900); color: var(--c-100); }

/* ---------- Police du grand titre ---------- */
/* Instrument Serif (licence libre OFL, voir fonts/LICENSE-InstrumentSerif.txt),
   réservée au titre de l'accueil. Hébergée sur le site plutôt que chargée
   depuis Google Fonts : aucune adresse IP de visiteur ne part chez un tiers. */
@font-face {
  font-family: "Instrument Serif";
  src: url("../fonts/instrument-serif.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Instrument Serif";
  src: url("../fonts/instrument-serif-italic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

/* ---------- Header ---------- */
/* Trois zones : le logo à gauche, le menu dans une pastille au centre, le
   bouton Contact à droite. Le fond est à peine transparent et flouté : le
   contenu se devine dessous quand on fait défiler. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(225, 221, 216, 0.88);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}

.site-header__inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 24px;
  max-width: 1280px;
  margin: 0 auto;
  padding: 14px 32px;
}

.site-logo {
  justify-self: start;
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Le sigle argenté posé dans un carré bleu nuit, comme sur la maquette. */
.site-logo__marque {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  background: var(--c-900);
  transition: transform 0.35s var(--ease-out);
}

.site-logo img {
  height: 26px;
  width: auto;
}
/* Le logo pivote légèrement au survol : un petit clin d'œil, sans
   déplacer le texte à côté (transform seul). */
.site-logo:hover .site-logo__marque { transform: rotate(-8deg) scale(1.06); }

.site-logo__text {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--c-900);
}

/* La pastille du menu : bleu nuit à 7 % sur le beige (#D3CECC). Le texte
   --c-700 y tient 4,7:1. */
.site-nav {
  position: relative; /* référence de position de la pastille glissante */
  display: flex;
  gap: 2px;
  padding: 4px;
  border-radius: 999px;
  background: rgba(18, 9, 40, 0.07);
}

.site-nav a {
  position: relative;
  z-index: 1; /* au-dessus de la pastille sombre */
  padding: 8px 16px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 500;
  color: var(--text-muted);
  transition: color 0.25s ease;
}
.site-nav a:hover,
.site-nav a[aria-current] { color: var(--c-900); }

/* Le lien sous la pastille sombre passe en beige (14,2:1). La classe est
   posée par js/reveal.js, qui fait glisser la pastille. [aria-current] sans
   valeur : "page" sur les pages secondaires, "location" posé par le
   scroll-spy sur l'accueil. */
.site-nav a.sous-pastille { color: var(--c-100); }

/* Repli pour les pages où js/reveal.js n'est pas chargé (pages légales) :
   la pastille est posée directement sur le lien actif. */
.site-nav:not(.has-indicator) a[aria-current] {
  background: var(--c-900);
  color: var(--c-100);
}

/* Pastille unique qui glisse sous le lien actif, et suit la souris et le
   clavier. Sa position et sa largeur sont posées par js/reveal.js, la
   transition fait le reste. */
.site-nav__indicator {
  position: absolute;
  top: 4px;
  bottom: 4px;
  left: 0;
  width: 0;
  border-radius: 999px;
  background: var(--c-900);
  opacity: 0;
  transform: translateX(0);
  transition: transform 0.4s var(--ease-out), width 0.4s var(--ease-out), opacity 0.2s ease;
  pointer-events: none;
}
.site-nav__indicator.is-instant { transition: none; }

/* Contact : le seul bouton violet du header. Texte beige sur violet
   strong : 5,06:1. */
.site-header__cta {
  justify-self: end;
  padding: 10px 20px;
  font-size: 14px;
  background: var(--accent-violet-strong);
  color: var(--c-100);
}
.site-header__cta:hover {
  background: var(--accent-violet-strong-hover);
  box-shadow: 0 10px 24px -10px rgba(102, 57, 209, 0.65);
}
/* Flèche décorative : texte alternatif vide, elle ne s'ajoute pas au nom
   du lien lu par un lecteur d'écran. */
.site-header__cta::after {
  content: '↗';
  content: '↗' / '';
  display: inline-block;
  transition: transform 0.25s var(--ease-out);
}
.site-header__cta:hover::after { transform: translate(2px, -2px); }

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 40px;
  height: 40px;
  border: none;
  background: transparent;
  cursor: pointer;
}
.nav-toggle span {
  display: block;
  height: 2px;
  background: var(--c-900);
  border-radius: 2px;
}

/* Sous 900 px, la pastille et ses 5 liens ne tiennent plus entre le logo et
   le bouton Contact : le menu se replie derrière le bouton « burger ». */
@media (max-width: 900px) {
  .site-header__inner {
    grid-template-columns: 1fr auto auto;
    gap: 12px;
    padding: 12px 20px;
  }
  .site-nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    display: none;
    flex-direction: column;
    gap: 0;
    padding: 8px 20px 16px;
    border-radius: 0;
    background: var(--c-100);
    border-bottom: 1px solid var(--c-300);
  }
  .site-nav.is-open { display: flex; }
  .site-nav a {
    padding: 12px 0;
    border-radius: 0;
    border-bottom: 1px solid var(--c-300);
    font-size: 15px;
  }
  /* Menu empilé : pas de pastille glissante, le violet du lien actif
     suffit (5,06:1 sur le beige). */
  .site-nav__indicator { display: none; }
  .site-nav a.sous-pastille { color: var(--text-muted); }
  .site-nav a[aria-current],
  .site-nav:not(.has-indicator) a[aria-current] {
    background: none;
    color: var(--accent-violet-strong);
  }
  .nav-toggle { display: flex; }
}

/* ---------- Hero ---------- */
/* Tout est centré : le titre, le sous-titre, les deux boutons, puis la frise
   des étapes. Même voile beige que les autres sections (.section) sur les
   lignes du fond animé. */
.hero {
  padding: 64px 0 56px;
  text-align: center;
  background: rgba(225, 221, 216, 0.6);
}

.hero-title {
  margin: 0 0 28px;
  font-family: "Instrument Serif", Georgia, "Times New Roman", serif;
  font-size: clamp(52px, 8vw, 108px);
  font-weight: 400;
  line-height: 0.92;
  letter-spacing: -0.015em;
  color: var(--text-main);
}
.hero-title__ligne,
.hero-title__accent { display: block; }
/* Grand texte : le violet strong (5,06:1) dépasse largement le seuil de 3:1.
   Le petit espace en bas garde la place de la queue du « q ». */
.hero-title__accent {
  padding-bottom: 0.06em;
  font-style: italic;
  color: var(--accent-violet-strong);
}

.hero-subtitle {
  max-width: 34em;
  margin: 0 auto;
  font-size: 18px;
  color: var(--text-muted);
  text-wrap: balance;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: 32px;
}

.hero-note {
  max-width: 60em;
  margin: 28px auto 0;
  font-size: 13px;
  color: var(--text-muted);
  text-wrap: balance;
}
/* Icône dans la ligne de texte : elle reste collée au premier mot, même
   quand la note passe sur plusieurs lignes. */
.hero-note svg {
  display: inline-block;
  width: 15px;
  height: 15px;
  margin-right: 6px;
  vertical-align: -3px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
}

/* ---------- Frise des étapes (hero) ---------- */
/* Une carte sombre glisse au-dessus d'une frise de 5 nœuds. js/script.js ne
   fait que poser le numéro de l'étape active dans --n (0 à 4) et les états
   des nœuds ; le CSS en déduit tout le reste.
   La frise est un « conteneur » : 20cqw = la largeur d'une des 5 colonnes,
   quelle que soit la taille de l'écran. Le centre du nœud actif est donc à
   (n + 0,5) × 20cqw du bord gauche. */
.etapes {
  --pastille: 52px;
  container-type: inline-size;
  margin-top: 56px;
  text-align: left;
}

.etapes__scene {
  --carte: min(300px, 100cqw);
  --x: calc((var(--n, 0) + 0.5) * 20cqw);
  position: relative;
  padding-bottom: 22px; /* la place de la tige */
}

/* La carte se centre sur le nœud actif, sans jamais dépasser des bords de
   la frise (clamp) : aux deux extrémités, elle reste calée contre le bord. */
.etapes__carte {
  display: grid;
  width: var(--carte);
  padding: 16px 16px 18px;
  border-radius: 16px;
  background: linear-gradient(180deg, var(--c-800), var(--c-900));
  box-shadow:
    0 24px 48px -20px rgba(18, 9, 40, 0.55),
    0 8px 18px -10px rgba(18, 9, 40, 0.35);
  transform: translateX(clamp(0px, calc(var(--x) - var(--carte) / 2), calc(100cqw - var(--carte))));
  transition: transform 0.6s var(--ease-out);
}

/* Les 5 panneaux sont empilés dans la même case : la carte prend la hauteur
   du plus grand et ne change jamais de taille. Seul l'actif est visible ;
   visibility retire aussi les autres de la lecture d'écran. Le sortant
   s'efface d'abord, l'entrant apparaît juste derrière. */
.etape {
  grid-area: 1 / 1;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0s linear 0.3s;
}
.etape.is-active {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.35s ease 0.12s, visibility 0s;
}

/* Textes clairs sur la carte sombre : --c-500 tient 5,5:1 et --c-300
   7,4:1 sur --c-800, le plus clair des deux fonds du dégradé. */
.etape__num {
  margin: 0;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--c-500);
}
.etape__titre {
  margin: 4px 0 10px;
  font-size: 18px;
  font-weight: 700;
  color: var(--c-100);
}
.etape__dessin {
  display: block;
  width: 100%;
  height: 156px;
  overflow: visible;
}
.etape__dessin text { font-family: var(--font-base); }
.etape__texte {
  min-height: 2.9em; /* deux lignes, pour que la barre ne saute pas */
  margin: 10px 0 0;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--c-300);
}

/* La barre de la carte se remplit en 4 s : c'est elle qui donne le rythme.
   À la fin de son remplissage, js/script.js passe à l'étape suivante. La
   mettre en pause suspend donc toute la boucle : au survol d'un rond
   d'étape (et seulement d'un rond : ni la carte, ni la ligne, ni les
   noms), au clavier, hors de l'écran et pendant l'écran de chargement. */
.etapes__barre {
  grid-area: 2 / 1;
  display: block;
  height: 3px;
  margin-top: 12px;
  border-radius: 2px;
  background: linear-gradient(var(--accent-violet-light), var(--accent-violet-light)) no-repeat left / 0% 100%, var(--c-700);
}
.etapes__barre.est-en-cours { animation: etapes-compte 4s linear forwards; }

html.chargement .etapes__barre,
.etapes.est-hors-ecran .etapes__barre,
.etapes:has(:focus-visible) .etapes__barre { animation-play-state: paused; }
@media (hover: hover) {
  .etapes:has(.etapes__pastille:hover) .etapes__barre { animation-play-state: paused; }
}

@keyframes etapes-compte {
  to { background-size: 100% 100%, auto; }
}

/* La tige relie le bas de la carte au nœud actif. */
.etapes__tige {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 2px;
  height: 22px;
  background: var(--accent-violet);
  transform: translateX(calc(var(--x) - 1px));
  transition: transform 0.6s var(--ease-out);
}

/* La frise : 5 colonnes égales. La ligne grise relie le centre du premier
   nœud au centre du dernier (de 10 % à 90 %) ; la violette par-dessus se
   remplit jusqu'au nœud actif, au même rythme que la carte glisse. */
.etapes__frise {
  position: relative;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  margin: 0;
  padding: 0;
  list-style: none;
}
.etapes__frise::before,
.etapes__frise::after {
  content: '';
  position: absolute;
  top: calc(var(--pastille) / 2 - 1px);
  left: 10%;
  width: 80%;
  height: 2px;
  background: var(--c-500);
}
.etapes__frise::after {
  background: var(--accent-violet);
  transform-origin: left center;
  transform: scaleX(calc(var(--n, 0) / 4));
  transition: transform 0.6s var(--ease-out);
}

.etapes__frise li {
  position: relative;
  z-index: 1; /* nœuds au-dessus des lignes */
}

.etapes__noeud {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--text-muted);
  cursor: pointer;
}

/* Nœud à venir : contour, numéro en --c-700 (5,4:1 sur le beige). */
.etapes__pastille {
  display: grid;
  place-items: center;
  width: var(--pastille);
  height: var(--pastille);
  border: 1.5px solid var(--c-600);
  border-radius: 50%;
  background: var(--c-100);
  font-size: 15px;
  font-weight: 700;
  color: var(--c-700);
  transition:
    background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease,
    box-shadow 0.3s ease, transform 0.3s var(--ease-out);
}
.etapes__chiffre,
.etapes__coche {
  grid-area: 1 / 1;
  transition: opacity 0.25s ease, transform 0.3s var(--ease-out);
}
.etapes__coche {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
  transform: scale(0.5);
}
.etapes__nom {
  font-size: 14px;
  font-weight: 500;
  line-height: 1.3;
  text-align: center;
}
.etapes__pastille:hover {
  border-color: var(--accent-violet);
  transform: scale(1.06);
}
.etapes__noeud:focus-visible { outline: none; }
.etapes__noeud:focus-visible .etapes__pastille {
  outline: 2px solid var(--accent-violet);
  outline-offset: 10px;
}

/* Étape franchie : fond violet, le numéro laisse place à une coche.
   Beige sur violet strong : 5,06:1. */
.est-fait .etapes__pastille,
.etapes__noeud[aria-current] .etapes__pastille {
  border-color: var(--accent-violet-strong);
  background: var(--accent-violet-strong);
  color: var(--c-100);
}
.est-fait .etapes__chiffre { opacity: 0; transform: scale(0.5); }
.est-fait .etapes__coche { opacity: 1; transform: none; }
.est-fait .etapes__nom { color: var(--text-main); }

/* Étape en cours : numéro sur fond violet, entouré d'un halo. */
.etapes__noeud[aria-current] .etapes__pastille { box-shadow: 0 0 0 7px rgba(117, 66, 239, 0.2); }
.etapes__noeud[aria-current] .etapes__nom {
  color: var(--text-main);
  font-weight: 700;
}

/* ---------- Dessins des étapes ---------- */
/* Ce sont les dessins de l'ancienne animation du téléphone (classes pa-…).
   Chaque élément entre en scène quand son étape devient active ; --d le
   décale à l'intérieur de l'étape. Quand l'étape repart, ses éléments
   reviennent simplement à leur état final, invisible puisque le panneau
   s'efface : ils sont prêts pour le tour suivant.
   Couleurs : la carte est sombre, les cinq couleurs de marque y sont
   permises — y compris le doré et le bleu ciel, réservés aux fonds sombres. */
.pa-adresse {
  font-size: 5.5px;
  font-weight: 600;
  fill: var(--c-700);
}
.pa-url {
  font-size: 10px;
  font-weight: 700;
  fill: var(--c-900);
}

.pa-pop,
.pa-trace-x,
.pa-trace-y,
.pa-fondu,
.pa-trait,
.pa-tampon,
.pa-etincelle,
.pa-curseur,
.pa-fusee,
.pa-confetti {
  transform-box: fill-box;
  transform-origin: center;
  animation-duration: 0.5s;
  animation-fill-mode: both;
  animation-timing-function: var(--ease-out);
  /* +0,15 s : on attend que le panneau ait commencé à apparaître. */
  animation-delay: calc(var(--d, 0s) + 0.15s);
}
.is-active .pa-pop       { animation-name: pa-pop; animation-duration: 0.45s; }
.is-active .pa-trace-x   { animation-name: pa-trace-x; }
.is-active .pa-trace-y   { animation-name: pa-trace-y; }
.is-active .pa-fondu     { animation-name: pa-fondu; animation-duration: 0.4s; }
.is-active .pa-trait     { animation-name: pa-trait; animation-duration: 0.6s; }
.is-active .pa-tampon    { animation-name: pa-tampon; animation-duration: 0.45s; }
.is-active .pa-etincelle { animation-name: pa-etincelle; }
.is-active .pa-curseur   { animation-name: pa-curseur; animation-duration: 1s; }
.is-active .pa-fusee     { animation-name: pa-fusee; animation-duration: 1.2s; }
.is-active .pa-confetti  { animation-name: pa-confetti; animation-duration: 1s; }
.pa-trace-x { transform-origin: left center; }
.pa-trace-y { transform-origin: center top; }
.pa-curseur { transform-origin: left top; }
/* Les confettis finissent invisibles : c'est aussi leur état de repos. */
.pa-confetti { opacity: 0; }

/* Mouvements continus, indépendants des étapes : ils ne se voient que
   pendant l'étape qui les contient. --p décale un élément de ses voisins. */
.pa-point {
  transform-box: fill-box;
  animation: pa-saut 0.9s ease-in-out infinite;
  animation-delay: var(--p, 0s);
}
.pa-flamme {
  transform-box: fill-box;
  transform-origin: center top;
  animation: pa-flamme 0.16s ease-in-out infinite alternate;
}
.pa-vitesse {
  transform-box: fill-box;
  animation: pa-vitesse 0.7s linear infinite;
  animation-delay: var(--p, 0s);
}

/* Pendant l'écran de chargement, rien ne bouge encore : tout démarre au
   début quand le voile se lève (voir js/loader.js). */
html.chargement .etape * { animation-play-state: paused; }

@keyframes pa-pop {
  from { opacity: 0; transform: scale(0.4); }
  70%  { opacity: 1; transform: scale(1.08); }
  to   { opacity: 1; transform: none; }
}
@keyframes pa-trace-x {
  from { transform: scaleX(0); }
}
@keyframes pa-trace-y {
  from { transform: scaleY(0); }
}
@keyframes pa-fondu {
  from { opacity: 0; }
}
/* Trait qui se dessine : le tracé porte pathLength="1" et
   stroke-dasharray="1", un décalage de 1 le cache entièrement. */
@keyframes pa-trait {
  from { stroke-dashoffset: 1; }
  to   { stroke-dashoffset: 0; }
}
@keyframes pa-tampon {
  from { opacity: 0; transform: scale(1.8) rotate(-25deg); }
  60%  { opacity: 1; transform: scale(0.9) rotate(0deg); }
  to   { opacity: 1; transform: none; }
}
@keyframes pa-etincelle {
  from { opacity: 0; transform: scale(0) rotate(-90deg); }
  60%  { opacity: 1; transform: scale(1.3) rotate(0deg); }
  to   { opacity: 1; transform: none; }
}
/* Le curseur arrive, « clique » (petit enfoncement), puis reste. */
@keyframes pa-curseur {
  from { opacity: 0; transform: translate(34px, 46px); }
  20%  { opacity: 1; }
  60%  { transform: translate(0, 0) scale(1); }
  72%  { transform: translate(0, 0) scale(0.8); }
  to   { opacity: 1; transform: none; }
}
/* La fusée monte du bas de la carte jusqu'à sa place. */
@keyframes pa-fusee {
  from { transform: translateY(86px); }
}
/* --x / --y : direction de chaque confetti, posée sur l'élément. */
@keyframes pa-confetti {
  from { opacity: 0; transform: translate(0, 0) scale(0.3); }
  10%  { opacity: 1; }
  55%  { opacity: 1; transform: translate(var(--x), var(--y)) scale(1) rotate(90deg); }
  to   { opacity: 0; transform: translate(var(--x), calc(var(--y) + 12px)) scale(1) rotate(180deg); }
}
@keyframes pa-saut {
  0%, 60%, 100% { transform: translateY(0); }
  30%           { transform: translateY(-4px); }
}
@keyframes pa-flamme {
  from { transform: scaleY(1); }
  to   { transform: scaleY(0.7); }
}
@keyframes pa-vitesse {
  from { opacity: 0; transform: translateY(-30px); }
  30%  { opacity: 0.8; }
  to   { opacity: 0; transform: translateY(70px); }
}

@media (max-width: 720px) {
  .hero { padding: 40px 0; }
  .hero-subtitle { font-size: 16px; }
  .etapes {
    --pastille: 36px;
    margin-top: 48px;
  }
  .etapes__pastille { font-size: 12px; }
  .etapes__coche { width: 16px; height: 16px; }
  .etapes__nom { font-size: 11.5px; }
  .etapes__noeud { gap: 8px; }
  .etape__dessin { height: 140px; }
}

/* ---------- Hexora (à propos) ---------- */

.hexora-section .container {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 64px;
  align-items: center;
}

.hexora-section__media img {
  /* height:auto : garde les proportions malgré les attributs width/height (anti-saut de page) */
  height: auto;
  border-radius: var(--radius);
  border: 1px solid var(--c-500);
  transition: transform 0.5s var(--ease-out), box-shadow 0.5s ease;
}
.hexora-section__media img:hover {
  transform: scale(1.02);
  box-shadow: 0 24px 40px -24px rgba(18, 9, 40, 0.45);
}

.hexora-section p {
  color: var(--text-muted);
  margin: 0 0 18px;
  font-size: 16px;
}

/* ---------- Forfaits ---------- */

.forfaits-section__head {
  max-width: 640px;
  margin-bottom: 40px;
}
.forfaits-section__intro {
  margin: 0;
  font-size: 17px;
  color: var(--text-muted);
}

.forfaits {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--gap);
  align-items: stretch;
  /* Le badge de la carte mise en avant déborde au-dessus d'elle : sans cette
     marge, il serait rogné par le haut de la grille. */
  padding-top: 14px;
}

/* Même traitement de surface que les cartes d'actualités (fond, liseré,
   relief, soulèvement au survol) : les deux sections se suivent presque,
   elles doivent parler la même langue. */
.forfait {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 32px 28px 28px;
  background: var(--c-100);
  border: 1px solid var(--c-500);
  border-radius: var(--radius-lg);
  box-shadow: 0 6px 18px -14px rgba(18, 9, 40, 0.5);
  transition: transform 0.35s var(--ease-out), box-shadow 0.35s ease, border-color 0.35s ease;
}
.forfait:hover {
  transform: translateY(-6px);
  box-shadow: 0 22px 40px -22px rgba(18, 9, 40, 0.4);
  border-color: var(--c-700);
}

/* Carte mise en avant. Pas de classe dédiée : c'est la présence d'un texte
   dans le badge qui la déclenche, vider le badge rend la carte ordinaire.
   L'anneau passe par box-shadow et non par border-width, pour ne décaler
   d'aucun pixel le contenu de la carte par rapport à ses voisines. */
.forfait:has(.forfait__badge:not(:empty)) {
  border-color: var(--accent-violet);
  box-shadow: 0 0 0 1px var(--accent-violet), 0 24px 48px -28px rgba(117, 66, 239, 0.55);
}

.forfait__badge {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 5px 14px;
  border-radius: 999px;
  /* Beige sur violet strong : 5,06:1. */
  background: var(--accent-violet-strong);
  color: var(--c-100);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}
.forfait__badge:empty { display: none; }

.forfait__tag {
  align-self: flex-start;
  justify-self: start;   /* en grille (écran large), sans quoi la pastille s'étire */
  margin-bottom: 14px;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--c-900);
  color: var(--c-100);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
}
/* Même code que la plaquette : l'étiquette de la formule mise en avant passe
   au violet, les autres restent en bleu nuit. */
.forfait:has(.forfait__badge:not(:empty)) .forfait__tag {
  background: var(--accent-violet-strong);
}
.forfait__tag:empty { display: none; }

.forfait__nom {
  margin: 0 0 6px;
  font-size: clamp(20px, 2vw, 24px);
  font-weight: 700;
  line-height: 1.2;
  color: var(--text-main);
}
.forfait__meta {
  margin: 0 0 24px;
  font-size: 14px;
  color: var(--text-muted);
}

.forfait__prix {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0 0 24px;
}
.forfait__des {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-700);
}
/* Gros texte (> 24 px, gras) : le seuil est 3:1, le violet strong tient 5,06:1. */
.forfait__montant {
  font-size: clamp(40px, 4.2vw, 52px);
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--accent-violet-strong);
  font-variant-numeric: tabular-nums;
}
.forfait__devise {
  margin-left: 0.1em;
  font-size: 0.42em;
  font-weight: 700;
  letter-spacing: 0;
}

/* « Ce qui est inclus » : la ligne « Idéal pour » en tête, puis les points.
   Même présentation pour les deux — c'est une seule liste à l'œil. La ligne
   « Idéal pour » est un paragraphe à part (et non un point de la liste) parce
   que le public visé n'est pas une prestation incluse. */
.forfait__inclus {
  margin: 0 0 28px;
  padding: 22px 0 0;
  border-top: 1px solid var(--c-500);
}
.forfait__points {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* Public visé laissé vide : pas de ligne « Idéal pour » orpheline. */
.forfait__point--pour:has(.forfait__detail:empty) { display: none; }
.forfait__point {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: 14px;
  font-size: 15px;
  line-height: 1.4;
}
.forfait__point:last-child { margin-bottom: 0; }
.forfait__coche {
  flex: 0 0 auto;
  font-weight: 700;
  color: var(--accent-violet-strong);
}
.forfait__point strong {
  display: block;
  font-weight: 600;
  color: var(--text-main);
}
.forfait__detail {
  display: block;
  font-size: 14px;
  color: var(--text-muted);
}
.forfait__detail:empty { display: none; }

/* Le bouton descend toujours en bas de sa carte : les boutons s'alignent d'une
   carte à l'autre, quel que soit le nombre de points listés.
   Hiérarchie : le site réserve l'aplat violet à ses CTA principaux. Ici, seule
   la formule mise en avant le reçoit ; les autres ont le contour. */
.forfait__cta {
  width: 100%;
  justify-content: center;
  margin-top: auto;
  background: transparent;
  color: var(--accent-violet-strong);
  border-color: var(--accent-violet-strong);
}
.forfait__cta:hover {
  background: var(--accent-violet-strong);
  color: var(--c-100);
}
.forfait:has(.forfait__badge:not(:empty)) .forfait__cta {
  background: var(--accent-violet-strong);
  color: var(--c-100);
}
.forfait:has(.forfait__badge:not(:empty)) .forfait__cta:hover {
  background: var(--accent-violet-strong-hover);
  border-color: var(--accent-violet-strong-hover);
  box-shadow: 0 10px 24px -10px rgba(102, 57, 209, 0.65);
}

/* Toute la carte ouvre la page de la formule. Le lien reste le bouton — un
   lecteur d'écran annonce son seul texte, pas la carte entière —, mais sa zone
   de clic s'étend à toute la carte.
   Le bouton ne se soulève pas au survol, contrairement aux autres : un
   transform ferait de lui le repère de ce ::after, qui se rétracterait alors
   sur le bouton en plein survol. C'est la carte qui se soulève, et son bouton
   qui s'allume, où que soit la souris. */
.forfait__cta::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: var(--radius-lg);
}
.forfait__cta:hover,
.forfait__cta:active { transform: none; }
.forfait:hover .forfait__cta {
  background: var(--accent-violet-strong);
  color: var(--c-100);
}
.forfait:has(.forfait__badge:not(:empty)):hover .forfait__cta {
  background: var(--accent-violet-strong-hover);
  border-color: var(--accent-violet-strong-hover);
}
/* Au clavier, c'est la carte entière qui porte l'anneau de focus : c'est
   elle qui s'ouvre. */
.forfait:has(.forfait__cta:focus-visible) {
  outline: 2px solid var(--accent-violet);
  outline-offset: 4px;
}
.forfait__cta:focus-visible { outline: none; }

.forfaits-section__note {
  margin: 28px 0 0;
  text-align: center;
  font-size: 14px;
  color: var(--text-muted);
}
.forfaits-section__note:empty { display: none; }

/* Écran large : les cartes sont côte à côte, et leurs rangées s'alignent
   d'une carte à l'autre (sous-grille). Sans cela, un nom de formule qui passe
   sur deux lignes décale le prix et la liste de sa carte par rapport aux
   voisines — les montants ne se lisent plus sur une même ligne.
   Six rangées : étiquette, nom, sous-titre, prix, ce qui est inclus, bouton.
   Le badge est positionné en absolu, il n'en occupe aucune. */
@media (min-width: 901px) {
  .forfait {
    display: grid;
    grid-row: span 6;
    grid-template-rows: subgrid;
    row-gap: 0;
  }
  /* Étiquette vide : la rangée doit rester occupée, sinon tout le contenu de
     cette carte remonterait d'un cran et la sous-grille serait décalée. */
  .forfait__tag:empty {
    display: block;
    visibility: hidden;
  }
}

/* Sous 900 px, trois colonnes deviendraient trop étroites pour un prix de
   cette taille : les cartes s'empilent, sur une largeur de lecture confortable. */
@media (max-width: 900px) {
  .forfaits {
    grid-template-columns: 1fr;
    max-width: 520px;
    margin: 0 auto;
  }
}

/* ---------- Actualités ---------- */

.news-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: 40px;
  flex-wrap: wrap;
}

.news-carousel {
  position: relative;
}

.news-track {
  display: flex;
  gap: var(--gap);
  overflow-x: auto;
  scroll-behavior: smooth;
  scroll-snap-type: x mandatory;
  padding-bottom: 8px;
  scrollbar-width: none;
}
.news-track::-webkit-scrollbar { display: none; }

.news-card {
  scroll-snap-align: start;
  flex: 0 0 calc((100% - (2 * var(--gap))) / 3);
  background: var(--c-100);
  border: 1px solid var(--c-500);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  /* La carte est du même beige que la section : sans ce léger relief, elle
     s'y fondrait et seul le liseré la distinguerait. */
  box-shadow: 0 6px 18px -14px rgba(18, 9, 40, 0.5);
  transition: transform 0.35s var(--ease-out), box-shadow 0.35s ease, border-color 0.35s ease;
}
/* La carte se soulève et son image zoome doucement au survol — le même
   geste que les cartes projets de la référence, en plus retenu. */
.news-card:hover {
  transform: translateY(-6px);
  box-shadow: 0 22px 40px -22px rgba(18, 9, 40, 0.4);
  border-color: var(--c-700);
}

.news-card__media {
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--c-300);
}
.news-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.6s var(--ease-out);
}
.news-card:hover .news-card__media img { transform: scale(1.05); }

.news-card__body {
  padding: 22px;
  display: flex;
  flex-direction: column;
  flex: 1;
}

.news-card__date {
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 8px;
}

.news-card__title {
  font-size: 19px;
  font-weight: 700;
  margin: 0 0 10px;
}

.news-card__excerpt {
  color: var(--text-muted);
  font-size: 15px;
  margin: 0 0 20px;
  flex: 1;
}

.news-card__link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 14px;
  font-weight: 600;
  color: var(--accent-violet-strong);
  border-bottom: 1px solid var(--accent-violet-strong);
  align-self: flex-start;
}
/* Flèche en pseudo-élément (le libellé est dans le <span>). */
.news-card__link::after {
  content: '→';
  content: '→' / ''; /* décorative : masquée des lecteurs d'écran, voir .btn--arrow */
  transition: transform 0.25s var(--ease-out);
}
.news-card__link:hover::after { transform: translateX(4px); }

.carousel-nav {
  display: flex;
  gap: 10px;
}

/* Boutons de contrôle secondaires : un simple accent violet sur la
   bordure/l'icône (pas d'aplat plein) pour garder les 2 vrais CTA du
   site visuellement premiers — voir le commentaire de :root. */
.carousel-nav__btn {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--accent-violet-strong);
  background: var(--c-100);
  color: var(--accent-violet-strong);
  font-size: 18px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.2s ease, color 0.2s ease, opacity 0.2s ease, transform 0.2s var(--ease-out);
}
.carousel-nav__btn:hover { background: var(--accent-violet-strong); color: var(--c-100); transform: scale(1.1); }
.carousel-nav__btn:active { transform: scale(0.94); }
.carousel-nav__btn:disabled {
  opacity: 0.35;
  cursor: default;
  pointer-events: none;
}

/* ---------- Projets (défilement horizontal) ---------- */

/* .section.projects-section (et pas .projects-section seul) pour passer devant
   le padding de .section, y compris celui de la media query mobile : ce padding
   rognerait la course du sticky et le mur n'atteindrait jamais sa fin. */
.section.projects-section {
  padding: 0;
}

.projects-section {
  position: relative;
  /* Pas de fond propre : « Notre agence », « Actualités » et « Projets »
     partagent celui de .section pour former une seule surface continue.
     Seul le hero et le pied de page restent sombres. */
  /* Hauteur réelle posée par JS : un écran + la longueur du mur. C'est cette
     hauteur « en trop » qui fournit la course de scroll pendant laquelle
     .projects-pin reste accroché et les tableaux défilent.
     Surtout pas d'overflow:hidden ici : ça transformerait la section en
     conteneur de défilement et casserait le sticky de .projects-pin. */
  min-height: calc(100vh - var(--header-h));
  scroll-margin-top: var(--header-h);
}

.projects-pin {
  position: sticky;
  top: var(--header-h);
  height: calc(100vh - var(--header-h));
  min-height: 480px;
  padding: 28px 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  overflow: hidden;
}

.projects-section__head {
  padding: 0 24px;
  max-width: var(--container);
  margin: 0 auto 20px;
}

/* Mur de projets — défilement horizontal avec inertie (moteur : js/horizontal-scroller.js + js/wall.js) */
.projects-section {
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

.projects-viewport {
  position: relative;
  width: 100%;
  /* Occupe l'espace restant dans la section (hauteur fixe, cf. .projects-section)
     plutôt qu'une hauteur en vh qui pourrait dépasser l'écran. */
  flex: 1 1 auto;
  min-height: 220px;
  overflow: hidden;
  cursor: grab;
  touch-action: pan-y;
  background: transparent;
}
.projects-viewport.is-grabbing { cursor: grabbing; }
/* Violet pur : anneau de focus (seuil 3:1) sur le beige de la section,
   où il tient 4,11:1 — même violet que tous les autres anneaux de focus
   du site posés sur fond clair. */
.projects-viewport:focus-visible { outline: 2px solid var(--accent-violet); outline-offset: -2px; }

.projects-scene {
  display: flex;
  height: 100%;
  width: max-content;
  will-change: transform;
}

.wall__rail {
  display: flex;
  align-items: center;
  height: 100%;
  gap: clamp(18px, 2.2vw, 30px);
  padding: 0 clamp(2rem, 7vw, 10rem);
}

/* Enveloppe nue : js/projects-wall.js lui pose un scale et une opacité
   selon sa distance au centre de l'écran. Surtout pas de transition ici,
   ni de transform au survol — tout le visuel est sur .poster__card.
   Hauteur prise sur la bande disponible, largeur déduite des proportions
   de la carte (400 × 544) ; 106vw plafonne la hauteur pour que la carte
   ne dépasse jamais ~78 % de la largeur d'un écran de téléphone. */
.poster {
  flex: 0 0 auto;
  height: min(92%, 560px, 106vw);
  aspect-ratio: 400 / 544;
}

/* La carte : image pleine surface, numéro en haut, texte en bas sur un
   dégradé sombre. isolation + z-index:-1 posent l'image et le dégradé
   SOUS le texte sans le positionner : le lien étendu (.poster__open::after)
   peut ainsi couvrir toute la carte, et non le seul bloc de texte. */
.poster__card {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  height: 100%;
  border-radius: 22px;
  overflow: hidden;
  background: var(--c-800);
  box-shadow: 0 22px 44px -18px rgba(18, 9, 40, 0.45);
  transition: transform 0.6s var(--ease), box-shadow 0.6s var(--ease);
}
.poster__card:hover {
  transform: translateY(-6px);
  box-shadow: 0 30px 54px -20px rgba(18, 9, 40, 0.55);
}

.poster__img {
  position: absolute;
  inset: 0;
  z-index: -1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.8s var(--ease);
}
.poster__card:hover .poster__img { transform: scale(1.06); }

/* Dégradé de lisibilité. Assez dense là où se trouve le texte pour que le
   doré de la catégorie tienne ~5:1 même sur une image très claire. */
.poster__card::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to bottom,
    rgba(18, 9, 40, 0) 35%,
    rgba(18, 9, 40, 0.6) 70%,
    rgba(18, 9, 40, 0.92) 100%);
}

/* Numéro : un repère visuel, pas une information (aria-hidden). */
.poster__num {
  position: absolute;
  top: 18px;
  left: 22px;
  font-size: clamp(28px, 2.8vw, 40px);
  font-weight: 800;
  line-height: 1;
  color: rgba(255, 255, 255, 0.78);
  text-shadow: 0 2px 14px rgba(18, 9, 40, 0.45);
}

.poster__info {
  padding: clamp(18px, 1.8vw, 26px);
  color: var(--c-100);
}
/* Catégorie : doré sur le bas sombre de la carte (le doré n'est permis que
   sur fond sombre — c'est le cas ici, grâce au dégradé). */
.poster__cat {
  margin: 0 0 8px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent-gold);
}
.poster__title {
  margin: 0;
  font-size: clamp(18px, 1.7vw, 23px);
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.02em;
  text-transform: uppercase;
}
.poster__open {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  text-decoration: none;
  text-transform: inherit;
  cursor: pointer;
}
/* Toute la carte est la zone de clic du lien (voir .poster__card). */
.poster__open::after {
  content: '';
  position: absolute;
  inset: 0;
}
/* Au clavier, c'est la carte entière qui porte l'anneau : c'est elle qui
   s'ouvre. Violet pur (bordure, seuil 3:1) : 4,11:1 sur le beige. */
.poster__open:focus-visible { outline: none; }
.poster__card:has(.poster__open:focus-visible) {
  outline: 2px solid var(--accent-violet);
  outline-offset: 4px;
}

@media (max-width: 860px) {
  .wall__rail { padding: 0 clamp(1.4rem, 6vw, 3rem); }
  .poster { height: min(86%, 480px, 106vw); }
}

.projects-hint {
  text-align: center;
  font-size: 13px;
  color: var(--text-muted);
  margin-top: 14px;
  flex: 0 0 auto;
}

.projects-noscript {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 24px;
}
/* Violet strong : 5,06:1 sur le beige de la section. */
.projects-noscript a {
  color: var(--accent-violet-strong);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ---------- Contact ---------- */

.contact-section__inner {
  max-width: 640px;
  margin: 0 auto;
  text-align: center;
}

.contact-section .form-field,
.contact-section form {
  text-align: left;
}

.contact-section p.lead {
  color: var(--text-muted);
  margin: 0 auto 28px;
  font-size: 16px;
  max-width: 480px;
}

.contact-section .contact-info {
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px 24px;
  margin-bottom: 40px;
}

.contact-info {
  margin-bottom: 32px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* E-mail/téléphone : ce sont des liens « actifs » au sens de la
   consigne (ils déclenchent une action). --accent-violet-strong,
   contexte fond clair (5,06:1). */
.contact-info a {
  font-weight: 600;
  color: var(--accent-violet-strong);
  /* Soulignement qui se dessine de gauche à droite au survol : un dégradé
     de 1px en fond, dont seule la largeur change (0 → 100 %). */
  background: linear-gradient(currentColor, currentColor) no-repeat left bottom / 0 1px;
  transition: background-size 0.3s var(--ease-out);
}
.contact-info a:hover,
.contact-info a:focus-visible { background-size: 100% 1px; }

.form-field {
  margin-bottom: 20px;
}

.form-field label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 8px;
  color: var(--text-main);
}

.form-field input,
.form-field textarea,
.form-field select {
  width: 100%;
  padding: 13px 16px;
  border: 1px solid var(--c-500);
  border-radius: var(--radius);
  background: var(--c-100);
  color: var(--text-main);
  font-family: inherit;
  font-size: 15px;
  transition: border-color 0.2s ease, box-shadow 0.25s ease;
}

.form-field textarea { resize: vertical; min-height: 140px; }

/* Menu « Sujet » : même champ que les autres, flèche dessinée à la place de
   celle du système (qui change d'un navigateur à l'autre). Bleu nuit 800 :
   un repère d'interface, 10,2:1 sur le beige du champ. */
.form-field select {
  appearance: none;
  padding-right: 44px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%23312942' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
  background-size: 12px 8px;
  cursor: pointer;
}
/* Tant qu'aucun sujet n'est choisi, la ligne d'invite a l'air d'une
   indication, comme le texte d'exemple des autres champs. */
.form-field select:has(option[value=""]:checked) { color: var(--c-700); }
.form-field select option,
.form-field select optgroup { color: var(--text-main); }

.form-field input:focus,
.form-field textarea:focus,
.form-field select:focus {
  outline: none;
  /* Violet pur : bordure (seuil 3:1), 4,11:1 sur le fond clair du champ —
     marque le champ actif, cohérent avec « liens actifs » étendu aux
     champs de formulaire. Le halo qui s'élargit en douceur rend la prise
     de focus visible du coin de l'œil. */
  border-color: var(--accent-violet);
  box-shadow: 0 0 0 4px rgba(117, 66, 239, 0.18);
}

.form-success {
  margin-top: 18px;
  padding: 14px 18px;
  border-radius: var(--radius);
  background: var(--c-300);
  color: var(--c-900);
  font-size: 14px;
  display: none;
}
.form-success.is-visible { display: block; animation: fade-up 0.4s var(--ease-out) both; }

/* ---------- Page projet (squelette) ---------- */

.project-hero {
  background: var(--bg-dark);
  color: var(--text-on-dark);
  padding: 64px 0 48px;
}

.project-hero__client {
  color: var(--text-on-dark-muted);
  font-size: 14px;
  margin-bottom: 10px;
}

.project-hero__title {
  font-size: clamp(30px, 5vw, 48px);
  font-weight: 800;
  margin: 0;
}

.project-detail__media {
  margin: -48px auto 0;
  max-width: var(--container);
  padding: 0 24px;
}
.project-detail__media img {
  width: 100%;
  /* L'image du projet est la même que celle de la carte (format portrait) :
     on n'en montre ici que le haut, comme le premier écran du site. */
  aspect-ratio: 16 / 10;
  object-fit: cover;
  object-position: top;
  border-radius: var(--radius);
  border: 1px solid var(--c-500);
}

.project-detail__body {
  max-width: 760px;
  margin: 56px auto 0;
  padding: 0 24px;
}

.project-detail__body p {
  color: var(--text-muted);
  font-size: 17px;
  margin-bottom: 20px;
}

/* Contenu détaillé : texte simple, les retours à la ligne sont respectés. */
.project-detail__content {
  white-space: pre-line;
}

/* ---------- Page projet : blocs ---------- */

/* Informations en bref. Deux colonnes sur écran large, empilées dessous :
   un <dl> se prête exactement à cette paire libellé / valeur. */
.project-facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 10px 24px;
  margin: 0 0 32px;
  padding: 24px;
  border: 1px solid var(--c-500);
  border-left: 3px solid var(--accent-violet);
  border-radius: var(--radius);
}
.project-facts dt {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--c-800);
}
.project-facts dd {
  margin: 0;
  font-size: 16px;
  color: var(--text-main);
}
.project-facts a {
  color: var(--accent-violet-strong);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.project-facts a:hover { color: var(--accent-violet-strong-hover); }

/* Sections « titre + texte » de l'étude de cas. */
.project-block { margin-bottom: 32px; }
.project-block__title {
  font-size: clamp(20px, 2.4vw, 26px);
  font-weight: 700;
  margin: 0 0 12px;
  color: var(--text-main);
}
/* Même traitement que le contenu détaillé : texte simple, mais les retours
   à la ligne sont respectés. */
.project-block__text {
  white-space: pre-line;
}

/* Galerie. Les images gardent leurs proportions : une capture d'écran de
   site est large, une photo de terrain peut être haute — imposer un format
   unique les rognerait toutes de la même mauvaise façon. */
.project-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 20px;
  margin-bottom: 32px;
}
.project-gallery__item { margin: 0; }
.project-gallery__item img {
  width: 100%;
  height: auto;
  border-radius: var(--radius);
  border: 1px solid var(--c-500);
}
.project-gallery__item figcaption {
  margin-top: 8px;
  font-size: 14px;
  color: var(--text-muted);
}

@media (max-width: 600px) {
  .project-facts {
    grid-template-columns: 1fr;
    gap: 4px 0;
    padding: 20px;
  }
  /* Empilé, chaque valeur doit respirer avant le libellé suivant. */
  .project-facts dd { margin-bottom: 12px; }
  .project-facts dd:last-child { margin-bottom: 0; }
}

.back-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  margin: 40px 0 60px;
  padding: 0 24px;
  max-width: var(--container);
  /* Lien actif sur fond clair : --accent-violet-strong (5,06:1). Avant,
     cette règle n'avait pas de couleur propre et héritait du texte
     principal — un lien de navigation ne s'y distinguait pas du tout. */
  color: var(--accent-violet-strong);
}
.back-link-wrap { max-width: var(--container); margin: 0 auto; }

/* ---------- Page d'un article (actualite-*.html) ---------- */

/* En-tête sombre, comme les pages formule et projet. */
.ar-hero {
  background: var(--bg-dark);
  color: var(--text-on-dark);
  padding: 56px 0 96px;
}
.ar-hero__inner { max-width: 820px; }
.ar-hero__date {
  margin: 0 0 12px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  /* Doré : réservé aux fonds sombres (9,57:1 sur le bleu nuit). */
  color: var(--accent-gold);
}
.ar-hero__date:empty { display: none; }
.ar-hero__titre {
  margin: 0 0 20px;
  font-size: clamp(30px, 4.6vw, 48px);
  font-weight: 800;
  line-height: 1.12;
}
.ar-hero__intro {
  max-width: 640px;
  margin: 0;
  font-size: 18px;
  line-height: 1.6;
  color: var(--c-300);
}

/* L'image chevauche le bas de l'en-tête, comme sur la page projet. */
.ar-media {
  max-width: 900px;
  margin: -64px auto 0;
  padding: 0 24px;
}
.ar-media img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: 0 24px 48px -28px rgba(18, 9, 40, 0.5);
}

.ar-corps {
  max-width: 720px;
  margin: 48px auto 0;
  padding: 0 24px;
}
/* Texte de l'article : les retours à la ligne et les lignes vides entre
   paragraphes sont respectés tels quels. */
.ar-corps__texte {
  margin: 0;
  font-size: 18px;
  line-height: 1.75;
  color: var(--text-main);
  white-space: pre-line;
}

@media (max-width: 600px) {
  .ar-hero { padding: 40px 0 80px; }
  .ar-corps__texte { font-size: 17px; }
}

/* ---------- Page d'une formule (forfait-*.html) ---------- */

/* En-tête sombre sous l'en-tête beige du site, comme la page projet. */
.fd-hero {
  background: var(--bg-dark);
  color: var(--text-on-dark);
  padding: 56px 0 72px;
}
.fd-hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 360px;
  gap: 56px;
  align-items: center;
}

/* Violet light sur bleu nuit : 5,30:1. */
.fd-retour {
  display: block;
  width: fit-content;
  margin-bottom: 28px;
  font-size: 14px;
  font-weight: 600;
  color: var(--accent-violet-light);
}
.fd-retour:hover { color: var(--c-100); }

.fd-hero__tag {
  display: block;
  width: fit-content;
  margin-bottom: 14px;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--accent-violet-strong);
  color: var(--c-100);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
}
.fd-hero__tag:empty { display: none; }

.fd-hero__titre {
  margin: 0 0 20px;
  font-size: clamp(32px, 5vw, 52px);
  font-weight: 800;
  line-height: 1.1;
}

/* « Idéal pour » : la phrase par laquelle un visiteur se reconnaît. Le doré
   n'est lisible que sur fond sombre (9,57:1 sur le bleu nuit) — c'est le cas. */
.fd-hero__pour { margin: 0 0 20px; }
.fd-hero__pour:has(.fd-hero__public:empty) { display: none; }
.fd-hero__pour-label {
  display: block;
  margin-bottom: 4px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-gold);
}
.fd-hero__public {
  font-size: 19px;
  font-weight: 600;
  color: var(--c-100);
}

.fd-hero__resume {
  max-width: 620px;
  margin: 0;
  font-size: 17px;
  line-height: 1.65;
  color: var(--c-300);
}

/* Encadré du prix : même surface et même prix que les cartes de l'accueil. */
.fd-carte {
  padding: 32px 28px 28px;
  background: var(--c-100);
  color: var(--text-main);
  border-radius: var(--radius-lg);
  box-shadow: 0 0 0 1px var(--accent-violet), 0 30px 60px -30px rgba(0, 0, 0, 0.6);
}
.fd-carte__des {
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-700);
}
.fd-carte__montant {
  margin: 2px 0 8px;
  font-size: clamp(40px, 4.5vw, 52px);
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--accent-violet-strong);
  font-variant-numeric: tabular-nums;
}
.fd-carte__devise {
  margin-left: 0.1em;
  font-size: 0.42em;
  font-weight: 700;
  letter-spacing: 0;
}
.fd-carte__meta {
  margin: 0 0 24px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--c-500);
  font-size: 14px;
  color: var(--text-muted);
}
.fd-carte__meta:empty { display: none; }
.fd-carte__cta {
  width: 100%;
  justify-content: center;
}
.fd-carte__note {
  margin: 12px 0 0;
  text-align: center;
  font-size: 13px;
  color: var(--text-muted);
}
.fd-carte__note:empty { display: none; }

/* Sections de la page : un simple filet entre deux, l'espace fait le reste. */
.fd-section { padding: 88px 0; }
.fd-section + .fd-section { border-top: 1px solid rgba(18, 9, 40, 0.1); }
.fd-section__head {
  max-width: 640px;
  margin-bottom: 40px;
}
.fd-section__head .section-title { margin-bottom: 12px; }
.fd-section__intro {
  margin: 0;
  font-size: 17px;
  color: var(--text-muted);
}
.fd-section__intro:empty { display: none; }
.fd-section__note {
  max-width: 860px;
  margin: 32px 0 0;
  font-size: 15px;
  color: var(--text-muted);
}
.fd-section__note:empty { display: none; }

/* Ce qui est inclus : une carte par point, son explication en dessous.
   Deux colonnes, pas trois : les formules comptent 3 à 5 points, et sur
   trois colonnes une formule à 4 points laissait une carte seule sur sa
   ligne. Une colonne sur mobile (voir plus bas). */
.fd-inclus {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap);
}
.fd-inclus__point {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 24px;
  background: var(--c-100);
  border: 1px solid var(--c-500);
  border-radius: var(--radius);
  box-shadow: 0 6px 18px -14px rgba(18, 9, 40, 0.5);
}
.fd-inclus__coche {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--accent-violet-strong);
  color: var(--c-100);
  font-size: 15px;
  font-weight: 700;
}
.fd-inclus__titre {
  margin: 4px 0 2px;
  font-size: 18px;
  font-weight: 700;
  line-height: 1.3;
}
.fd-inclus__detail {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--accent-violet-strong);
}
.fd-inclus__texte {
  margin: 10px 0 0;
  font-size: 15px;
  line-height: 1.6;
  color: var(--text-muted);
}
.fd-inclus__detail:empty,
.fd-inclus__texte:empty { display: none; }

/* Options : un tableau allégé, nom à gauche et prix à droite. Largeur
   limitée — étiré sur tout le conteneur, l'œil perdrait la ligne entre
   l'option et son prix. */
.fd-options:not(:has(.fd-option)) { display: none; }
.fd-options__liste {
  max-width: 860px;
  border-top: 1px solid var(--c-500);
}
.fd-option {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  padding: 20px 0;
  border-bottom: 1px solid var(--c-500);
}
.fd-option__titre {
  display: block;
  font-size: 17px;
  font-weight: 600;
}
.fd-option__detail {
  display: block;
  margin-top: 2px;
  font-size: 15px;
  color: var(--text-muted);
}
.fd-option__detail:empty { display: none; }
.fd-option__prix {
  flex: 0 0 auto;
  font-weight: 700;
  white-space: nowrap;
  color: var(--accent-violet-strong);
  font-variant-numeric: tabular-nums;
}

/* Étapes : une frise verticale numérotée. Le titre reste à l'écran pendant
   qu'on la parcourt (écran large seulement). */
.fd-etapes__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  gap: 64px;
  align-items: start;
}
.fd-etapes__head {
  position: sticky;
  top: calc(var(--header-h) + 32px);
  margin-bottom: 0;
}
.fd-etapes__liste {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: etape;
}
.fd-etape {
  position: relative;
  padding: 0 0 36px 64px;
  counter-increment: etape;
}
/* Le numéro est décoratif pour un lecteur d'écran : une liste ordonnée
   annonce déjà la position de chaque étape. La première ligne sert de
   repli aux navigateurs qui ignorent le texte alternatif de « content ». */
.fd-etape::before {
  content: counter(etape);
  content: counter(etape) / '';
  position: absolute;
  top: 0;
  left: 0;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--c-900);
  color: var(--c-100);
  font-weight: 700;
}
/* Le fil entre deux numéros. */
.fd-etape::after {
  content: '';
  position: absolute;
  top: 48px;
  bottom: 8px;
  left: 19px;
  width: 2px;
  background: var(--c-500);
}
.fd-etape:last-child { padding-bottom: 0; }
.fd-etape:last-child::after { display: none; }
.fd-etape__titre {
  margin: 7px 0 6px;
  font-size: 19px;
  font-weight: 700;
}
.fd-etape__texte {
  margin: 0;
  font-size: 16px;
  line-height: 1.6;
  color: var(--text-muted);
}

/* Questions fréquentes. Largeur de lecture limitée, alignée à gauche comme
   le reste de la page. */
.fd-faq__liste { max-width: 860px; }
.fd-faq:not(:has(.fd-faq__item)) { display: none; }
.fd-faq__item { border-bottom: 1px solid var(--c-500); }
.fd-faq__item:first-of-type { border-top: 1px solid var(--c-500); }
.fd-faq__question {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 22px 0;
  font-size: 17px;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}
.fd-faq__question::-webkit-details-marker { display: none; }
.fd-faq__question:hover { color: var(--accent-violet-strong); }
.fd-faq__question::after {
  content: '+';
  content: '+' / '';
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 1px solid var(--accent-violet-strong);
  border-radius: 50%;
  color: var(--accent-violet-strong);
  font-size: 18px;
  line-height: 1;
  transition: transform 0.25s var(--ease-out);
}
.fd-faq__item[open] .fd-faq__question::after { transform: rotate(45deg); }
.fd-faq__reponse {
  margin: 0;
  padding: 0 44px 24px 0;
  font-size: 16px;
  line-height: 1.65;
  color: var(--text-muted);
}

/* Appel final : encadré sombre sur le beige. */
.fd-fin__carte {
  padding: 64px 32px;
  border-radius: var(--radius-lg);
  background: var(--bg-dark);
  color: var(--text-on-dark);
  text-align: center;
}
.fd-fin__titre {
  margin: 0 0 14px;
  font-size: clamp(28px, 4vw, 40px);
  font-weight: 700;
}
.fd-fin__texte {
  max-width: 560px;
  margin: 0 auto 32px;
  font-size: 17px;
  color: var(--c-300);
}
.fd-fin__texte:empty { display: none; }
.fd-fin__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
}
/* Second choix : contour clair sur le bleu nuit (--c-500 : 7,6:1). */
.fd-fin__secondaire {
  background: transparent;
  color: var(--c-100);
  border-color: var(--c-500);
}
.fd-fin__secondaire:hover {
  background: var(--c-100);
  border-color: var(--c-100);
  color: var(--c-900);
}

@media (max-width: 900px) {
  .fd-hero__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 40px;
  }
  .fd-carte { max-width: 460px; }
  .fd-etapes__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 8px;
  }
  .fd-etapes__head { position: static; }
}

@media (max-width: 600px) {
  .fd-hero { padding: 40px 0 56px; }
  .fd-section { padding: 64px 0; }
  .fd-inclus { grid-template-columns: minmax(0, 1fr); }
  .fd-inclus__point { padding: 20px; }
  /* Le prix passe sous l'option plutôt que de l'écraser. */
  .fd-option {
    flex-direction: column;
    gap: 6px;
  }
  .fd-faq__reponse { padding-right: 0; }
  .fd-fin__carte { padding: 48px 20px; }
}

/* ---------- Footer ---------- */
/* Trois colonnes de texte à gauche (Navigation / Contact / Informations),
   les dessins au trait à droite (ordinateur + téléphone sur grand écran,
   téléphone seul sur mobile), une ligne de copyright dessous — sur le
   bleu nuit de la palette, le plus proche du noir de la maquette. */

.site-footer {
  position: relative;
  background: var(--c-900);
  color: var(--c-300);
  font-size: 14px;
}

.site-footer__inner {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: 48px;
  align-items: end;
  padding-top: 64px;
  padding-bottom: 40px;
}

.site-footer__cols {
  display: grid;
  grid-template-columns: repeat(3, auto);
  justify-content: start;
  gap: 32px 36px;
  text-align: center; /* comme la maquette : chaque colonne centrée sous son titre */
}

/* Titres de colonnes en doré : sur fond sombre, le doré tient 9,6:1 —
   c'est aussi la couleur des titres de la maquette. */
.site-footer__title {
  margin: 0 0 16px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-gold);
}

.site-footer__list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.site-footer__list a,
.site-footer__list button {
  color: var(--c-300); /* 10,3:1 sur le bleu nuit */
  text-decoration: none;
  transition: color 0.2s ease;
}
.site-footer__list a:hover,
.site-footer__list a:focus-visible,
.site-footer__list button:hover,
.site-footer__list button:focus-visible {
  color: var(--accent-violet-light); /* 5,3:1 sur le bleu nuit */
}

/* « Gérer les cookies » est un bouton et non un lien : il n'emmène nulle
   part, il rouvre la bannière. On lui rend juste l'apparence d'un lien. */
.site-footer__list button {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
}

/* Adresse : pre-line respecte les retours à la ligne. */
.site-footer__address {
  white-space: pre-line;
  line-height: 1.6;
}

/* Dessins : ordinateur et téléphone posés côte à côte, alignés sur leur
   base, calés à droite comme la maquette. Les SVG sont en beige sur fond
   transparent, ils se fondent donc sur n'importe quel fond sombre. */
.site-footer__art {
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  gap: 24px;
  min-width: 0;
}
/* Largeurs en % de la zone dessin, plafonnées : les deux images rétrécissent
   avec elle et ne peuvent jamais déborder sur les colonnes de texte.
   min-width:0 est indispensable — sans lui, un <img> dans un flex refuse de
   descendre sous sa largeur déclarée et déborde vers la gauche. */
.site-footer__art-desktop { width: 78%; max-width: 420px; min-width: 0; height: auto; }
.site-footer__art-phone   { width: 16%; max-width: 88px;  min-width: 0; height: auto; }

.site-footer__bottom {
  border-top: 1px solid rgba(225, 221, 216, 0.14);
  padding: 20px 0;
  font-size: 13px;
  color: var(--text-on-dark-muted); /* 7,6:1 sur le bleu nuit */
}
.site-footer__bottom p { margin: 0; }

@media (max-width: 900px) {
  .site-footer__inner { grid-template-columns: 1fr; }
  .site-footer__cols { gap: 28px 32px; justify-content: center; }
  .site-footer__art { justify-content: center; }
  .site-footer__art-desktop { max-width: 340px; }
  .site-footer__art-phone { max-width: 72px; }
}

@media (max-width: 720px) {
  .site-footer__inner {
    grid-template-columns: 1fr;
    gap: 40px;
    padding-top: 48px;
    padding-bottom: 32px;
  }
  .site-footer__cols {
    grid-template-columns: 1fr;
    justify-content: center;
    gap: 32px;
  }
  /* Version téléphone : seul le dessin du téléphone, centré. */
  .site-footer__art { justify-content: center; }
  .site-footer__art-desktop { display: none; }
  .site-footer__art-phone { width: 120px; max-width: 120px; }
  .site-footer__bottom { text-align: center; }
}

/* ---------- Responsive ---------- */

@media (max-width: 900px) {
  .hexora-section .container {
    grid-template-columns: 1fr;
  }
  .hexora-section__media { order: -1; }

  .news-card { flex: 0 0 calc((100% - var(--gap)) / 2); }
}

@media (max-width: 720px) {
  .section { padding: 64px 0; }

  .news-card { flex: 0 0 85%; }
}


/* =========================================================
   Erreurs de formulaire
   ========================================================= */

.form-error {
  margin: 8px 0 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--c-900);
}
/* Un message qui apparaît glisse en place au lieu de surgir. On ne peut
   pas transitionner depuis display:none ([hidden]) : une animation jouée
   à l'affichage fait le même office. */
.form-error:not([hidden]),
.form-message:not([hidden]) { animation: fade-up 0.35s var(--ease-out) both; }

/* La palette n'a que des gris : impossible de signaler une erreur par la
   couleur. On la marque donc par un trait épais à gauche, visible quel que
   soit le daltonisme — et de toute façon, s'appuyer sur la seule couleur
   serait un défaut d'accessibilité même avec du rouge disponible. */
.form-error::before {
  content: '⚠ ';
}

.form-field input[aria-invalid='true'],
.form-field textarea[aria-invalid='true'],
.form-field select[aria-invalid='true'] {
  border-color: var(--c-900);
  border-left-width: 4px;
}

.form-notice {
  margin: 0 0 20px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--c-800);
}
/* Même traitement que .legal-page a : un lien dans une phrase doit se
   voir comme un lien, pas se fondre dans le texte qui l'entoure. */
.form-notice a { color: var(--accent-violet-strong); }

.form-message {
  margin-top: 18px;
  padding: 14px 18px;
  border-radius: var(--radius);
  border-left: 4px solid var(--c-900);
  background: var(--c-300);
  color: var(--c-900);
  font-size: 14px;
}

/* Pot de miel : invisible pour un visiteur, rempli par les robots qui
   lisent le HTML sans appliquer le CSS. Il ne suffit PAS de display:none —
   certains robots ignorent les champs masqués ainsi. On le sort donc de
   l'écran tout en le laissant « affiché », et on le retire du parcours
   clavier et des lecteurs d'écran pour ne gêner personne. */
.form-honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}


/* =========================================================
   Bannière cookies (injectée par js/consent.js)
   ========================================================= */

.cookie-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  background: var(--c-900);
  color: var(--text-on-dark);
  border-top: 1px solid var(--c-700);
  padding: 20px 24px;
  /* Décalée hors écran au départ : js/consent.js ajoute .is-visible à la
     frame suivante, ce qui déclenche la transition d'apparition. */
  transform: translateY(100%);
  transition: transform 0.35s ease;
}
.cookie-banner.is-visible { transform: translateY(0); }

.cookie-banner__inner {
  max-width: var(--container);
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}

.cookie-banner__title {
  margin: 0 0 4px;
  font-size: 15px;
  font-weight: 700;
}

.cookie-banner__text { flex: 1 1 320px; }
.cookie-banner__text p { margin: 0; font-size: 14px; color: var(--c-300); }
/* Violet éclairci : lien sur fond sombre (5,3:1), cohérent avec les
   autres liens actifs du site. */
.cookie-banner__text a { color: var(--accent-violet-light); }

.cookie-banner__actions {
  display: flex;
  gap: 12px;
  flex-shrink: 0;
}

/* Refuser et Accepter partagent exactement le même style : la CNIL impose
   que refuser soit aussi simple et aussi visible qu'accepter. Ne pas
   différencier ces deux boutons est une exigence, pas un oubli. */
.btn--banner {
  background: var(--c-100);
  color: var(--c-900);
  border: 1px solid var(--c-100);
  padding: 11px 22px;
  font-size: 14px;
  cursor: pointer;
}
.btn--banner:hover { background: var(--c-300); border-color: var(--c-300); }

@media (max-width: 600px) {
  .cookie-banner__inner { flex-direction: column; align-items: stretch; }
  .cookie-banner__actions { justify-content: stretch; }
  .btn--banner { flex: 1 1 auto; text-align: center; }
}



/* =========================================================
   Pages légales (mentions, confidentialité, CGU)
   ========================================================= */

.legal-page {
  max-width: 760px;
  margin: 0 auto;
  padding: 64px 24px 80px;
}

.legal-page h1 {
  font-size: clamp(28px, 4vw, 40px);
  margin: 0 0 8px;
}

.legal-page__updated {
  color: var(--text-muted);
  font-size: 14px;
  margin: 0 0 40px;
}

.legal-page h2 {
  font-size: 20px;
  margin: 40px 0 12px;
  padding-top: 24px;
  border-top: 1px solid var(--c-500);
}

.legal-page h3 {
  font-size: 16px;
  margin: 24px 0 8px;
}

.legal-page p,
.legal-page li {
  color: var(--text-main);
  font-size: 16px;
  line-height: 1.7;
}

/* Les liens dans le corps des pages légales (mailto, renvois entre
   pages, lien vers la CNIL) héritaient jusqu'ici du texte principal —
   aucune distinction visuelle. --accent-violet-strong + soulignement
   les rend identifiables comme des liens actifs (5,06:1 sur fond clair). */
.legal-page a {
  color: var(--accent-violet-strong);
  text-decoration: underline;
  text-underline-offset: 2px;
  transition: color 0.2s ease, text-underline-offset 0.25s var(--ease-out);
}
.legal-page a:hover { color: var(--accent-violet-strong-hover); text-underline-offset: 5px; }

.legal-page ul { padding-left: 22px; }
.legal-page li { margin-bottom: 8px; }

.legal-page table {
  width: 100%;
  border-collapse: collapse;
  margin: 16px 0;
  font-size: 15px;
}
.legal-page th,
.legal-page td {
  border: 1px solid var(--c-500);
  padding: 10px 12px;
  text-align: left;
  vertical-align: top;
}
.legal-page th { background: var(--c-300); font-weight: 700; }

/* Emplacements que le gérant du site doit compléter avec ses informations
   légales réelles. Volontairement très visible : une mention légale laissée
   avec un texte d'exemple n'a aucune valeur et expose à une sanction. */
.legal-todo {
  display: inline-block;
  padding: 2px 8px;
  border: 1px dashed var(--c-900);
  background: var(--c-300);
  font-weight: 700;
  font-size: 0.92em;
}


/* =========================================================
   Page 404
   ========================================================= */

.error-page {
  max-width: 620px;
  margin: 0 auto;
  padding: 120px 24px;
  text-align: center;
}

.error-page__code {
  display: block;
  font-size: clamp(64px, 14vw, 120px);
  font-weight: 800;
  line-height: 1;
  color: var(--c-500);
  margin-bottom: 8px;
}

.error-page h1 {
  font-size: clamp(24px, 4vw, 34px);
  margin: 0 0 16px;
}

.error-page p {
  color: var(--text-muted);
  font-size: 17px;
  margin-bottom: 32px;
}

.error-page__links {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 12px 28px;
  margin-top: 36px;
  font-size: 15px;
}


/* =========================================================
   Animations d'apparition (pilotées par js/reveal.js)

   Tout est conditionné à html.js-reveal, posé par un script inline dans
   <head> : sans JavaScript, aucune de ces règles ne s'applique et la
   page est entièrement visible d'emblée.

   Un élément [data-reveal] démarre invisible et décalé ; js/reveal.js
   ajoute .is-visible quand il entre dans l'écran, puis RETIRE l'attribut
   une fois la transition finie — l'élément retrouve alors ses propres
   transitions (survol, focus…) sans que celles-ci ne soient jamais
   écrasées par la règle ci-dessous.
   ========================================================= */

.js-reveal [data-reveal] {
  opacity: 0;
  transform: translateY(var(--reveal-distance, 32px));
  transition: opacity 0.7s var(--ease-out), transform 0.7s var(--ease-out);
}
.js-reveal [data-reveal="left"]  { transform: translateX(-36px); }
.js-reveal [data-reveal="right"] { transform: translateX(36px); }
.js-reveal [data-reveal="scale"] { transform: scale(0.94); }
.js-reveal [data-reveal="fade"]  { transform: none; }
.js-reveal [data-reveal].is-visible { opacity: 1; transform: none; }

/* Sur écran étroit, un élément pleine largeur décalé de 36 px vers la
   droite dépasse du bord et ouvre un défilement horizontal le temps
   qu'il apparaisse. Les entrées latérales deviennent donc verticales
   dès que les colonnes se replient. */
@media (max-width: 900px) {
  .js-reveal [data-reveal="left"],
  .js-reveal [data-reveal="right"] { transform: translateY(24px); }
}

/* Header : descend en place au chargement, comme la barre flottante de
   la référence. Le sticky n'en souffre pas : à la fin de l'animation,
   transform vaut none. */
.js-reveal .site-header { animation: header-in 0.6s var(--ease-out) both; }

@keyframes header-in {
  from { opacity: 0; transform: translateY(-16px); }
  to   { opacity: 1; transform: none; }
}

/* Utilisé par les messages du formulaire. */
@keyframes fade-up {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}


/* =========================================================
   Réduction de mouvement
   Tout ce qui bouge de lui-même s'arrête, tout ce qui apparaît est déjà
   là. Les changements de couleur au survol restent : ils n'agitent rien.
   ========================================================= */

@media (prefers-reduced-motion: reduce) {
  .js-reveal [data-reveal] { opacity: 1; transform: none; transition: none; }
  .js-reveal .site-header,
  .etape *,
  .form-success.is-visible,
  .form-error:not([hidden]),
  .form-message:not([hidden]) { animation: none !important; }

  .btn:hover, .btn:active,
  .site-logo:hover .site-logo__marque,
  .etapes__pastille:hover,
  .news-card:hover,
  .news-card:hover .news-card__media img,
  .carousel-nav__btn:hover, .carousel-nav__btn:active,
  .hexora-section__media img:hover,
  .poster__card:hover,
  .poster__card:hover .poster__img { transform: none; }

  .site-nav__indicator { transition: none; }

  /* Frise du hero : la carte change de place sans glisser, seul le fondu
     entre deux étapes reste. Elle avance toujours toute seule (la barre
     de 4 s n'est pas arrêtée), sans aucun mouvement. */
  .etapes__carte,
  .etapes__tige,
  .etapes__frise::after { transition: none; }
}
