/* ===================================================
   RM MOUSTIQUAIRES — style.css
   Composants, dans l'ordre des blocs de la page.
   Les valeurs vivent dans tokens.css : aucune couleur ni
   taille en dur ici, sauf les cas explicitement commentés.
   =================================================== */

/* === Reset === */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  scroll-behavior: smooth;
  /* La nav flottante mesure ~5em : sans ce décalage, chaque
     ancre atterrit sous la barre et cache son propre titre. */
  scroll-padding-top: 7em;
  background: var(--bg);
}

body {
  font-family: var(--font-sans);
  font-size: var(--text-body);
  font-weight: 400;
  line-height: var(--lh-body);
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  transition: background var(--dur-fast) linear, color var(--dur-fast) linear;
}

/* height:auto est la moitié indispensable de la règle : sans elle, les
   attributs width/height du HTML (hints de présentation) figent la hauteur
   native pendant que max-width réduit la largeur, et l'image s'étire.
   Les composants qui veulent une hauteur imposée la redéclarent. */
img, video { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
ul, ol { list-style: none; }

/* L'anneau de focus doit atteindre 3:1 contre son fond (WCAG 1.4.11).
   L'or de la marque (#C29A57) ne fait que 2,4:1 sur blanc : il était donc
   sous le seuil sur toute la partie claire du site, c'est-à-dire presque
   partout. --gold-deep (#8A6733, 5,16:1) était déjà défini dans les tokens
   exactement pour ce cas et n'était utilisé nulle part. */
:focus-visible {
  outline: 2px solid var(--gold-deep);
  outline-offset: 3px;
}
/* Pas de variante par thème : l'attribut de thème est GLOBAL et ne sait pas
   sur quel fond se trouve réellement l'élément focalisé. Un champ du
   configurateur (section claire) héritait de la variante sombre tant que le
   scroll n'avait pas basculé le thème. --gold-deep passe le seuil de 3:1
   dans les deux cas : 5,16:1 sur blanc et 3,76:1 sur le noir de la charte.
   Une seule couleur, aucune condition à maintenir. */

.skip-link {
  position: absolute; top: -100px; left: 1em; z-index: 200;
  padding: .75em 1.25em;
  background: var(--gold); color: var(--black);
  font-family: var(--font-mono); font-size: var(--text-small);
  text-transform: uppercase; letter-spacing: var(--track-button);
}
.skip-link:focus { top: 1em; }

/* === Typographie ===
   La règle du projet : mono capitales pour la métadonnée,
   sans casse normale pour le discours, et AUCUN gras. Toute
   la hiérarchie vient de la taille et du tracking. */

/* Aucun gras nulle part : la règle est posée dans tokens.css et n'était
   appliquée que section par section. Quatre <strong> de la modale de mesure
   sortaient en 700 par le défaut du navigateur. Une règle globale, plutôt
   qu'une par endroit où le cas se présente. */
/* 500 et non 400 : ramener <strong> à la graisse courante ET à la couleur
   courante le rendait rigoureusement identique au texte qui l'entoure. Dans
   la modale de mesure, la phrase qui porte toute la consigne (« prenez
   trois mesures », « retirez 3 mm ») n'avait plus aucun relief. Le 500
   existe déjà dans le système, il ne casse pas la règle « aucun gras » :
   c'est le 700 qui était proscrit. */
strong, b { font-weight: 500; color: var(--text); }

.mono {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: var(--track-eyebrow);
  font-weight: 400;
}

.display-heading {
  font-family: var(--font-mono);
  font-size: var(--display);
  font-weight: 400;
  line-height: var(--lh-tight);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  text-wrap: balance;
}

.heading-h2 {
  font-size: var(--h2);
  font-weight: 400;
  line-height: var(--lh-tight);
  letter-spacing: var(--track-h2);
  text-wrap: balance;
}

.heading-h3 {
  font-size: var(--h3);
  font-weight: 400;
  line-height: var(--lh-title);
  letter-spacing: var(--track-h3);
  text-wrap: balance;
}

.heading-h4 {
  font-size: var(--h4);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: var(--track-h4);
  text-wrap: balance;
}

.label {
  display: flex;
  align-items: center;
  gap: .75em;
  font-family: var(--font-mono);
  font-size: var(--eyebrow);
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--text);
}

/* Le carré de 8px est le motif qui porte l'accent partout
   dans la page sans jamais peser. */
.label-square {
  width: 8px; height: 8px;
  background: var(--gold);
  flex: none;
}

/* Une largeur maximale, sinon la ligne s'allonge indéfiniment avec l'écran :
   mesuré à 92 caractères sur 1920px là où le confort de lecture s'arrête
   vers 75. C'est ça, « le texte ne s'adapte pas au grand écran » — ce n'est
   pas la taille de police qui manquait, c'est la longueur de ligne qui
   n'était bornée nulle part. margin-inline: auto pour que le cadrage centré
   des sections concernées soit conservé. */
.body-large  { font-size: var(--text-large); max-width: 34em; margin-inline: auto; }
/* Dans une grille en deux colonnes, la colonne fait déjà la mesure : on ne
   recentre pas, ça décollerait le texte du bord gauche de sa colonne. */
.split-right .body-large { margin-inline: 0; }
.body-small  { font-size: var(--text-small); }
.text-muted  { color: var(--text-muted); }

/* === Layout === */
.section { padding: var(--space-section) 0; position: relative; }
.padding-global { padding-left: var(--space-global); padding-right: var(--space-global); }
.container-large  { max-width: var(--container-large);  margin: 0 auto; width: 100%; }
.container-medium { max-width: var(--container-medium); margin: 0 auto; width: 100%; }

/* Espaceurs FLUIDES, comme le padding de section.
   Figés en em, ils valaient 96px à l'intérieur d'une section sur mobile
   pendant que l'écart ENTRE deux sections tombait à 113px : les deux
   distances devenaient équivalentes et la page n'avait plus de rythme,
   plus rien ne se lisait comme un bloc séparé. Ils suivent maintenant la
   largeur, et restent toujours nettement inférieurs à l'écart de section. */
.spacer-2 { height: clamp(1rem, .8469rem + .6531vw, 1.5rem) }     /*  16 →  24 */
.spacer-3 { height: clamp(1.25rem, .714rem + 2.286vw, 3rem) }   /*  20 →  48 */
.spacer-4 { height: clamp(1.5rem, .735rem + 3.265vw, 4rem) }    /*  24 →  64 */
.spacer-6 { height: clamp(2.5rem, 1.429rem + 4.571vw, 6rem) }   /*  40 →  96 */
/* Même arrondi au pas de 4px que les tokens d'espacement, voir tokens.css. */
@supports (height: round(1px, 1px)) {
  .spacer-2 { height: round(clamp(1rem, .8469rem + .6531vw, 1.5rem), 4px) }
  .spacer-3 { height: round(clamp(1.25rem, .714rem + 2.286vw, 3rem), 4px) }
  .spacer-4 { height: round(clamp(1.5rem, .735rem + 3.265vw, 4rem), 4px) }
  .spacer-6 { height: round(clamp(2.5rem, 1.429rem + 4.571vw, 6rem), 4px) }
}

/* Le filet de 1px est l'unité structurelle du design.
   Aucune boîte encadrée nulle part, uniquement des traits. */
.divider { height: 1px; background: var(--line); width: 100%; }
.divider-strong { background: var(--line-strong); }

/* Grille à colonne centrale vide : la gouttière fait la
   respiration mieux qu'un simple gap. */
.split-grid {
  display: grid;
  grid-template-columns: 4fr 1fr 7fr;
  align-items: start;
}
.split-left  { grid-column: 1 }
.split-right { grid-column: 3 }

.section-head-centered {
  max-width: var(--max-text-large);
  margin: 0 auto;
  text-align: center;
}
.section-head-centered .label { justify-content: center; }

/* === Boutons === */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .6em;
  padding: 1em 1.6em;
  font-family: var(--font-mono);
  font-size: var(--text-small);
  font-weight: 500;
  letter-spacing: var(--track-button);
  text-transform: uppercase;
  line-height: 1;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  transition: background var(--dur-fast) var(--ease-button),
              color var(--dur-fast) var(--ease-button),
              border-color var(--dur-fast) var(--ease-button);
}
.button-primary { background: var(--gold); color: var(--black); }
.button-primary:hover { background: var(--gold-hover); }
.button-secondary {
  background: transparent;
  color: var(--text);
  border-color: var(--line-strong);
}
.button-secondary:hover { border-color: var(--text); }

.text-link {
  font-size: var(--text-small);
  color: var(--text-muted);
  border-bottom: 1px solid var(--line-strong);
  padding-bottom: 2px;
  transition: color var(--dur-fast) var(--ease-button),
              border-color var(--dur-fast) var(--ease-button);
}
.text-link:hover { color: var(--text); border-color: var(--gold); }

/* === 01 · Nav flottante ===
   Pilule en verre dépoli, jamais pleine largeur. Elle inverse
   ses couleurs avec le thème de scroll (voir tokens.css). */
/* Largeur et respiration revues : la pilule était calée sur 70em et tout se
   touchait. 84em et des gouttières plus larges laissent respirer les trois
   groupes (marque / navigation / actions). */
.navbar {
  position: fixed;
  inset: 1.5em 0 auto;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 3em;
  width: calc(100% - 3em);
  max-width: 84em;
  margin: 0 auto;
  padding: .7em .7em .7em 1.75em;
  border: 1px solid var(--nav-border);
  border-radius: var(--radius-md);
  background: var(--nav-bg);
  backdrop-filter: blur(17px);
  -webkit-backdrop-filter: blur(17px);
  box-shadow: var(--shadow-nav);
  transition: background var(--dur-fast) linear,
              border-color var(--dur-fast) linear,
              color var(--dur-fast) linear,
              transform 320ms var(--ease-button);
}
/* La barre s'escamote quand on descend et revient dès qu'on remonte : sur un
   écran de téléphone elle mangeait un huitième de la hauteur en permanence.
   On la sort de sa propre hauteur PLUS son décalage du haut, sinon un liseré
   reste visible. */
.navbar.is-tucked { transform: translateY(calc(-100% - 1.5em)); }
/* RÈGLE APPLIQUÉE — dans un lockup, ce qui compte n'est pas la hauteur
   totale de l'image mais la HAUTEUR DE CAPITALE du logotype comparée à celle
   des liens de navigation. La marque doit peser au moins autant que son
   propre menu : on vise un rapport de 1,1 à 1,3.

   Dans ce PNG, le mot "RM MOUSTIQUAIRES" n'occupe qu'environ 35 % de la
   hauteur de l'image, le reste étant le monogramme. À 1.6em (27px), la
   capitale du logotype tombait donc à ~9,5px, contre ~10,5px pour les liens
   de nav : la marque était visuellement DOMINÉE par son menu. C'est ça, le
   ratio qui sonnait faux.

   À 2.3em (39px), la capitale monte à ~13,7px, soit 1,3× les liens. Le
   monogramme reste sous 50 % de la hauteur de la barre, ce qui est la
   fourchette habituelle pour un logo en barre de navigation. */
.navbar-logo { display: flex; align-items: center; flex: none; }
.navbar-logo img { height: 2.3em; width: auto; }
.navbar-logo-mark { display: none; }
/* Le logo est un PNG noir : en thème sombre on l'inverse
   plutôt que de charger un second fichier. */
[data-theme-status="dark"] .navbar-logo img { filter: brightness(0) invert(1); }

.navbar-nav { display: flex; align-items: center; }
.navbar-nav-inner { display: flex; align-items: center; }
.nav-link {
  font-family: var(--font-mono);
  font-size: var(--text-small);
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  /* Un libellé de navigation ne se coupe jamais : « CE QU'ON ÉQUIPE » passait
     sur trois lignes et faisait doubler la hauteur de la pilule. */
  white-space: nowrap;
  padding: 1em 1.15em;
  opacity: 1;
  transition: opacity 120ms var(--ease-button), color 120ms var(--ease-button);
}
.nav-link:hover { opacity: .6; }

.navbar-actions { display: flex; align-items: center; gap: .6em; flex: none; }
.nav-phone { white-space: nowrap; }
.cta-court { display: none; }

/* Sans cadre : le bouton encadré ajoutait une boîte là où la référence ne
   pose que le trait. Le carré doublait la forme de la pilule à 8px de
   distance, deux rectangles imbriqués pour rien. */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
  width: 2.75em; height: 2.75em;
  border: 0;
  padding: 0;
}
.nav-toggle span { width: 20px; }
.nav-toggle span {
  display: block; height: 1px; width: 100%;
  background: currentColor;
  transition: transform var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease);
}
.nav-toggle[aria-expanded="true"] span:first-child { transform: translateY(3px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:last-child  { transform: translateY(-3px) rotate(-45deg); }

/* === 02 · Hero ===
   Contenu centré et posé bas dans l'image : le regard entre par la photo,
   descend, et tombe sur le titre au moment où il a déjà compris le sujet. */
.hero {
  position: relative;
  /* Plein écran, sans plafond : le max-height coupait l'image sur les
     grands écrans au lieu de la laisser occuper toute la hauteur. */
  height: 100svh;
  min-height: 34rem;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
  background: var(--black);
}
.hero-media { position: absolute; inset: 0; will-change: transform; }
.hero-media img { width: 100%; height: 100%; object-fit: cover; }

/* Voile uniforme pour le contraste général, dégradé bas pour le bandeau.
   Le contenu étant centré, un dégradé latéral n'a plus lieu d'être. */
.hero-overlay {
  position: absolute; inset: 0;
  background-color: rgb(0 0 0 / .34);
  background-image: linear-gradient(to bottom, rgb(0 0 0 / .25) 0%, transparent 30%, rgb(0 0 0 / .82) 100%);
}

.hero-inner {
  position: relative;
  z-index: 2;
  width: 100%;
  padding: 0 var(--space-global) 4.5vh;
  color: var(--white);
  text-align: center;
}
.hero .display-heading { color: var(--white); }
.hero-mark, .hero-rule { display: none; }

/* Fabriqué en France et posé en 7 jours sont les deux arguments qui font
   basculer une décision : ils sont traités comme un bloc à part, pas comme
   une ligne de mentions parmi d'autres. */
/* Une seule phrase, une seule ligne. Les deux arguments décisifs sont dits
   d'un trait plutôt qu'empilés en blocs avec leurs précisions. */
/* Bloc et non flex : dès que la phrase passe sur deux lignes, un drapeau
   centré verticalement à gauche flotte à côté du texte. En flux, il se pose
   simplement au début de la première ligne. */
.hero-key {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-lg);
  font-weight: 300;
  text-transform: uppercase;
  letter-spacing: var(--track-eyebrow);
  line-height: 1.35;
  margin-top: 2.25rem;
  color: var(--white);
  text-shadow: 0 2px 18px rgb(0 0 0 / .5);
  text-wrap: balance;
}
.hero-flag {
  display: inline-block;
  vertical-align: -.18em;
  margin-right: .65em;
  border-radius: 2px;
  box-shadow: 0 0 0 1px rgb(255 255 255 / .25);
}

.hero-strip {
  position: relative;
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-md);
  justify-content: center;
  padding: 1.25em var(--space-global);
  font-size: var(--text-xsmall);
  color: var(--white);
  opacity: .7;
  border-top: 1px solid rgb(255 255 255 / .18);
}
.hero-strip li { display: flex; align-items: center; gap: .6em; }
.hero-strip li::before {
  content: ""; width: 4px; height: 4px; background: var(--gold); flex: none;
}

/* === 03 · Manifeste ===
   Une seule phrase, très grande. Pas un paragraphe : c'est la
   contrainte qui fait tout l'effet du bloc. */
/* Le manifeste tournait à 10em (173px) quand toutes les autres sections sont
   à --space-section (112px), et il était déjà ramené sur le token sous 991px.
   L'écart n'existait donc qu'en bureau : deux gouttières à 173px suivies de
   six à 224px, c'est ce qui se lisait comme incohérent. */
.section-manifeste { padding-block: var(--space-section); }
.manifeste-statement {
  font-size: var(--h3);
  font-weight: 400;
  line-height: 1.18;
  letter-spacing: var(--track-h3);
  max-width: 22em;
  text-wrap: balance;
}

/* === 04 · Panneau 01–04 ===
   Un seul panneau en ratio cinéma qui change, pas quatre
   cartes côte à côte. Le 2.39:1 est ce qui le fait lire comme
   une image de film plutôt qu'une capture de présentation. */
.section-tabs { padding-top: 0; }
.tab-visual { width: 100%; position: relative; }
.tab-stage {
  position: relative;
  aspect-ratio: 2.39;
  max-height: calc(100vh - 9em);
  border-radius: var(--radius-md);
  border: 1px solid rgb(0 0 0 / .2);
  overflow: hidden;
  background: var(--black);
}
.tab-image {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  transform: translateX(3%);
  transition: opacity 300ms var(--ease-button), transform 300ms var(--ease-button);
}
.tab-image.is-active { opacity: 1; transform: none; }

/* Sombre en haut ET en bas, transparent au milieu : le titre
   et le numéro restent lisibles sans assombrir le sujet.

   Deux voiles distincts et non un seul dégradé à trois arrêts.
   Un dégradé linéaire interpole l'opacité en ligne droite : avec un
   arrêt à zéro au milieu, les deux droites se rejoignaient en angle
   et l'œil lisait une cassure nette en travers de l'image. Chaque
   voile suit donc une courbe d'atténuation, échantillonnée en onze
   arrêts — assez pour que l'interpolation entre deux arrêts reste
   sous le seuil visible. Les deux s'éteignent avant de se croiser,
   il n'y a plus de point de rencontre à trahir. */
.tab-gradient {
  position: absolute; inset: 0;
  background-image:
    linear-gradient(to bottom,
      rgb(0 0 0 / .35) 0%,
      rgb(0 0 0 / .289) 5%,
      rgb(0 0 0 / .234) 10%,
      rgb(0 0 0 / .185) 15%,
      rgb(0 0 0 / .141) 20%,
      rgb(0 0 0 / .101) 25%,
      rgb(0 0 0 / .066) 30%,
      rgb(0 0 0 / .040) 35%,
      rgb(0 0 0 / .020) 40%,
      rgb(0 0 0 / .006) 45%,
      rgb(0 0 0 / 0) 50%),
    linear-gradient(to top,
      rgb(0 0 0 / .95) 0%,
      rgb(0 0 0 / .846) 9%,
      rgb(0 0 0 / .743) 18%,
      rgb(0 0 0 / .639) 27%,
      rgb(0 0 0 / .536) 36%,
      rgb(0 0 0 / .443) 45%,
      rgb(0 0 0 / .344) 54%,
      rgb(0 0 0 / .253) 63%,
      rgb(0 0 0 / .165) 72%,
      rgb(0 0 0 / .075) 81%,
      rgb(0 0 0 / 0) 90%);
}

.tab-title {
  position: absolute;
  inset: 2.5rem auto auto 2.5rem;
  z-index: 3;
  /* 12em coupait « Les cotes finales, c'est nous » à 4px près : ce titre
     mesure 12,2em, les trois autres tiennent sous 11em. Le plafond existe
     pour qu'un titre ne traverse pas tout le panneau cinémascope, il n'a
     jamais eu de raison d'être aussi serré — il reste 890px de libre avant
     le numéro. */
  max-width: 14em;
  color: var(--white);
  font-size: var(--h5);
  font-weight: 400;
  line-height: var(--lh-title);
  letter-spacing: var(--track-h4);
}
.tab-number {
  position: absolute;
  inset: 2.5rem 2.5rem auto auto;
  z-index: 3;
  color: var(--white);
  font-size: 3em;
  line-height: 1;
  letter-spacing: var(--track-display);
  font-variant-numeric: tabular-nums;
}

.tab-nav {
  position: absolute;
  inset: auto 2.5rem 2.5rem;
  z-index: 3;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--gap-md);
  /* Sans ça, les quatre colonnes s'étirent à la hauteur de la plus haute et
     les libellés s'alignent sur le HAUT de la rangée : déplier une seule
     description faisait monter les quatre titres d'un bloc. Calés en bas,
     seule la colonne active se soulève, les trois autres ne bougent plus. */
  align-items: end;
}
.tab-item {
  position: relative;
  padding-bottom: 1em;
  text-align: left;
  color: var(--white);
  opacity: .45;
  transition: opacity 250ms var(--ease-button);
}
.tab-item.is-active, .tab-item:hover { opacity: 1; }
.tab-item-head {
  display: block;
  width: 100%;
  text-align: left;
  color: inherit;
  padding: 0;
}
/* En sortant la description du bouton, celui-ci s'est réduit au libellé,
   soit 20px de haut : sous le seuil tactile alors que toute la ligne
   paraît cliquable. Un calque invisible rend la ligne entière active sans
   toucher au rendu. */
.tab-item-head::after {
  content: "";
  position: absolute;
  inset: 0;
}
.tab-item-label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-xsmall);
  text-transform: uppercase;
  letter-spacing: var(--track-eyebrow);
}
/* Le détail se déplie en hauteur au lieu d'apparaître : le
   mouvement fait comprendre lequel des quatre est actif. */
.tab-item-detail {
  display: grid;
  grid-template-rows: 0fr;
  overflow: hidden;
  font-size: var(--text-small);
  line-height: 1.45;
  opacity: 0;
  transition: grid-template-rows var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.tab-item.is-active .tab-item-detail { grid-template-rows: 1fr; opacity: .92; margin-top: .7em; }
.tab-item-detail > * { min-height: 0; overflow: hidden; }

.tab-item-track {
  position: absolute; inset: auto 0 0 0;
  height: 1px;
  background: rgb(255 255 255 / .35);
}
.tab-item-progress {
  display: block; height: 100%; width: 100%;
  background: var(--white);
  transform: scaleX(0);
  transform-origin: left center;
}
.tab-item.is-active .tab-item-progress {
  animation: tab-fill var(--tab-duration, 5000ms) linear forwards;
}
@keyframes tab-fill { to { transform: scaleX(1) } }

/* === 05 · Nos ouvertures === */
.count-items {
  font-size: .35em;
  vertical-align: super;
  margin-left: .4em;
  letter-spacing: var(--track-eyebrow);
  color: var(--text-muted);
}

.solutions-item { position: relative; }
.solutions-item:hover { z-index: 2; }

.solutions-link {
  display: grid;
  grid-template-columns: 3em 1fr auto;
  align-items: center;
  gap: var(--gap-sm);
  padding: 1.4em 0;
  border-bottom: 1px solid var(--line-strong);
  transition: color var(--dur-fast) var(--ease-button),
              border-color var(--dur-fast) var(--ease-button);
}
.solutions-item:hover .solutions-link { color: var(--text); border-color: var(--gold); }

.solutions-number { font-size: var(--text-small); color: var(--text-muted); }
.solutions-item:hover .solutions-number { color: inherit; }
.solutions-name {
  font-size: var(--h5);
  letter-spacing: var(--track-h4);
  line-height: 1.1;
}
.solutions-arrow {
  font-size: var(--text-small);
  opacity: 0;
  transform: translateX(-.5em);
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.solutions-item:hover .solutions-arrow { opacity: 1; transform: none; }

/* La photo au survol, en CSS pur.
   L'asymétrie de durée est le cœur de l'effet : 600 ms à
   l'entrée, 0 s à la sortie. Une disparition progressive
   rendrait le survol mou et traînant. */
.solutions-img {
  position: absolute;
  inset: calc(50% - 7em) 0 auto auto;
  width: 12em;
  /* height:auto est indispensable : les attributs width/height du HTML sont
     des hints de présentation qui l'emportent sur aspect-ratio tant qu'aucune
     règle auteur ne les écrase. Sans ça, l'image gardait sa hauteur native
     (1200px) au lieu des 255px du ratio 2/2.5. */
  height: auto;
  aspect-ratio: 2 / 2.5;
  object-fit: cover;
  border-radius: var(--radius-sm);
  pointer-events: none;
  z-index: 2;
  transform: scale(0);
  transition-property: transform;
  transition-duration: 0s;
  transition-timing-function: var(--ease);
}
/* Les deux dernières lignes bordent la section suivante : centrée sur la
   ligne, la photo déborderait de 200px par-dessus. On l'ancre par le bas et
   on la laisse déborder de 5em seulement, ce qui la garde dans la respiration
   blanche sous la liste sans qu'elle recouvre la moitié des lignes. */
.solutions-item:nth-last-child(-n+2) .solutions-img { inset: auto 0 -5em auto; }

/* Le poster du van est un 9:16 : recadré en 2/2.5 et centré, il ne montre
   que le bitume. On remonte le point de mire sur le véhicule. */
.solutions-img[data-focus="high"] { object-position: center 22%; }

@media (min-width: 992px) and (hover: hover) {
  .solutions-item:hover .solutions-img {
    transform: scale(1);
    transition-duration: var(--dur);
  }
}
@media (max-width: 991px) { .solutions-img { display: none } }

/* === 06 · Le produit en détail === */
.section-produit {
  background: var(--black);
  --text:        var(--neutral-100);
  /* Sur fond noir, le gris moyen qui passe très bien en thème clair devient
     illisible : le texte courant de cette section monte au quasi-blanc. */
  --text-muted:  var(--neutral-200);
  --line:        var(--neutral-800);
  --line-strong: var(--neutral-700);
  --surface:     var(--neutral-900);
  color: var(--text);
}
.module-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gap-xl);
  align-items: start;
}

.accordion-item { border-bottom: 1px solid var(--neutral-800); position: relative; }
.accordion-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-sm);
  width: 100%;
  padding: 1.4em 0;
  text-align: left;
}
.accordion-title {
  font-size: var(--text-large);
  letter-spacing: -.01em;
  transition: color var(--dur-fast) var(--ease-button);
}
.accordion-item.is-open .accordion-title { color: var(--white); }
.accordion-head:hover .accordion-title { color: var(--white); }

/* Dépliage par grid-template-rows plutôt que max-height :
   la hauteur réelle est animée, sans valeur magique à
   deviner et sans à-coup en fin de course. */
.accordion-body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur) var(--ease);
}
.accordion-item.is-open .accordion-body { grid-template-rows: 1fr; }
.accordion-body > * { min-height: 0; overflow: hidden; }
/* .accordion-body-inner reste NU : c'est lui l'élément de grille, et un
   élément de grille conserve son padding même quand la piste vaut 0fr.
   L'espacement descend donc sur le paragraphe, qui n'est plus un élément
   de grille mais un simple enfant. */
.accordion-body p { color: var(--text-muted); max-width: 30em; padding-bottom: 1.6em; }

.accordion-track {
  position: absolute; inset: auto 0 -1px 0;
  height: 1px;
  background: transparent;
}
.accordion-progress {
  display: block; height: 100%; width: 100%;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: left center;
}
.accordion-item.is-open .accordion-progress {
  animation: tab-fill var(--module-duration, 7000ms) linear forwards;
}

.module-visual {
  position: sticky;
  top: 9em;
  /* Un peu plus haut que large : le carré coupait les prises de vue
     verticales, qui sont la majorité des photos produit. */
  aspect-ratio: 1 / 1.14;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--neutral-900);
}
.module-image {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 400ms var(--ease);
}
.module-image.is-active { opacity: 1; }
/* Les visuels inactifs restent superposés à opacité nulle : sans ça, le
   curseur du comparateur intercepterait les clics destinés au visuel
   affiché, quel que soit celui qui est visible. */
.module-image:not(.is-active) { pointer-events: none; }

/* === Comparateur des deux finitions ===
   Deux photos superposées, celle du dessus rognée à la position du
   curseur. Le curseur lui-même est un input[type=range] étendu sur toute
   la surface et rendu invisible : il apporte le clavier, le tactile et le
   glisser sans une ligne de code, là où un pointerdown maison aurait
   redemandé les trois et laissé le clavier de côté. */
/* === Pastille « Fabriqué en France » ===
   Posée en HTML par-dessus la photo plutôt qu'incrustée dans le fichier :
   elle reste nette à toutes les résolutions, utilise la typo du site, et
   surtout se lit comme un élément d'interface et non comme un autocollant
   photographié sur le châssis — le produit n'en porte aucun. */
.media-badge { overflow: hidden; }
.accordion-image.media-badge { position: relative; }
.media-badge-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.badge-fr {
  position: absolute;
  right: .9em; bottom: .9em;
  display: inline-flex; align-items: center; gap: .55em;
  padding: .5em .8em;
  font-size: .6875rem;
  letter-spacing: .04em;
  color: var(--white);
  background: rgb(0 0 0 / .62);
  border-radius: var(--radius-sm);
  /* Le fond noir translucide seul ne suffit pas au-dessus d'un ciel clair :
     le voile de contraste garantit la lisibilité quelle que soit la photo. */
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.badge-fr-flag { display: block; flex: none; border-radius: 1px; }

.compare { overflow: hidden; }
/* Dans l.accordéon le bloc n.hérite d.aucun positionnement, il faut le
   lui donner pour que les images superposées aient un repère. */
.accordion-image.compare { position: relative; }
.compare-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
/* Rognage par la droite : --pos est la largeur révélée depuis le bord
   gauche, donc la même valeur pilote l'image et le filet. */
.compare-top { clip-path: inset(0 calc(100% - var(--pos)) 0 0); }
.compare-line {
  position: absolute; top: 0; bottom: 0;
  left: var(--pos);
  width: 1px;
  margin-left: -.5px;
  background: var(--white);
}
.compare-grip {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 2.75rem; height: 2.75rem;
  border-radius: 50%;
  border: 1px solid var(--white);
  background: rgb(0 0 0 / .35);
}
/* Les deux chevrons, tracés en bordures plutôt qu'en glyphes : à cette
   taille une flèche typographique se centre mal et change de dessin d'une
   police de secours à l'autre. */
.compare-grip::before,
.compare-grip::after {
  content: '';
  position: absolute; top: 50%;
  width: .45rem; height: .45rem;
  border: 1px solid var(--white);
  border-width: 1px 1px 0 0;
}
.compare-grip::before { left: .7rem;  transform: translateY(-50%) rotate(-135deg); }
.compare-grip::after  { right: .7rem; transform: translateY(-50%) rotate(45deg); }

.compare-range {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  margin: 0;
  opacity: 0;
  cursor: ew-resize;
  -webkit-appearance: none; appearance: none;
  background: transparent;
}
/* La zone de saisie du pouce doit couvrir toute la hauteur, sinon le
   glissement ne démarre que sur une bande centrale de quelques pixels. */
.compare-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 3rem; height: 100vh;
  cursor: ew-resize;
}
.compare-range::-moz-range-thumb {
  width: 3rem; height: 100vh;
  border: 0; border-radius: 0;
  cursor: ew-resize;
}
/* Le curseur étant invisible, l'anneau de focus se reporte sur la
   poignée, seul élément que le visiteur voit. */
.compare:has(.compare-range:focus-visible) .compare-grip {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
}
/* Copie du visuel à l'intérieur de chaque item. Elle ne sert qu'au mobile,
   où le panneau unique obligeait à remonter pour voir de quoi parle le
   texte qu'on vient d'ouvrir. Le fichier est le même, le navigateur ne le
   télécharge qu'une fois. */
.accordion-image { display: none; }

/* === 07 · Le processus ===
   Ligne de temps plutôt que quatre cartes : le filet horizontal relie les
   étapes, les nœuds l'interrompent. Les icônes ne sont plus enfermées dans
   des carrés — le cadre les rendait décoratives au lieu de descriptives. */
.process-grid {
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--gap-md);
}
.process-grid::before {
  content: "";
  position: absolute;
  left: 0; right: 0;
  top: 1.5em;
  height: 1px;
  background: var(--line-strong);
}
.process-item { position: relative; }
.process-node {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3em; height: 3em;
  margin-bottom: 1.75em;
  margin-left: -.5em;
  /* Seul rond restant du site depuis que les flèches ont été carrées.
     Le rayon suit maintenant l'échelle générale. */
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text);
  transition: background var(--dur-fast) linear, color var(--dur-fast) linear;
}
.process-number {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-bottom: .6em;
}
.process-title {
  font-size: var(--text-lg);
  font-weight: 400;
  letter-spacing: -.012em;
  line-height: 1.18;
  margin-bottom: .55em;
  text-wrap: balance;
}
.process-item > p:last-child { color: var(--text-muted); font-size: var(--text-sm); max-width: 22em; }
.process-item strong { font-weight: 400; color: var(--text); }

/* === 08 · Zone d'intervention ===
   Le vérificateur est centré et porte la section : c'est la seule question
   que le visiteur se pose à cet endroit. Posé dans une colonne de côté, il
   passait pour un encart secondaire. */
.cp-panel {
  max-width: 34rem;
  margin: 0 auto;
  text-align: center;
}
.cp-panel .field-label { text-align: left; }
.cp-row { display: flex; gap: .6em; }
.cp-row input { flex: 1; min-width: 0; letter-spacing: .14em; text-align: center; }
.cp-row .button { flex: none; }

.cp-result {
  display: none;
  margin-top: 1.5em;
  padding: 1.1em 1.25em;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: var(--text-base);
  line-height: 1.45;
  text-align: left;
}
.cp-result.is-shown { display: flex; gap: .85em; align-items: flex-start; }
.cp-result::before {
  content: "";
  flex: none;
  width: 8px; height: 8px;
  margin-top: .5em;
  background: var(--neutral-500);
}
.cp-direct::before  { background: #2E7D53; }
.cp-partner::before { background: var(--gold); }
/* NE PAS SUPPRIMER : les classes .cp-direct, .cp-partner, .cp-horszone et
   .cp-invalide sont construites par concaténation dans ui.js
   (`'cp-result is-shown cp-' + v.etat`). Aucune recherche de texte ne les
   trouve dans le HTML — un nettoyage automatique de CSS mort les emporterait.
   .cp-none, elle, n'était produite par aucun état : celle-là est bien partie. */
.cp-horszone::before { background: var(--gold-deep); }
.cp-invalide { color: var(--text-muted); }

/* Refus de zone dans le formulaire du configurateur. Même vocabulaire que le
   reste : un filet de 1px, l'or en accent, aucune ombre portée. */
.cfg-form-zone {
  margin: 0 0 var(--space-3);
  padding: 12px 14px;
  border-left: 1px solid var(--gold);
  background: var(--surface-2, rgba(194, 154, 87, .06));
  font-size: .875rem;
  line-height: 1.5;
}
.cfg-form-zone[hidden] { display: none; }
/* Boutons neutralisés tant que la zone n'est pas desservie : l'état doit se
   voir, sinon le visiteur clique dans le vide et croit le site cassé. */
.cfg-form-actions .button[disabled] { opacity: .4; cursor: not-allowed; }

.zone-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .75em 2em;
  margin-top: 2.25em;
  font-size: var(--text-sm);
  color: var(--text-muted);
}
/* align-items: center centrait la pastille sur la HAUTEUR du texte : dès que
   la ligne passait sur deux lignes, le carré flottait au milieu au lieu de
   marquer le début. Calé en haut, avec un décalage d'une demi-ligne pour
   tomber sur l'axe de la première ligne. */
.zone-legend li { display: flex; align-items: flex-start; gap: .6em; }
.zone-legend .zone-dot { margin-top: calc((1em * var(--lh-body, 1.5) - 8px) / 2); }
.zone-dot { width: 8px; height: 8px; flex: none; }
.zone-dot-direct  { background: #2E7D53; }
.zone-dot-partner { background: var(--gold); }

/* === 09 · Réalisations & avis ===
   Une carte par avis : média du chantier à gauche en 2/3 portrait, guillemet
   puis citation en MONO à gauche, attribution en bas. C'est la mono sur la
   citation qui fait tout le caractère du bloc — en sans, la même carte
   ressemble à n'importe quel encart de témoignage. */
.quote-group { position: relative; --carousel-gap: 1.5em; }
.carousel-viewport { overflow: hidden; }
.carousel-track {
  display: flex;
  gap: var(--carousel-gap, 1.5em);
  transition: transform var(--dur) var(--ease);
}
.quote-item {
  flex: 0 0 100%;
  min-width: 0;
}
/* La hauteur de la carte vient du TEXTE, pas de la photo : c'est la photo
   qui s'étire pour la remplir. L'inverse imposait une carte de 33em de haut
   avec la citation flottant au milieu de deux vides. */
.quote-card {
  display: grid;
  grid-template-columns: 19em 1fr;
  gap: var(--gap-lg);
  align-items: stretch;
  padding: 1.5em;
  border-radius: var(--radius-sm);
  background: var(--neutral-200);
  color: var(--neutral-900);
}
.quote-media {
  position: relative;
  height: 100%;
  min-height: 15em;
  max-height: 19em;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--neutral-300);
}
.quote-media img, .quote-media video {
  width: 100%; height: 100%; object-fit: cover;
}
.quote-media.is-video video { object-position: center 32%; }
/* Repère de lecture : sur iOS en économie d'énergie la vidéo ne démarre pas,
   et une image figée sans indice ressemble à un bug. */
.quote-media.is-video::after {
  content: "\25B6\FE0E";
  position: absolute; inset: auto auto 1em 1em;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--white);
  opacity: .85;
  transition: opacity var(--dur-fast) var(--ease);
}
.quote-media.is-playing::after { opacity: 0; }

.quote-inner {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  min-height: 15em;
  padding: 1.25em 2.5em 1.25em 0;
}
.quote-icon { color: var(--neutral-500); margin-bottom: 1.25em; }
.quote-text {
  /* Même raison : 76 caractères par ligne à 1920px, en mono qui plus est. */
  max-width: 38em;
  font-family: var(--font-mono);
  font-size: var(--text-xl);
  line-height: 1.4;
  letter-spacing: -.01em;
  color: var(--neutral-900);
  text-wrap: pretty;
}
.quote-meta { display: flex; flex-direction: column; gap: .35em; padding-top: 1.75em; }
.quote-name { font-size: var(--text-xs); color: var(--neutral-900); }
.quote-sub  { font-size: var(--text-xs); color: var(--neutral-700); }

/* Flèches au-dessus et à droite, comme sur la référence : elles ne
   recouvrent aucune photo. Calées sur la ligne du label plutôt que sur le
   haut du carrousel, sinon la barre de navigation flottante passe dessus. */
.quote-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-md);
  flex-wrap: wrap;
}
.quote-count { font-size: var(--text-xs); color: var(--text-muted); font-variant-numeric: tabular-nums; }
/* Les flèches vivent DANS le carrousel pour que leur bloc de référence soit
   lui, et non l'en-tête. Sur le bureau elles remontent sur la ligne du
   titre ; en mobile elles se posent sur la photo. Le compteur garde sa
   place à droite du titre et leur réserve la largeur. */
/* Les flèches se réservent leur place dans le padding haut du carrousel,
   au lieu de remonter d'un décalage calculé sur la hauteur du titre : ajouter
   un H2 au-dessus les avait envoyées flotter par-dessus. Ancrées au groupe,
   elles restent justes quoi qu'on mette plus haut dans la section. */
.quote-group { padding-top: 3.75rem; }
.carousel-controls {
  position: absolute; top: 0; right: 0;
  display: flex; gap: .5em; z-index: 3;
}
.carousel-arrow {
  display: flex; align-items: center; justify-content: center;
  width: 2.7em; height: 2.7em;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--bg);
  transition: border-color var(--dur-fast) var(--ease-button),
              background var(--dur-fast) var(--ease-button),
              opacity var(--dur-fast) var(--ease-button);
}
.carousel-arrow:hover:not(:disabled) { border-color: var(--gold); background: var(--surface); }
.carousel-arrow:disabled { opacity: .25; cursor: default; }

/* === 10 · FAQ === */
.faq-item { border-bottom: 1px solid var(--line-strong); }
.faq-question {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-sm);
  width: 100%;
  padding: 1.4em 0;
  text-align: left;
  font-size: var(--text-large);
  letter-spacing: -.01em;
  transition: color var(--dur-fast) var(--ease-button);
}
.faq-question:hover { color: var(--text); opacity: .65; }
.faq-icon { font-size: var(--text-body); transition: transform var(--dur) var(--ease); }
.faq-item.is-open .faq-icon { transform: rotate(45deg); }
.faq-answer {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur) var(--ease);
}
.faq-item.is-open .faq-answer { grid-template-rows: 1fr; }
.faq-answer > * { min-height: 0; overflow: hidden; }
.faq-answer-inner { max-width: var(--max-text-medium); }
.faq-answer-inner::after { content: ""; display: block; height: 1.8em; }
.faq-answer-inner p { color: var(--text-muted); }
.faq-answer-inner .text-link { margin-top: 1em; display: inline-block; }

/* === 11 · Configurateur === */
/* Le configurateur redéclare les rôles LOCALEMENT au lieu d'hériter du thème
   global. C'est la correction de fond du piège rencontré quatre fois : les
   rôles basculent sur <html>, qui ignore le fond réel de chaque section, et
   index.html démarre en data-theme-status="dark" — si motion.js ne s'exécute
   pas, la page reste sombre pour toujours et le configurateur devenait
   illisible. Avec les rôles posés ici, la section est juste quelle que soit
   la valeur globale, et les composants dedans peuvent revenir aux rôles. */
.section-configurateur {
  --bg:          var(--white);
  --text:        var(--neutral-900);
  --text-muted:  var(--neutral-700);
  --line:        var(--neutral-300);
  --line-strong: var(--neutral-400);
  --surface:     var(--neutral-100);
  background: var(--surface);
  color: var(--text);
}
.cfg-layout {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gap-lg);
  /* stretch et non start : avec start, la colonne de droite ne mesurait que
     la hauteur de son contenu et le sticky n'avait aucune course. Étirée sur
     toute la hauteur de la ligne, elle suit le visiteur même avec vingt
     ouvertures saisies à gauche. */
  align-items: stretch;
}
.cfg-side { height: 100%; }

.cfg-inputs-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--gap-sm);
  padding-bottom: 1em;
  border-bottom: 1px solid var(--line-strong);
}
.cfg-inputs-head .label { font-size: var(--text-xsmall); }

.window-card { border-bottom: 1px solid var(--line); padding: 1.4em 0; }
.window-card-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--gap-sm); margin-bottom: 1em;
}
.window-card-title { font-family: var(--font-mono); font-size: var(--text-xsmall);
  text-transform: uppercase; letter-spacing: var(--track-eyebrow); }
.window-card-head-right { display: flex; align-items: baseline; gap: 1em; }
.window-card-price {
  font-family: var(--font-mono); font-size: var(--text-small);
  font-variant-numeric: tabular-nums;
}
.window-card-remove {
  display: inline-flex; align-items: center; gap: .45em;
  font-family: var(--font-mono); font-size: var(--text-xsmall);
  text-transform: uppercase; letter-spacing: var(--track-eyebrow);
  color: var(--text-muted); line-height: 1;
  padding: .55em .8em;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  transition: color var(--dur-fast) var(--ease-button),
              border-color var(--dur-fast) var(--ease-button),
              background var(--dur-fast) var(--ease-button);
}
.window-card-remove:hover { color: #B03030; border-color: #B03030; background: #FDF4F4; }

.window-inputs { display: grid; grid-template-columns: 1fr 1fr; gap: 1em; }

.field { display: block; }
.field-label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-small);
  text-transform: uppercase;
  letter-spacing: var(--track-eyebrow);
  color: var(--text);
  margin-bottom: .6em;
}
.field input,
.window-inputs input {
  width: 100%;
  padding: .8em .9em;
  font-family: var(--font-mono);
  font-size: var(--text-base);
  font-variant-numeric: tabular-nums;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  transition: border-color var(--dur-fast) var(--ease-button);
}
/* Jamais outline:none sans remplacement : ici la bordure dorée de 1px
   remplaçait l'anneau par un signal à 2,4:1. L'anneau global reste, la
   bordure ne fait que l'accompagner. */
.field input:focus, .window-inputs input:focus { border-color: var(--gold-deep); }
.window-inputs label { display: block; }

.window-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--gap-sm); margin-top: 1em; flex-wrap: wrap;
}
.window-row-label { font-family: var(--font-mono); font-size: var(--text-xsmall);
  text-transform: uppercase; letter-spacing: var(--track-eyebrow); color: var(--text-muted); }

.qty-control { display: flex; align-items: center; gap: .25em; }
.qty-btn {
  width: 2.25em; height: 2.25em;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono); line-height: 1;
  transition: border-color var(--dur-fast) var(--ease-button);
}
.qty-btn:hover { border-color: var(--text); }
.qty-val { font-family: var(--font-mono); min-width: 2em; text-align: center;
  font-variant-numeric: tabular-nums; }

.option-group { display: flex; gap: .5em; flex-wrap: wrap; }
.option { position: relative; }
.option input { position: absolute; opacity: 0; width: 0; height: 0; }
.option-btn {
  display: flex; align-items: center; gap: .5em;
  padding: .7em 1em;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: var(--text-xsmall);
  text-transform: uppercase;
  letter-spacing: var(--track-eyebrow);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-button),
              background var(--dur-fast) var(--ease-button);
}
/* Deux signaux, pas un : la bordure passe à 2px ET le fond change. La
   couleur seule, à 2,4:1, ne suffisait ni au seuil de contraste ni à un
   œil qui distingue mal les teintes chaudes. */
.option input:checked + .option-btn {
  border-color: var(--gold-deep);
  border-width: 2px;
  background: var(--neutral-200);
  padding: calc(.7em - 1px) calc(1em - 1px);
}
/* Oublié lors du passage global à --gold-deep, et c'est l'anneau qui en a
   le plus besoin : la radio native est masquée, il est le SEUL indicateur
   de focus sur ces contrôles. 2,46:1 avant, 4,87:1 maintenant. */
.option input:focus-visible + .option-btn { outline: 2px solid var(--gold-deep); outline-offset: 2px; }
.option-dot { width: 10px; height: 10px; border: 1px solid var(--line-strong); flex: none; }
.dot-blanc      { background: #FFFFFF; }
.dot-anthracite { background: #383E42; }  /* RAL 7016, la teinte réelle du cadre */
.option-note { color: var(--text-muted); }

.cfg-add-btn {
  width: 100%;
  margin-top: 1.5em;
  padding: 1em;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-sm);
  font-size: var(--text-xsmall);
  color: var(--text-muted);
  transition: border-color var(--dur-fast) var(--ease-button), color var(--dur-fast) var(--ease-button);
}
.cfg-add-btn:hover { border-color: var(--gold); color: var(--text); }
.cfg-add-btn:disabled { opacity: .4; cursor: default; }

/* Le récap suit le visiteur pendant qu'il saisit : le prix
   ne doit jamais sortir du champ de vision, c'est lui qui
   motive le remplissage du formulaire juste en dessous. */
/* La colonne de droite doit tenir DANS l'écran quel que soit le nombre
   d'ouvertures saisies à gauche. Deux contraintes en découlent :
   - le récap ne grandit plus indéfiniment, il défile à l'intérieur d'une
     hauteur plafonnée ;
   - tout le reste est compacté pour que récap + formulaire tiennent sous la
     hauteur de fenêtre moins la barre de navigation.
   Sans ça, à neuf lignes le formulaire sortait du champ et le visiteur
   arrivait au bout de sa saisie sans jamais voir le bouton d'envoi. */
.cfg-side-inner {
  position: sticky;
  top: 9em;
  max-height: calc(100svh - 12em);
  display: flex;
  flex-direction: column;
  gap: .9em;
}
.cfg-recap {
  padding: 1.1em 1.25em;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--bg);
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.cfg-recap .label { font-size: var(--text-xs); }
.cfg-recap-list {
  margin: .75em 0 .9em;
  /* Plafond volontaire : au-delà, la liste défile au lieu de pousser le
     formulaire hors de l'écran. */
  max-height: 8.5em;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.cfg-recap-line {
  display: flex; justify-content: space-between; gap: 1em;
  padding: .3em 0;
  font-size: var(--text-sm);
}
.cfg-recap-line span:first-child { color: var(--text-muted); }
.cfg-recap-line span:last-child { font-family: var(--font-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.cfg-recap-empty { font-size: var(--text-sm); color: var(--text-muted); padding: .3em 0; }

.cfg-total {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1em; padding-top: .9em;
}
.cfg-total-label { font-size: var(--text-xs); color: var(--text-muted); }
.cfg-total-price {
  font-size: var(--text-h5);
  letter-spacing: var(--track-h4);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
/* État fautif du configurateur.
   Rouge FIXE et non un rôle de thème : le configurateur est toujours une
   section claire, alors que les rôles basculent avec le thème global.
   6,3:1 sur blanc. Et jamais la couleur seule : le message texte est le
   second signal, c'est lui qui dit quoi corriger. */
.window-card.is-invalid input[aria-invalid="true"] {
  border-color: #B03030;
  background: #FDF4F4;
}
.window-error {
  margin-top: .7em;
  font-size: var(--text-sm);
  line-height: 1.4;
  color: #B03030;
}
.window-error[hidden] { display: none; }

.cfg-recap-line.is-invalid span:last-child { color: #B03030; }
.cfg-total-price.is-partial { color: var(--text-muted); }
.cfg-total-note {
  margin-top: .7em;
  font-size: var(--text-xs);
  line-height: 1.4;
  color: #B03030;
}
.cfg-total-note[hidden] { display: none; }

.cfg-total-notes {
  display: flex; flex-wrap: wrap; gap: .4em 1em;
  margin-top: .8em;
  font-size: var(--text-xs);
  color: var(--text-muted);
}
.cfg-total-notes li { display: flex; align-items: center; gap: .5em; }
.cfg-total-notes li::before { content: ""; width: 4px; height: 4px; background: var(--gold); flex: none; }

.cfg-form-wrap {
  padding: 1.25em;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--bg);
}
.cfg-form-title { font-size: var(--text-base); letter-spacing: -.01em; margin-bottom: .25em; }
.cfg-form-wrap .body-small { font-size: var(--text-xs); }
.cfg-form-wrap .field-label { font-size: var(--text-xs); margin-bottom: .35em; }
.cfg-form-wrap input { padding: .6em .75em; font-size: var(--text-sm); }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: .7em; margin-bottom: .7em; }
.cfg-form-actions { display: grid; grid-template-columns: 1fr 1fr; gap: .6em; }
/* Même règle qu'en mobile : le bouton ne passe pas sous la hauteur des
   champs qu'il valide. */
.cfg-form-actions .button { padding: .95em 1em; font-size: var(--text-xs); }
/* Piège à robots : caché à l'œil, mais pas en display:none, que certains
   bots savent détecter. */
.hp-field { position: absolute; left: -9999px; opacity: 0; height: 0; overflow: hidden; }
.cfg-call { font-size: var(--text-xsmall); color: var(--text-muted); }
.cfg-call a { color: var(--text); border-bottom: 1px solid var(--gold); padding-bottom: 1px; }

/* === 12 · Footer ===
   Carte détachée flottant dans une gouttière, pas une bande
   pleine largeur : c'est ce qui l'empêche de lire comme un
   pied de page de template. */
.footer-wrapper { padding: 1em; }
.footer {
  background: var(--neutral-300);
  color: var(--neutral-900);
  border-radius: var(--radius-md);
  padding: 3em 2em 2em;
}
.footer-cta {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--gap-md); flex-wrap: wrap;
}
.footer .divider { background: var(--neutral-400); }
.footer-grid { display: grid; grid-template-columns: 1fr 1.4fr; gap: var(--gap-md); }
.footer-brand img { height: 4em; width: auto; margin-bottom: 1.25em; }
.footer-tagline { font-size: var(--text-sm); color: var(--neutral-700); max-width: 22em; }
.footer-links { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap-md); }
.footer-col { display: flex; flex-direction: column; }
.footer-link {
  font-size: var(--text-xsmall);
  padding: .75em 0;
  color: var(--neutral-900);
  transition: color var(--dur-fast) var(--ease-button);
}
.footer-link:hover { color: var(--neutral-700); }
.footer-bottom {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--gap-md); flex-wrap: wrap;
  font-size: var(--text-xsmall);
  color: var(--neutral-700);
}
.footer-badges { display: flex; gap: 1.5em; flex-wrap: wrap; }

/* === Barre collante mobile === */
.mobile-bar {
  display: none;
  position: fixed; inset: auto 0 0 0; z-index: 90;
  gap: 1px;
  background: var(--line-strong);
  border-top: 1px solid var(--line-strong);
}
.mobile-bar a {
  flex: 1;
  padding: 1.15em;
  text-align: center;
  font-size: var(--text-xsmall);
}
.mobile-bar-call { background: var(--bg); color: var(--text); }
.mobile-bar-cta  { background: var(--gold); color: var(--black); }

/* === Modale guide de mesure === */
/* Le panneau entre en glissant depuis la droite. Une apparition instantanée
   ne dit pas d'où vient le contenu ni comment revenir en arrière ; le
   glissement, si. On pilote par une classe et non par [hidden], sinon le
   display:none coupe la transition avant qu'elle ne démarre. */
.modal {
  position: fixed; inset: 0; z-index: 150;
  display: flex; justify-content: flex-end;
  background: rgb(0 0 0 / 0);
  backdrop-filter: blur(0px);
  -webkit-backdrop-filter: blur(0px);
  visibility: hidden;
  transition: background 500ms var(--ease),
              backdrop-filter 500ms var(--ease),
              visibility 0s linear 500ms;
}
.modal.is-open {
  background: rgb(0 0 0 / .5);
  backdrop-filter: blur(24px);
  -webkit-backdrop-filter: blur(24px);
  visibility: visible;
  transition: background 500ms var(--ease),
              backdrop-filter 500ms var(--ease),
              visibility 0s;
}
.modal-panel {
  position: relative;
  width: min(42em, 100%);
  height: 100%;
  overflow-y: auto;
  padding: 3em;
  background: var(--bg);
  color: var(--text);
  transform: translateX(100%);
  transition: transform 560ms var(--ease);
}
.modal.is-open .modal-panel { transform: none; }

@media (prefers-reduced-motion: reduce) {
  .modal, .modal-panel { transition-duration: 1ms; }
}
.modal-close {
  position: absolute; top: 1.5em; right: 1.5em;
  width: 2.5em; height: 2.5em;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  font-size: var(--text-small);
}
.modal-step {
  display: grid; grid-template-columns: 1fr 1.2fr;
  gap: var(--gap-md); align-items: start;
  padding: 2em 0;
  border-top: 1px solid var(--line);
}
.modal-step img { border-radius: var(--radius-sm); background: var(--surface); }
.modal-step-title { font-size: var(--text-large); font-weight: 400; letter-spacing: -.01em; margin-bottom: .5em; }
.modal-step p { color: var(--text-muted); margin-bottom: .8em; }
.modal-formula {
  display: flex; flex-wrap: wrap; gap: .5em 1em;
  padding: .8em 1em;
  border-left: 2px solid var(--gold);
  background: var(--surface);
  font-size: var(--text-xsmall);
  color: var(--text) !important;
}
.modal-formula span:first-child { color: var(--text-muted); }

/* === Reveals au scroll ===
   IntersectionObserver natif, pas de librairie. L'état initial
   n'est posé que si JS est actif : sans JS, tout reste
   visible plutôt que masqué à jamais. */
[data-reveal] { opacity: 1; }
.js [data-reveal] {
  opacity: 0;
  transform: translateY(1.5em);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.js [data-reveal].is-visible { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js [data-reveal] { opacity: 1; transform: none; }
  .tab-item.is-active .tab-item-progress,
  .accordion-item.is-open .accordion-progress { animation: none; transform: scaleX(1); }
  /* On garde l'escamotage (c'est de la place gagnée, pas une animation
     décorative) mais sans le glissement. */
  .navbar { transition: background var(--dur-fast) linear, border-color var(--dur-fast) linear, color var(--dur-fast) linear; }
}

/* ===================================================
   RESPONSIVE
   Paliers alignés sur la référence : 991 (tablette paysage),
   767 (tablette portrait), 479 (mobile).

   La typographie et les espacements sont déjà fluides (clamp), il n'y a donc
   ici que des changements de STRUCTURE : ce qui passe de deux colonnes à une,
   ce qui se réordonne, et ce qui doit exister autrement en l'absence de
   survol.

   RÈGLE DU SANS-SURVOL : tout ce qui n'apparaissait qu'au :hover doit être
   soit visible en permanence, soit supprimé. Un doigt ne survole pas, et une
   information cachée derrière un survol n'existe simplement pas sur mobile.
   =================================================== */

@media (max-width: 1200px) {
  .quote-card { grid-template-columns: 16em 1fr; }
  .cfg-layout { gap: var(--gap-md); }
}

/* ---------- ≤ 991px : tablette ---------- */
@media (max-width: 991px) {

  /* Le menu passe en panneau déroulant sous la pilule. */
  .nav-toggle { display: flex; }
  /* gap horizontal SEULEMENT. Le menu replié reste une deuxième ligne flex,
     de hauteur nulle mais bien présente : avec un gap uniforme, elle ajoutait
     son interligne à la pilule. Résultat, une barre de 78px pour un contenu
     de 44, et le logo remonté de 8px au-dessus du centre optique. */
  .navbar { flex-wrap: wrap; gap: 0 1rem; padding: .5rem .5rem .5rem .875rem; }
  .navbar-logo img { height: 2em; }
  .navbar-nav {
    display: grid;
    grid-template-rows: 0fr;
    order: 3;
    width: 100%;
    overflow: hidden;
    align-items: stretch;
    justify-items: stretch;
    transition: grid-template-rows var(--dur) var(--ease);
  }
  .navbar-nav > * { min-height: 0; }
  .navbar-nav.is-open { grid-template-rows: 1fr; }
  .navbar-nav-inner { display: flex; flex-direction: column; align-items: stretch; min-height: 0; }
  .nav-link { padding: .9em .5em; }
  .navbar-nav-inner .nav-link:first-child {
    border-top: 1px solid var(--nav-border);
    margin-top: .4em;
    padding-top: 1.1em;
  }
  .nav-phone { display: none; }

  .split-grid { grid-template-columns: 1fr; gap: var(--gap-md); }
  .split-left, .split-right { grid-column: 1; }
  .manifeste-statement { max-width: none; }

  /* Panneau à onglets : posés DANS l'image, les quatre libellés empilés plus
     leur description recouvraient la photo presque entièrement. Ils sortent
     du cadre et se posent dessous, sur le fond de page : l'image redevient
     une image, et les libellés deviennent lisibles sans voile. */
  .tab-visual { display: flex; flex-direction: column; gap: 1.5em; }
  .tab-stage { aspect-ratio: 3 / 2; max-height: none; }
  /* Seule la marge change ici : le plafond de largeur reste celui de la
     règle de base. À 9em il coupait les QUATRE titres en deux lignes alors
     qu'il reste 27em de libre avant le numéro. */
  .tab-title { inset: 1.25rem auto auto 1.25rem; }
  .tab-number { inset: 1.25rem 1.25rem auto auto; font-size: 2.4em; }

  .tab-nav { position: static; inset: auto; grid-template-columns: 1fr; gap: 0; }
  .tab-item {
    color: var(--text);
    opacity: 1;
    padding: 1em 0 1.1em;
    border-top: 1px solid var(--line);
  }
  .tab-item:first-child { border-top: 0; }
  .tab-item-label { color: var(--text-muted); }
  .tab-item.is-active .tab-item-label { color: var(--text); }
  .tab-item-detail { color: var(--text-muted); }
  .tab-item.is-active .tab-item-detail { opacity: 1; }
  .tab-item-track { background: var(--line); }
  .tab-item-progress { background: var(--gold); }

  /* Sans survol, la flèche de chaque ligne doit être là en permanence. */
  .solutions-arrow { opacity: 1; transform: none; }
  .solutions-link { padding: 1.15em 0; }

  .module-grid { grid-template-columns: 1fr; gap: var(--gap-md); }
  /* En une colonne, une image sticky resterait figée au-dessus du texte
     qu'elle illustre : elle repasse dans le flux, avant l'accordéon.
     RELATIVE et non STATIC : les .module-image sont en position absolute et
     se calent sur le premier ancêtre positionné. En static, le conteneur
     cessait d'en être un, les images s'échappaient et venaient recouvrir
     toute la section — le texte de l'accordéon se lisait par-dessus une
     photo en pleine page. */
  /* top: auto est indispensable. La règle de base pose « position: sticky;
     top: 9em ». En repassant en relative sans neutraliser le top, celui-ci
     ne colle plus l'élément mais le DÉCALE de 9em vers le bas, soit 144px :
     c'est à la fois le grand vide sous le titre et le chevauchement de
     l'image par l'accordéon en dessous. Un seul oubli, deux symptômes. */
  /* Le panneau unique disparaît : chaque item porte son propre visuel, qui
     s'ouvre avec lui. Plus besoin de remonter pour voir ce que le texte
     décrit. */
  .module-visual { display: none; }
  .accordion-image {
    display: block;
    width: 100%;
    height: auto;
    /* Carré et non 16/10 : les images de détail sont des fichiers portrait
       (1/1,14). Un cadre en 16/10 leur coupait 45 % de la hauteur, ce qui
       vidait de son sens un plan serré sur la maille ou sur un repli. À
       390 px de large la vignette passe de 219 à 350 px de haut. */
    aspect-ratio: 1 / 1;
    object-fit: cover;
    border-radius: var(--radius-sm);
    margin-top: 1rem;
  }
  .accordion-body p { padding-bottom: 0; }
  /* Sur l'IMAGE et non sur .accordion-body-inner : celui-ci est l'élément de
     grille, et un élément de grille conserve son padding même quand la piste
     vaut 0fr. Les corps repliés faisaient 20px au lieu de 0. */
  .accordion-image { margin-bottom: 1.25rem; }

  /* La ligne de temps relie des étapes alignées : sur deux rangées elle
     traverserait le vide entre elles. */
  .process-grid { grid-template-columns: repeat(2, 1fr); gap: var(--gap-lg) var(--gap-md); }
  .process-grid::before { display: none; }
  .process-item { border-top: 1px solid var(--line-strong); padding-top: 1.25em; }
  .process-node { margin-left: -.35em; margin-bottom: 1.25em; }

  /* Carte d'avis empilée : média en haut, citation en dessous. La référence
     masque sa photo à ce palier, mais chez nous elle EST la réalisation :
     la masquer reviendrait à supprimer la moitié de la preuve. */
  .quote-card { grid-template-columns: 1fr; gap: var(--gap-md); padding: 1.1em; }
  .quote-media { height: auto; min-height: 0; max-height: none; aspect-ratio: 16 / 10; }
  .quote-inner { min-height: 0; padding: 0 .4em 1em; }

  .cfg-layout { grid-template-columns: 1fr; }
  .cfg-side { height: auto; }
  /* Rien à suivre en une colonne : le récap se pose simplement après la
     saisie, et le sticky ferait plus de mal que de bien. */
  .cfg-side-inner { position: static; max-height: none; }
  .cfg-recap-list { max-height: none; }

  /* La compaction du formulaire n'existe QUE pour faire tenir la colonne
     sticky dans la hauteur de fenêtre en desktop. Ici la colonne n'est plus
     sticky : la compaction n'a plus de raison d'être, et elle ramenait les
     champs et les boutons à 36-37px de haut, sous le seuil de 44px au doigt.
     C'est le seul formulaire de conversion de la page, sur une audience qui
     remplit au téléphone : il reprend des dimensions confortables. */
  .cfg-form-wrap { padding: 1.5em; }
  .cfg-form-title { font-size: var(--text-lg); }
  .cfg-form-wrap .body-small { font-size: var(--text-sm); }
  /* Les libellés reprennent le --text-xs des métadonnées du reste de la page :
     en mono capitales avec du tracking, 14px lisait déjà comme du corps de
     texte et alourdissait chaque champ de 5px pour rien. */
  .cfg-form-wrap .field-label { font-size: var(--text-xs); margin-bottom: .4em; }
  /* 48px : la cible tactile recommandée. 53px dépassait le confortable sans
     rien apporter. Le 16px de police reste obligatoire, en dessous Safari iOS
     zoome tout seul à la mise au point. */
  .cfg-form-wrap input { padding: .85em 1em; font-size: var(--text-base); }
  .field-row { gap: .9em; margin-bottom: .9em; }
  .cfg-form-actions { gap: .75em; }
  /* Les boutons étaient plus bas (45px) que les champs qu'ils valident (53px) :
     hiérarchie inversée, l'action passait après la saisie. Ils dominent
     maintenant les champs. */
  .cfg-form-actions .button { padding: 1.2em 1em; font-size: var(--text-sm); }
  /* Un numéro de téléphone sur 19px de haut ne se vise pas au doigt. */

  /* Cibles tactiles, bornées sur la LARGEUR et pas seulement sur (hover:none).
     Un petit écran se pilote au doigt dans la quasi-totalité des cas, et la
     détection de survol ne couvre ni les hybrides ni les tablettes à souris.
     44px est le minimum confortable. */
  .carousel-arrow { min-width: 44px; min-height: 44px; }
  .qty-btn { min-width: 44px; min-height: 44px; }
  .option-btn { min-height: 44px; }
  .cfg-form-actions .button { min-height: 52px; }
  .cfg-form-wrap input, .cp-row input { min-height: 48px; }
  .faq-question, .accordion-head { min-height: 48px; }
  .navbar-actions .button, .nav-toggle { min-height: 44px; }
  .footer-link { padding: .95em 0; }
  /* Les liens de navigation du pied de page tiennent à 36px, au-dessus du
     minimum WCAG. Les coordonnées, elles, tombaient à 31 et 35px : ce sont
     les deux seules actions du bloc, et le téléphone est le chemin de
     conversion principal sur cette audience. Elles passent à 44px. */
  .footer-col:last-child .footer-link {
    display: flex;
    align-items: center;
    min-height: 44px;
    padding: 0;
  }
  /* inline-flex + min-height : sur un élément inline le padding vertical
     n'agrandit pas la boîte de ligne, et l'em part ici d'un texte de 13px,
     trop petit pour atteindre 44 par du padding. */
  .cfg-call a { display: inline-flex; align-items: center; min-height: 44px; }
  /* Le lien souligné garde son trait collé au texte : on élargit la zone de
     touche avec un calque invisible plutôt qu'avec du padding, qui
     décrocherait le soulignement. */
  .text-link { position: relative; }
  .text-link::after {
    content: "";
    position: absolute;
    inset: -12px -8px;
  }

  .footer-grid { grid-template-columns: 1fr; gap: var(--gap-lg); }
  .modal-panel { padding: 2em; }
  .modal-step { grid-template-columns: 1fr; }
}

/* ---------- 480 → 599px : grands téléphones et petites tablettes ----------
   Sous 480px, le bouton « Calculer mon prix » de la barre est masqué parce
   que la barre collante du bas prend le relais. À partir de 480 cette barre
   basse disparaît ET le bouton revient : la pilule devait alors loger le logo
   complet (226px) plus le bouton (257px) dans 410px. Elle débordait de 89px.

   Le monogramme rend les 162px qui manquent. Le bouton reste visible, ce qui
   est le point important : dans cette plage il n'y a plus aucune autre
   action à l'écran. */
@media (min-width: 480px) and (max-width: 599px) {
  .navbar-logo-full { display: none; }
  .navbar-logo-mark { display: block; }
}

/* ---------- 992 → 1399px : portables ----------
   Entre la largeur où le menu déplié réapparaît (992) et celle où tout tient
   vraiment, il manquait 275px : la pilule passait sur deux lignes et le
   bouton « Calculer mon prix » sortait par la droite. Mesuré à 992px, il
   fallait 1176px de contenu pour 901px disponibles.

   C'est la plage la plus courante des écrans d'ordinateur portable — c'était
   donc cassé là où le plus de monde regarde. Ma sonde de débordement ne l'a
   pas vu parce qu'elle ignorait les éléments en position fixe.

   Le logo passe au monogramme (−206px), les gouttières et le padding des
   liens se resserrent (−124px). Rien ne disparaît : les quatre liens, le
   téléphone et le bouton restent tous visibles. */
/* ---------- Règle du logo ----------
   Le logo occupe TOUJOURS la même part de la hauteur de barre, à toutes les
   largeurs. Quand le bloc complet « monogramme + RM MOUSTIQUAIRES » ne tient
   pas en largeur, ce n'est pas sa hauteur qu'on rabote — c'est le monogramme
   seul qui prend sa place, à cette même hauteur.

   L'état précédent faisait exactement l'inverse : le bloc complet était
   rapetissé à 38% de la hauteur de barre entre 992 et 1499px, contre 50%
   partout ailleurs. Le logo paraissait écrasé sans qu'on sache pourquoi. */
@media (min-width: 992px) and (max-width: 1499px) {
  .navbar { gap: 1.25em; padding-left: 1em; }
  .navbar-logo-full { display: none; }
  .navbar-logo-mark { display: block; }
  .nav-link { padding: 1em .75em; font-size: var(--text-xs); }
  .cta-long  { display: none; }
  .cta-court { display: inline; }
}

/* ---------- ≤ 767px : tablette portrait ---------- */
@media (max-width: 767px) {
  .navbar { margin: 0 auto; }

  /* Hero mobile : titre CENTRÉ sur deux lignes maximum, collé au bas de
     l'image, sous un dégradé qui monte du bas. Le corps du titre descend à
     une taille qui laisse « Dormez fenêtres ouvertes. » tenir sur une seule
     ligne : au-delà, la phrase se coupe en quatre et le bloc s'effondre. */
  .hero-inner { text-align: center; padding-bottom: 2.5rem; }
  /* Le monospace impose sa limite : chaque glyphe fait 0,6 em de large, et
     « Dormez fenêtres ouvertes. » compte 25 signes. Sur 366px utiles, deux
     lignes plafonnent donc à 24px. C'est le maximum, pas un choix. */
  .hero .display-heading { font-size: clamp(1.375rem, 6vw, 1.5rem); letter-spacing: -.045em; }
  .hero-inner { padding-left: .75rem; padding-right: .75rem; }
  .hero-mark { display: none; }
  .hero-rule {
    display: block;
    height: 1px;
    background: rgb(255 255 255 / .34);
    margin: 1rem auto 0;
    max-width: 22rem;
  }
  /* Dégradé du bas vers le haut : la photo reste nette en haut, le pied
     s'assombrit juste assez pour porter le texte. */
  /* Le dégradé s'arrêtait à 48 % et le titre débordait dans la zone claire.
     Il monte maintenant jusqu'aux deux tiers de l'image, en restant nul au
     sommet pour ne pas assombrir la photo. */
  .hero-overlay {
    background-color: transparent;
    background-image: linear-gradient(to top, rgb(0 0 0 / .88) 0%, rgb(0 0 0 / .78) 28%, rgb(0 0 0 / .42) 52%, rgb(0 0 0 / .1) 72%, transparent 88%);
  }

  .hero-key {
    font-size: var(--text-base);
    text-transform: none;
    letter-spacing: 0;
    line-height: 1.45;
    max-width: 24rem;
    margin: .9rem auto 0;
  }
  .hero-flag { width: 22px; height: 15px; vertical-align: -.16em; margin-right: .5em; }
  .hero-strip { display: none; }

  /* Tout aligné à gauche. Un en-tête centré au-dessus d'un contenu aligné à
     gauche donne deux axes de lecture concurrents sur 390px de large : l'œil
     redémarre à un endroit différent à chaque bloc. Sur écran étroit, une
     seule marge de gauche tient toute la page. */
  .section-head-centered { text-align: left; margin-left: 0; max-width: none; }
  .section-head-centered .label { justify-content: flex-start; }
  .cp-panel { text-align: left; margin-left: 0; }
  .cp-row input { text-align: left; }
  .zone-legend { align-items: flex-start; text-align: left; }
  .quote-head { align-items: flex-start; }

  /* La note passe SOUS son libellé au lieu d'être repoussée à l'autre bout
     du bouton : « AVEC PERÇAGE » se coupait en deux lignes avec « incl. »
     seul à droite. */
  /* wrap plutôt que column : en colonne, la pastille de couleur passait
     au-dessus de son libellé. En wrap, pastille et libellé restent sur la
     même ligne et seule la note est renvoyée dessous. */
  .option-btn {
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: .15em .5em;
    text-align: center;
    padding: .6em .5em;
  }
  .option-note { flex-basis: 100%; font-size: var(--text-xs); }
  /* Les deux boutons d'une même ligne doivent faire la même hauteur, sinon
     celui qui porte une note dépasse son voisin de quelques pixels. */
  .option { display: flex; }
  .option-btn { width: 100%; height: 100%; }
  .tab-stage { aspect-ratio: 3 / 2; }
  .tab-number { font-size: 2em; }
  .process-grid { grid-template-columns: 1fr; gap: var(--gap-md); }
  .footer-links { grid-template-columns: 1fr 1fr; }
  .field-row { grid-template-columns: 1fr; }
  .cp-row { flex-direction: column; }
  .cp-row .button { width: 100%; }
  .zone-legend { flex-direction: column; align-items: flex-start; gap: .6em; text-align: left; justify-content: flex-start; }
  .hero-strip { gap: 1em; padding: 1em var(--space-global); }
}

/* ---------- ≤ 479px : mobile ---------- */
@media (max-width: 479px) {

  /* La barre collante n'apparaît qu'ici : appeler ou obtenir son prix ne
     doit jamais demander de remonter la page. */
  .mobile-bar { display: flex; }
  body { padding-bottom: 3.5em; }
  .hero { height: calc(100svh - 3.5em); min-height: 32rem; }

  .navbar { inset: .75em 0 auto; width: calc(100% - 1.5em); padding: .5em .5em .5em .85em; }
  .navbar.is-tucked { transform: translateY(calc(-100% - .75em)); }
  /* Le logotype REVIENT sur mobile. Réduit au monogramme, la marque devenait
     un sigle que personne ne peut identifier : le visiteur ne sait pas chez
     qui il est arrivé. Le nom lisible passe avant l'économie de place. */
  /* Sélecteur aligné sur .navbar-logo img, sinon celui-ci l'emporte en
     spécificité et cette hauteur ne s'applique jamais — c'était le cas. */
  .navbar-logo img.navbar-logo-full { display: block; height: 1.9em; }
  .navbar-logo-mark { display: none; }
  /* Le CTA et le téléphone vivent déjà dans la barre collante du bas : les
     répéter ici forçait la pilule à passer sur deux lignes. */
  .navbar-actions .button-primary { display: none; }



  /* Image moins haute : en 4/5 le bloc image + onglets faisait 768px sur un
     écran de 844, et déplier un onglet le poussait hors du cadre. En 3/2 il
     reste de la place pour la description qui s'ouvre. */
  .tab-stage { aspect-ratio: 3 / 2; }
  .tab-item { padding: .85em 0 .95em; }
  /* Titre et numéro retirés de l'image sur mobile. Ils répètent mot pour mot
     le libellé de l'onglet actif affiché juste dessous, et sur 390px de
     large ils se serraient l'un contre l'autre par-dessus une photo chargée.
     Deux fois la même information, deux fois moins lisible. La photo
     redevient une photo. */
  .tab-title, .tab-number { display: none; }
  .tab-gradient { background-image: linear-gradient(to bottom, rgb(0 0 0 / .18), rgb(0 0 0 / 0) 40%); }

  .solutions-link { grid-template-columns: 2.5em 1fr auto; padding: 1em 0; }

  /* Média plus bas et marges resserrées pour que l'en-tête, la photo et
     l'avis tiennent dans un écran sans faire défiler : une preuve sociale
     qu'il faut scroller pour lire en entier ne convainc personne. */
  /* Réduire cette marge ne gagne rien : la photo est carrée, elle reprend en
     largeur ce que la marge lui rend. */
  .quote-card { padding: .75rem; gap: .75rem; }
  /* Boîte CARRÉE. Le raisonnement au pourcentage de rognage était trompeur :
     un carré coupe davantage qu'un 4/5 sur le papier, mais ce qu'il coupe
     est du mur et du bitume, pas le sujet. Les ouvertures et le van sont
     des motifs à peu près carrés et centrés ; serrés au carré ils lisent
     comme de la photo produit, alors qu'en portrait ils flottent au milieu
     de leur contexte. Vérifié sur les cinq médias avant de trancher.

     Le carré rend en plus 81px de hauteur, ce qui permet de remettre le
     guillemet supprimé au tour précédent et d'aérer la citation. */
  /* Le guillemet décoratif saute en mobile : il coûte 40px et la section a
     maintenant un vrai H2 qui dit ce qu'on lit. Sans lui, titre + photo +
     avis + signature tiennent dans un écran d'iPhone sans scroller. */
  .quote-icon { display: none; }
  .quote-media { aspect-ratio: 1 / 1; }
  .quote-media img, .quote-media video { object-position: center 38%; }
  .quote-inner { padding: 0 .2em .4em; }
  .quote-text { font-size: var(--text-base); }
  .quote-meta { padding-top: .9em; }  /* une seule règle : il y en avait deux ici */
  /* Flèches posées SUR la photo, à gauche et à droite, centrées dessus.
     En haut de l'écran il fallait remonter le pouce pour changer d'avis.
     Le conteneur épouse exactement le carré du média (mêmes marges, même
     ratio), donc les boutons tombent à mi-hauteur de l'image sans jamais
     recouvrir la citation. */
  .quote-head { gap: .75rem; }
  .quote-count { margin-right: 0; }
  .quote-group { padding-top: 0; }
  .carousel-controls {
    inset: .75rem .75rem auto;
    aspect-ratio: 1;
    align-items: center;
    justify-content: space-between;
    pointer-events: none;
  }
  .carousel-arrow {
    pointer-events: auto;
    background: rgb(255 255 255 / .92);
    border-color: rgb(0 0 0 / .12);
    /* Fond blanc en dur : la couleur du chevron doit l'être aussi, sinon
       elle hérite du blanc du thème sombre et le chevron disparaît. */
    color: var(--neutral-900);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    margin: 0 .5rem;
  }

  .window-inputs { grid-template-columns: 1fr; }
  .cfg-form-actions { grid-template-columns: 1fr; }
  .cfg-recap, .cfg-form-wrap { padding: 1.1em; }
  /* Libellé au-dessus des options : côte à côte, ils se renvoyaient à la
     ligne de façon irrégulière selon la longueur du libellé. */
  .window-row { flex-direction: column; align-items: stretch; gap: .55em; }
  .option-group { display: grid; grid-template-columns: 1fr 1fr; gap: .5em; }
  .option-btn { justify-content: center; }
  .window-row .qty-control { align-self: flex-start; }

  /* Deux colonnes plutôt qu'une : neuf liens empilés faisaient 398px de
     haut à eux seuls. La hauteur de touche est conservée, c'est la colonne
     unique qui coûtait, pas le lien. */
  /* Pied de page : trois niveaux, pas un seul.
     Quinze éléments étaient rendus à 13px, en capitales, dans la même
     couleur, avec le même écart entre eux : rien ne se distinguait et rien
     ne se groupait. Le contenu se hiérarchise maintenant en navigation,
     contact, puis mentions, chacun avec sa taille, sa couleur et son
     espacement, et un filet entre les groupes. */
  .footer-links { grid-template-columns: 1fr; gap: 0; }
  .footer-col { min-width: 0; }

  /* 1 — Navigation : le corps du pied de page. */
  .footer-link { padding: .5rem 0; overflow-wrap: anywhere; }
  .footer-col + .footer-col { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--neutral-400); }

  /* 2 — Contact : c'est une action, pas un lien de plus. Casse normale,
     taille au-dessus, et le téléphone souligné pour se détacher. */
  .footer-col:last-child .footer-link {
    font-family: var(--font-sans);
    font-size: var(--text-base);
    text-transform: none;
    letter-spacing: 0;
    padding: .35rem 0;
  }
  .footer-col:last-child .footer-link:first-child {
    font-family: var(--font-mono);
    font-size: var(--text-lg);
    padding-top: 0;
  }

  /* 3 — Mentions : le dernier niveau, nettement plus petit et effacé. */
  /* Dernier niveau, encore plus effacé : c'est une mention de bas de page,
     pas une information qu'on vient chercher. */
  .footer-bottom {
    margin-top: 1.25rem;
    padding-top: .875rem;
    border-top: 1px solid var(--neutral-400);
    font-size: 10px;
    gap: .5rem;
    color: var(--neutral-600);
  }

  .footer-brand img { height: 3em; margin-bottom: 1rem; }
  .footer-tagline { font-size: var(--text-sm); }
  .footer { padding: 1.75rem 1.1rem 1.25rem; }
  .footer-cta { gap: 1.25em; }
  .footer-cta .button { width: 100%; }
  .modal-panel { padding: 1.5em; }
  .modal-close { top: 1em; right: 1em; }
}

/* ---------- Écrans tactiles ----------
   Détecté par la capacité, pas par la largeur : une tablette large sans
   souris a le même besoin qu'un téléphone. */
@media (hover: none) {
  /* Sans survol, le retour au doigt passe par l'état actif. */
  .button:active { transform: scale(.985); }
  .solutions-link:active { border-color: var(--gold); }
  .carousel-arrow:active { border-color: var(--gold); }
  .solutions-arrow { opacity: 1; transform: none; }
  /* Cibles tactiles : 44px est le minimum confortable au doigt. Bornées en
     PIXELS et non en em : en em elles suivaient la taille de police, qui
     rétrécit en mobile, et retombaient à 36px là où c'était le plus gênant. */
  .carousel-arrow { min-width: 44px; min-height: 44px; }
  .qty-btn { min-width: 44px; min-height: 44px; }
  .cfg-form-actions .button { min-height: 52px; }
  .cfg-form-wrap input, .cp-row input { min-height: 48px; }
  .faq-question, .accordion-head { min-height: 48px; }
  .option-btn { min-height: 44px; }
}

/* ---------- ≤ 359px : très petits téléphones ----------
   Le logo complet à 1,9em mesure 224px pour 274px utiles : 10px de trop, et
   la pilule débordait. */
/* Même règle sous 360px : le bloc complet ne tenait pas en largeur (224px
   pour 274px utiles, marges comprises). Plutôt que de le rapetisser, on
   n'affiche que le monogramme, à sa hauteur normale. */
@media (max-width: 359px) {
  /* Sélecteur aligné sur .navbar-logo img.navbar-logo-full du bloc ≤479 :
     sans ça il l'emporte en spécificité et le monogramme ne sort jamais. */
  .navbar-logo img.navbar-logo-full { display: none; }
  .navbar-logo img.navbar-logo-mark { display: block; }
}
