/* ==========================================================================
   MMC Agencement — système de design
   Palette   encre #0F0F0F · bronze #B5762F · sable #F4F1ED
   Type      Archivo 300/400/500/600 + Instrument Serif italique
   Parti pris : minimalisme architectural. Filets d'un pixel, aucun arrondi,
   beaucoup de vide, une seule couleur d'accent. Les ondes du fond sont la
   continuation du logo, qui est lui-même une onde : des anneaux concentriques
   émis par le curseur, qui montent quand la souris se déplace.
   Identité claire unique — chaque surface peint sa couleur explicitement.
   ========================================================================== */

:root {
  --ink:        #0F0F0F;
  --ink-2:      #171717;
  --bronze:     #B5762F;   /* décoratif et texte >= 24px */
  --bronze-ink: #8A5620;   /* 5.8:1 sur blanc — petit texte */
  --sand:       #F4F1ED;
  --paper:      #FFFFFF;
  --rule:       #E2DDD7;
  --rule-soft:  #EDE8E2;
  --warm:       #8C837A;   /* décoratif */
  --mute:       #6F675F;   /* 5.5:1 sur blanc — petit texte */
  --text:       #4A4744;

  /* surfaces sombres */
  --d-rule:     #2A2724;
  --d-warm:     #A79D93;
  --d-mute:     #8E857C;
  --d-sand:     #C9BFB4;

  --gutter: clamp(20px, 4vw, 44px);
  --shell: 1440px;
  --narrow: 780px;
  --head: 86px;

  --fs-mega:  clamp(52px, 9vw, 132px);
  --fs-h1:    clamp(44px, 7.2vw, 104px);
  --fs-h2-xl: clamp(38px, 5.6vw, 76px);
  --fs-h2:    clamp(30px, 3.8vw, 52px);
  --fs-h3:    clamp(19px, 1.5vw, 22px);
  --fs-lede:  clamp(17px, 1.35vw, 21px);

  --ease: cubic-bezier(.22, .61, .36, 1);
  /* Le lever des révélations : départ franc, arrivée posée, aucun rebond. */
  --ease-lever: cubic-bezier(.16, .74, .22, 1);
  --z-body: 1; --z-ondes: 30; --z-pur: 31; --z-head: 40; --z-drawer: 45; --z-lightbox: 60;
}

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

html { -webkit-text-size-adjust: 100%; }
html { scroll-padding-top: calc(var(--head) + 24px); }  /* les ancres passent sous l'en-tête fixe sinon */
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }

body {
  margin: 0;
  background: var(--paper);
  color: var(--text);
  font-family: Archivo, "Helvetica Neue", Arial, sans-serif;
  font-size: 16px;
  font-weight: 400;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Les ondes passent PAR-DESSUS les surfaces, en mix-blend-mode difference :
   un seul dessin BLANC assombrit le fond blanc et éclaircit le fond noir.
   Le champ permanent du curseur plafonne à 0,132 d'alpha composée, ce qui laisse
   le texte gris à 5,2:1 ; seule l'impulsion de clic monte plus haut, et c'est un
   anneau d'un pixel qui balaie en quelques dizaines de millisecondes.
   Les valeurs sont dans js/ondes.js, avec le calcul de contraste.
   Tout ce qui doit rester pur (photos, en-tête, visionneuse) monte au-dessus. */
#ondes {
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  z-index: var(--z-ondes);
  pointer-events: none;
  display: block;
  mix-blend-mode: difference;
  opacity: .85;
}
/* Pas de z-index sur .page : sans contexte d'empilement, les photos peuvent
   remonter au-dessus du canvas des ondes. */
.page { position: relative; }
/* Les photographies ne sont jamais traversées par les ondes. */
/* Attention : un conteneur animé en transform crée son propre contexte
   d'empilement, ses enfants ne peuvent plus remonter. On monte donc le
   conteneur lui-même, pas seulement le cadre. */
.frame, .tile, .video-card, .marquee, .grid-photos, .coupe, .absorb, .demo,
.cvue, .certif__sceau {
  position: relative; z-index: var(--z-pur);
}

a { color: inherit; text-decoration: none; transition: color .25s var(--ease), border-color .25s var(--ease), background-color .25s var(--ease); }
::selection { background: var(--bronze); color: #fff; }
img { max-width: 100%; height: auto; }
h1, h2, h3, h4 { text-wrap: balance; margin: 0; }
p { margin: 0; }
/* Le navigateur pose 40px de marge sur figure et dd : sans ce reset,
   chaque photographie flotte au milieu de son cadre. */
figure, dl, dd, blockquote { margin: 0; }

:focus-visible { outline: 2px solid var(--bronze-ink); outline-offset: 3px; }
.dark :focus-visible, .lightbox :focus-visible { outline-color: var(--bronze); }

.skip {
  position: absolute; left: 12px; top: -70px; z-index: 70;
  background: var(--ink); color: var(--sand);
  padding: 13px 20px; font-size: 13px; letter-spacing: .12em; text-transform: uppercase;
  transition: top .2s var(--ease);
}
.skip:focus { top: 12px; }

.shell  { max-width: var(--shell); margin-inline: auto; padding-inline: var(--gutter); }
.narrow { max-width: var(--narrow); }

.serif { font-family: "Instrument Serif", Georgia, serif; font-style: italic; font-weight: 400; letter-spacing: -.02em; }

/* ---------- Titres ---------- */
.h1, h1 {
  font-size: var(--fs-h1); font-weight: 500; line-height: .96;
  letter-spacing: -.038em; color: var(--ink);
}
.mega { font-size: var(--fs-mega); font-weight: 500; line-height: .92; letter-spacing: -.042em; color: var(--ink); }
h2 { font-size: var(--fs-h2); font-weight: 500; line-height: 1.02; letter-spacing: -.03em; color: var(--ink); }
.h2-xl { font-size: var(--fs-h2-xl); line-height: .98; letter-spacing: -.035em; }
h3 { font-size: var(--fs-h3); font-weight: 500; line-height: 1.34; letter-spacing: -.012em; color: var(--ink); }

.eyebrow {
  font-size: 11px; font-weight: 500; letter-spacing: .34em; text-transform: uppercase;
  color: var(--bronze-ink); margin: 0;
}
.dark .eyebrow { color: var(--bronze); }
.lede { font-size: var(--fs-lede); line-height: 1.6; color: var(--text); }
.dark .lede, .dark p { color: var(--d-sand); }
.dark h2, .dark h3, .dark .h1 { color: var(--sand); }

/* ---------- En-tête ---------- */
.head {
  position: fixed; inset: 0 0 auto 0; z-index: var(--z-head);
  background: rgba(255,255,255,.93);
  -webkit-backdrop-filter: saturate(1.4) blur(16px);
  backdrop-filter: saturate(1.4) blur(16px);
  border-bottom: 1px solid var(--rule-soft);
}
.head__bar {
  max-width: var(--shell); margin-inline: auto; padding-inline: var(--gutter);
  height: var(--head); display: flex; align-items: center; gap: 24px;
}
.brand { display: flex; align-items: center; gap: 14px; margin-right: auto; min-width: 0; }
/* C'est le <picture> qui est l'enfant souple de .brand : sans flex:none,
   c'est lui qui plie et le logo se retrouve écrasé quand la barre est chargée. */
.brand picture { display: block; flex: none; }
.brand img { width: 150px; height: auto; display: block; }
@media (max-width: 700px) { .brand img { width: 124px; } }
@media (max-width: 360px) { .brand img { width: 104px; } }
.brand__tag {
  font-size: 9.5px; letter-spacing: .24em; text-transform: uppercase; line-height: 1.42;
  color: var(--mute); border-left: 1px solid var(--rule); padding-left: 14px;
}
@media (max-width: 620px) { .brand__tag { display: none; } }

.nav { display: flex; gap: clamp(14px, 1.9vw, 30px); align-items: center; }
.nav a {
  font-size: 13px; font-weight: 400; letter-spacing: .02em; color: var(--text);
  padding: 8px 0; position: relative; white-space: nowrap;
}
.nav a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 2px; height: 1px;
  background: var(--bronze); transform: scaleX(0); transform-origin: left;
  transition: transform .38s var(--ease);
}
.nav a:hover { color: var(--ink); }
.nav a:hover::after, .nav a:focus-visible::after { transform: scaleX(1); }
.nav a[aria-current="page"] { color: var(--ink); font-weight: 500; }
.nav a[aria-current="page"]::after { transform: scaleX(1); background: var(--ink); }

/* Bouton de contact : seul aplat de la barre, et seul point d'appel du
   bandeau depuis que le numéro en a été retiré. Il reste donc visible
   jusqu'en bas de la plage mobile. */
.btn-ct {
  display: inline-flex; align-items: center; min-height: 44px;
  border: 1px solid var(--ink); background: var(--ink); padding: 11px 20px;
  font-size: 13px; font-weight: 500; letter-spacing: .02em; color: var(--sand);
  white-space: nowrap;
  transition: background .35s var(--ease), border-color .35s var(--ease);
}
/* Bronze foncé et non --bronze : le texte fait 13px, et sur #B5762F le blanc
   ne donne que 3,75:1. Sur --bronze-ink il monte à 6,1:1, donc AA tenu —
   ce qui compte d'autant plus que l'état « page courante » est permanent. */
.btn-ct:hover { background: var(--bronze-ink); border-color: var(--bronze-ink); color: #fff; }
.btn-ct[aria-current="page"] { background: var(--bronze-ink); border-color: var(--bronze-ink); color: #fff; }

.burger {
  display: none; background: none; border: 1px solid var(--rule); cursor: pointer;
  font: inherit; font-size: 12px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink); padding: 0 16px; height: 44px; min-width: 44px;
}
.burger:hover { border-color: var(--ink); }

.drawer {
  display: none; border-top: 1px solid var(--rule-soft);
  background: rgba(255,255,255,.97); padding: 6px var(--gutter) 22px;
}
.drawer.open { display: block; }
.drawer a {
  display: block; padding: 15px 0; font-size: 21px; font-weight: 400;
  letter-spacing: -.015em; color: var(--ink); border-bottom: 1px solid var(--rule-soft);
}
.drawer a[aria-current="page"] { color: var(--bronze-ink); }
.drawer__tel { margin-top: 18px; }

@media (max-width: 1200px) {
  .nav { display: none; }
  .burger { display: inline-flex; align-items: center; justify-content: center; }
}
/* Sous 760px le bandeau perd sa nav : le bouton de contact devient le seul
   appel visible, il reste donc affiché, en libellé court pour tenir la barre. */
.btn-ct__court { display: none; }
@media (max-width: 760px) {
  .head__bar { gap: 12px; }
  .btn-ct { padding: 11px 14px; font-size: 12.5px; }
  .btn-ct__long { display: none; }
  .btn-ct__court { display: inline; }
}
@media (max-width: 1280px) and (min-width: 1201px) { .head__bar { gap: 16px; } .nav { gap: 18px; } }

/* ---------- Rythme vertical ---------- */
main { padding-top: var(--head); }
.sec { padding-block: clamp(72px, 9.5vw, 148px); }
.sec--tight { padding-block: clamp(48px, 6vw, 88px); }
.sec--first { padding-top: clamp(56px, 7vw, 104px); }
/* Section qui suit immédiatement un filet : c'est le filet qui sépare,
   pas le vide — sinon la démonstration tombe sous la ligne de flottaison. */
.sec--suite { padding-top: clamp(26px, 3.2vw, 46px); }

/* ---------- Héros de l'accueil ----------
   La photographie n'est plus un motif deviné sous un voile : elle occupe le
   cadre. Le voile est ASYMÉTRIQUE — plein sur la colonne de texte, nul sur la
   droite — donc le titre reste posé sur du blanc franc pendant que l'ouvrage
   se voit vraiment. En bas il se referme sur le blanc de la page : le héros se
   dissout sans arête, la bande de photographies enchaîne sans couture.
   Le fond reste SOUS le canvas des ondes, pour que l'impulsion traverse le
   héros comme le reste du site. */
.hero { position: relative; }
/* Grille et non flex : le .shell porte margin-inline:auto, et des marges
   automatiques sur un item flex annulent l'étirement — la colonne de texte
   se recentrerait au milieu de la photographie. */
.hero--grand {
  min-height: min(100svh, 980px);
  padding-top: var(--head);
  /* Deux rangées : le discours occupe la première et s'y centre, le socle
     tient la seconde et reste collé au bas du cadre. */
  display: grid; grid-template-rows: 1fr auto;
}
.hero--grand > .sec--first { align-self: center; }
/* L'accueil ouvre sur la photographie bord à bord : le décalage que main
   réserve à l'en-tête fixe est repris par le héros lui-même, et l'en-tête
   devient une barre posée SUR l'image tant qu'on n'a pas défilé.
   :has() n'est pas indispensable : sans lui, la barre reste blanche et opaque,
   soit exactement l'état des pages intérieures. */
body:has(.hero--grand) main { padding-top: 0; }
body:has(.hero--grand) .head {
  background: transparent; border-bottom-color: transparent;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  transition: background .4s var(--ease), border-color .4s var(--ease);
}
body:has(.hero--grand) .head.head--pose {
  background: rgba(255,255,255,.93); border-bottom-color: var(--rule-soft);
  -webkit-backdrop-filter: saturate(1.4) blur(16px);
  backdrop-filter: saturate(1.4) blur(16px);
}
.hero__fond {
  position: absolute; inset: 0; overflow: hidden;
  z-index: 0; pointer-events: none;
  background: var(--sand);
}
/* Deux calques imbriqués, et pas un seul : le parallaxe pilote la translation
   du premier, la révélation d'arrivée pilote l'échelle du second. Empilés sur
   le même élément, les deux transform se écraseraient l'un l'autre. */
.hero__par { position: absolute; inset: -9% 0; z-index: 0; }
.hero__img { position: absolute; inset: 0; }
.hero__img img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 72%;
}

/* Les chantiers se relaient en fond — un fondu croisé, rien d'autre.
   Pas de zoom lent : sur un site dont tout le reste tient en filets d'un
   pixel, une image qui grossit en permanence se voit comme un défaut. C'est
   l'ouvrage qui change, pas le cadrage.
   Sans JavaScript, seule la première vue existe (les suivantes n'ont ni src
   ni srcset) : le héros est alors exactement ce qu'il était avant. */
.diapo { position: absolute; inset: 0; }
.diapo__v {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity 1.5s var(--ease);
}
.diapo__v.is-on { opacity: 1; }
/* La légende nomme le chantier que l'on regarde : elle s'efface le temps du
   fondu et revient avec la vue suivante, sinon elle mentirait pendant 1,5 s. */
.hero__note { transition: opacity .4s var(--ease); }
.hero__note.is-swap { opacity: 0; }

/* Voile horizontal : blanc franc sous le texte, transparent sur l'image. */
.hero__fond::before {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(94deg,
    #fff 0%,
    #fff 15%,
    rgba(255,255,255,.93) 27%,
    rgba(255,255,255,.6) 41%,
    rgba(255,255,255,.22) 56%,
    rgba(255,255,255,.04) 72%,
    rgba(255,255,255,0) 84%);
}
/* Voile vertical : un souffle de blanc sous l'en-tête, et la dissolution
   complète sur les derniers pour-cent. */
.hero__fond::after {
  content: ""; position: absolute; inset: 0; z-index: 2;
  background:
    linear-gradient(to bottom, rgba(255,255,255,.78) 0%, rgba(255,255,255,.44) 11%, rgba(255,255,255,0) 28%),
    linear-gradient(to top, #fff 0%, rgba(255,255,255,.78) 9%, rgba(255,255,255,0) 38%);
}
.hero > .sec--first { position: relative; z-index: 1; }
/* width:100% explicite : dans une grille, un enfant qui porte margin-inline
   auto (c'est le cas de .shell) ne s'étire plus et se dimensionne sur son
   contenu — la colonne de texte se retrouverait centrée et rétrécie. */
.hero--grand > .sec--first { width: 100%; padding-block: clamp(26px, 3.4vw, 48px); }
/* Le titre respire d'un cran de moins que sur les pages intérieures : c'est
   la photographie qui porte l'échelle, le titre n'a plus à la porter seul.
   Le second terme du clamp regarde AUSSI la hauteur d'écran : sur un portable
   bas de plafond, le titre se resserre pour que les boutons restent visibles
   sans défiler — c'est là que se joue l'appel. */
.hero--grand .mega { font-size: clamp(42px, min(6.4vw, 12svh), 96px); }
.hero--grand .hero__bas { margin-top: clamp(22px, 2.6vw, 34px); }
.hero--grand .rule-grow.mt-xl { margin-top: clamp(20px, 2.4vw, 32px); }

/* Légende de la photographie : ce que l'on regarde, dit en un souffle.
   Filet d'un pixel et fond blanc à 88 % — lisible quelle que soit l'image. */
/* Socle du héros : un filet qui traverse, ce que fait la maison à gauche, ce
   que montre la photographie à droite. Le retrait du bas dégage l'interrupteur
   d'ondes, qui flotte dans ce coin. */
.hero__socle {
  position: relative; z-index: 1; width: 100%;
  padding-bottom: clamp(26px, 8vh, 76px);
}
.hero__socle__in {
  display: flex; flex-wrap: wrap; gap: 12px 30px;
  align-items: baseline; justify-content: space-between;
  padding-top: 16px;
}
.hero__reps {
  display: flex; flex-wrap: wrap; gap: 10px 28px;
  list-style: none; margin: 0; padding: 0;
}
/* --text et non --mute : le socle tombe sur la photographie, et sur mobile il
   se retrouve carrément dessus. Mesuré sur le rendu, --mute descendait à
   3,4:1 en bas de cadre ; --text tient 5,8:1 au pire endroit. */
.hero__reps li {
  display: flex; align-items: center; gap: 10px;
  font-size: 11px; font-weight: 500; letter-spacing: .22em; text-transform: uppercase;
  color: var(--text);
}
/* Un carré, pas une pastille : aucun arrondi dans cette charte. */
.hero__reps li::before {
  content: ""; width: 5px; height: 5px; background: var(--bronze); flex: none;
}
.hero__note {
  margin: 0;
  font-size: 10.5px; font-weight: 500; letter-spacing: .2em; text-transform: uppercase;
  color: var(--text); line-height: 1.5;
}

/* Arrivée : la photographie se pose — une seule fois, jamais rejouée.
   Uniquement une échelle, JAMAIS un fondu depuis l'opacité zéro : un élément
   transparent n'est pas candidat au LCP, et le fondu décalerait la mesure de
   toute sa durée. Là, l'image est peinte tout de suite et c'est le cadrage
   qui se resserre. Sans JS ou en mouvement réduit, elle est simplement là. */
.js .hero__img { transform: scale(1.075); }
.js .hero__img.on { transform: none; transition: transform 1.6s var(--ease); }

@media (max-width: 900px) {
  /* Le texte prend toute la largeur : le voile redevient vertical, et la
     photographie se lit dans le bas du cadre, sous les boutons. */
  /* Le contenu se cale en haut : tout le bas du cadre revient à la
     photographie, au lieu d'un blanc vide sous l'en-tête. */
  .hero--grand { min-height: min(100svh, 820px); align-content: start; }
  .hero__fond::before {
    background: linear-gradient(to bottom,
      #fff 0%, rgba(255,255,255,.97) 40%, rgba(255,255,255,.86) 57%,
      rgba(255,255,255,.44) 76%, rgba(255,255,255,.08) 93%, rgba(255,255,255,0) 100%);
  }
  /* Le bouton en filet tombe sur la photographie : il lui faut sa propre
     surface, sinon son libellé dépend de ce qu'il y a derrière. */
  .hero--grand .btn--ghost { background: rgba(255,255,255,.88); }
  .hero__fond::after {
    background: linear-gradient(to top, #fff 0%, rgba(255,255,255,.5) 7%, rgba(255,255,255,0) 24%);
  }
  .hero__img img { object-position: 50% 58%; }
  .hero--grand .rule-grow { max-width: none; }
  .hero__note { display: none; }
  .hero__socle { padding-bottom: clamp(22px, 4vh, 34px); }
}
@media (prefers-reduced-motion: reduce) {
  .js .hero__img { transform: none; }
}


/* ==========================================================================
   Le chantier signature — Musée des Beaux-Arts de Reims
   Une piste de plusieurs écrans, un cadre qui reste collé, et quatre vues qui
   se relaient au fil du défilement. Rien n'est détourné : la page avance
   normalement, c'est l'image qui change dessous.
   Sous 900 px, sans JS et en mouvement réduit, la piste retombe à sa hauteur
   naturelle et les quatre vues sont simplement empilées — même contenu,
   même ordre, aucune information perdue.
   ========================================================================== */
.chantier { background: var(--sand); }
.chantier__piste { position: relative; }
.chantier__in {
  display: grid;
  gap: clamp(30px, 3.6vw, 48px);
  padding-block: clamp(52px, 7vw, 92px);
}
.chantier__col { align-self: center; min-width: 0; }
/* Le titre porte la section ; la mention voisine ne fait que la situer.
   Le terme en svh le fait rentrer sur un ecran bas de plafond, ou le cadre
   collant ne peut rien renvoyer plus bas. */
.chantier__col h2 { font-size: clamp(29px, min(3.5vw, 5.6svh), 50px); }
.chantier__col .rule { max-width: 240px; margin-top: clamp(16px, 2.4svh, 26px); }
.chantier__col .lede {
  margin-top: clamp(14px, 2.2svh, 24px); max-width: 44ch;
  font-size: clamp(14.5px, min(1.08vw, 2.1svh), 17px);
}
.chantier__col .btn { margin-top: clamp(18px, 2.8svh, 30px); }

/* La mention tient la place que tenait le chiffre de surface, retire le
   21/08/2026 a la demande du client. Elle reste DERRIERE le titre dans la
   hierarchie : c'est le nom du chantier qui porte la section, la mention ne
   fait que donner l'echelle, sans la chiffrer. */
.mention { margin-top: clamp(16px, 2.4svh, 28px); }
.mention__t {
  display: block;
  font-size: clamp(24px, min(2.8vw, 4.2svh), 38px); line-height: 1.04;
  color: var(--bronze-ink);
}
.mention__l {
  display: block; margin-top: 12px;
  font-size: 11.5px; font-weight: 500; letter-spacing: .2em;
  text-transform: uppercase; color: var(--text);
  line-height: 1.6; max-width: 28ch;
}

.chantier__vues { min-width: 0; }
.chantier__pile { position: relative; display: grid; gap: 16px; }
.cvue { margin: 0; background: var(--sand); }
.cvue img { display: block; width: 100%; height: auto; }
.cvue__cap {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 3px 12px;
  padding-top: 11px;
}
.cvue__n {
  font-size: 11px; font-weight: 500; letter-spacing: .2em;
  color: var(--bronze-ink);
}
.cvue__t {
  font-size: 13.5px; font-weight: 500; letter-spacing: -.005em; color: var(--ink);
}
.cvue__d { font-size: 13px; line-height: 1.5; color: var(--mute); }

/* Repères de progression : ils ne servent qu'à dire où l'on en est dans la
   piste, donc ils n'existent qu'avec elle — et ils sont hors de l'arbre
   d'accessibilité, la légende de chaque vue porte déjà l'information. */
.chantier__pts { display: none; list-style: none; margin: 18px 0 0; padding: 0; }
.cpt {
  flex: 1; padding-top: 10px; border-top: 1px solid var(--rule);
  font-size: 11px; font-weight: 500; letter-spacing: .2em; color: var(--mute);
  transition: color .45s var(--ease), border-top-color .45s var(--ease);
}
.cpt.is-on { color: var(--ink); border-top-color: var(--bronze); }

@media (min-width: 900px) and (prefers-reduced-motion: no-preference) {
  .js .chantier__piste { height: 420vh; }
  /* Un élément collant crée son PROPRE contexte d'empilement, quoi qu'il
     arrive : les photographies qu'il contient ne peuvent plus remonter
     seules au-dessus du canvas d'ondes. On monte donc le cadre entier. */
  .js .chantier__cadre {
    position: sticky; top: var(--head);
    height: calc(100svh - var(--head));
    display: grid; align-content: center;
    z-index: var(--z-pur);
  }
  /* width:100% explicite : .shell porte margin-inline auto, et dans une
     grille un enfant qui l'a ne s'etire plus — il se dimensionne sur son
     contenu et les deux colonnes se retrouvent ecrasees au centre.
     Meme piege que .hero--grand > .sec--first. */
  .js .chantier__in {
    width: 100%;
    grid-template-columns: minmax(290px, 36%) 1fr;
    align-items: center; padding-block: 0;
  }
  .js .chantier__pile {
    display: block; overflow: hidden;
    height: min(calc(100svh - var(--head) - 176px), 640px);
  }
  /* visibility et pas seulement opacity : une vue éteinte ne doit pas rester
     survolable ni attraper le focus sous celle qui est affichée. */
  .js .cvue {
    position: absolute; inset: 0; opacity: 0; visibility: hidden;
    transition: opacity .8s var(--ease), visibility .8s var(--ease);
  }
  .js .cvue.is-on { opacity: 1; visibility: visible; }
  /* <picture> est inline par defaut, donc sans hauteur : l'image y retombe
     sur son ratio et laisse une bande vide au bas du cadre. */
  .js .cvue picture { display: block; height: 100%; }
  .js .cvue img {
    width: 100%; height: 100%; object-fit: cover;
    transform: scale(1.055); transition: transform 1.6s var(--ease);
  }
  .js .cvue.is-on img { transform: none; }
  /* La légende repasse dans le cadre : la pile est en overflow caché. */
  .js .cvue__cap {
    position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
    padding: 13px 16px; background: rgba(255,255,255,.92);
    -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  }
  .js .chantier__pts { display: flex; }
}

@media (max-width: 899px) {
  .mention__l { max-width: none; }
}

.surface { background: rgba(255,255,255,.86); }
.sand  { background: var(--sand); }
.dark  { background: var(--ink); color: var(--d-sand); }
.dark .rule { background: var(--d-rule); }

.rule { height: 1px; background: var(--rule); border: 0; margin: 0; }
/* Le filet de fin de héros se trace deux fois : un trait de bronze part
   devant, le filet gris le suit, et le bronze se dissout une fois arrivé.
   Il ne reste qu'un filet d'un pixel — l'accent n'a fait que passer. */
.rule-grow { position: relative; height: 1px; background: none; }
.rule-grow::before, .rule-grow::after {
  content: ""; position: absolute; inset: 0; transform-origin: left;
}
.rule-grow::before { background: var(--rule); }
.rule-grow::after { background: var(--bronze); opacity: 0; }
.dark .rule-grow::before { background: var(--d-rule); }
.js .rule-grow::before { transform: scaleX(0); transition: transform 1.45s var(--ease-lever); }
.js .rule-grow.on::before { transform: scaleX(1); }
.js .rule-grow.on::after { animation: filet-bronze 1.6s var(--ease-lever) both; }
@keyframes filet-bronze {
  0%   { transform: scaleX(0); opacity: 1; }
  46%  { transform: scaleX(1); opacity: 1; }
  100% { transform: scaleX(1); opacity: 0; }
}

/* ---------- Titres de section ---------- */
.sec__head { display: grid; grid-template-columns: 1fr; gap: 26px; margin-bottom: clamp(38px, 5vw, 72px); }
@media (min-width: 900px) {
  .sec__head { grid-template-columns: minmax(0,1.05fr) minmax(0,.85fr); gap: clamp(30px, 5vw, 90px); align-items: end; }
}
.sec__head h2 { margin-top: 14px; }

.split { display: grid; gap: clamp(34px, 5vw, 74px); }
@media (min-width: 980px) { .split { grid-template-columns: minmax(0,.78fr) minmax(0,1.22fr); } }
.split__aside { position: sticky; top: calc(var(--head) + 40px); align-self: start; }
@media (max-width: 979px) { .split__aside { position: static; } }
.split__aside h2 { margin-top: 14px; }
.split__aside p { margin-top: 20px; max-width: 42ch; color: var(--mute); }

.duo { display: grid; gap: clamp(34px, 5vw, 74px); align-items: center; }
@media (min-width: 980px) { .duo { grid-template-columns: minmax(0,1fr) minmax(0,1.05fr); } }

/* ---------- Cadres photo ---------- */
.frame {
  position: relative; overflow: hidden; background: var(--sand);
  border: 1px solid var(--rule); isolation: isolate;
}
.dark .frame { border-color: var(--d-rule); background: #141414; }
/* Le cadre impose le format ; l'image le remplit en position absolue.
   Sans cela, height:100% dans un parent en aspect-ratio se résout en auto
   et c'est l'image qui impose sa hauteur — la grille part de travers. */
.frame > picture { position: absolute; inset: 0; display: block; }
.frame img { width: 100%; height: 100%; object-fit: cover; display: block; }
.frame::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 2;
  background: linear-gradient(180deg, rgba(15,15,15,.10) 0%, rgba(15,15,15,0) 34%, rgba(15,15,15,0) 62%, rgba(15,15,15,.20) 100%);
}
.frame__cap {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
  padding: 16px 18px; display: flex; justify-content: space-between; gap: 16px;
  font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: #fff;
  text-shadow: 0 1px 14px rgba(0,0,0,.6);
}

/* ---------- Cartes ---------- */
.cards { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); }
@media (min-width: 700px) { .cards { grid-template-columns: 1fr 1fr; } }
.card {
  background: var(--paper); padding: clamp(26px, 3vw, 42px);
  display: grid; gap: 18px; align-content: start;
  position: relative; transition: background .4s var(--ease);
}
/* Un filet de bronze se tire en haut de la carte au survol. Deux pixels,
   aucun déplacement : la hauteur de la grille ne bouge pas d'un cheveu. */
.card::before {
  content: ""; position: absolute; left: 0; top: 0; height: 2px; width: 100%;
  background: var(--bronze); transform: scaleX(0); transform-origin: left;
  transition: transform .55s var(--ease-lever);
}
.card:hover::before, .card:focus-within::before { transform: scaleX(1); }
.card__n { font-size: 11px; letter-spacing: .3em; color: var(--bronze-ink); }
.card p { color: var(--mute); font-size: 15.5px; }
.card:hover { background: var(--sand); }

/* ---------- Listes de couches ---------- */
.layer { display: grid; grid-template-columns: 54px 1fr; gap: 22px; padding: 26px 0; border-top: 1px solid var(--d-rule); }
.layer:last-child { border-bottom: 1px solid var(--d-rule); }
.layer__n { font-size: 11px; letter-spacing: .28em; color: var(--bronze); padding-top: 5px; }
.layer h3 { color: var(--sand); }
.layer p { color: var(--d-mute); font-size: 15.5px; margin-top: 8px; }

/* ---------- Grandeurs acoustiques (accueil) ---------- */
/* Pendant clair de .layer : mêmes colonnes, mêmes filets, fond papier. */
.mesures { margin-top: 30px; border-top: 1px solid var(--rule); }
.mesure { display: grid; grid-template-columns: 54px 1fr; gap: 22px; padding: 20px 0; border-bottom: 1px solid var(--rule); }
.mesure__n { font-size: 11px; letter-spacing: .28em; color: var(--bronze-ink); padding-top: 4px; }
.mesure b { display: block; font-size: 15.5px; font-weight: 500; color: var(--ink); letter-spacing: -.01em; }
.mesure span { display: block; font-size: 14.5px; line-height: 1.55; color: var(--mute); margin-top: 7px; max-width: 56ch; }
.fine { font-size: 14.5px; line-height: 1.6; color: var(--mute); max-width: 60ch; }
.absorb figcaption { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--rule); font-size: 12.5px; line-height: 1.5; color: var(--mute); }

/* ---------- Chiffres / promesses ---------- */
.pledges { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); margin-top: 34px; }
@media (min-width: 640px) { .pledges { grid-template-columns: 1fr 1fr; } }
.pledge { background: var(--paper); padding: 24px; display: grid; gap: 7px; align-content: start; }
.pledge b { font-size: 14px; font-weight: 500; color: var(--ink); letter-spacing: -.005em; }
.pledge span { font-size: 14px; color: var(--mute); line-height: 1.5; }

/* ---------- FAQ ---------- */
.faq { border-top: 1px solid var(--rule); }
.qa { border-bottom: 1px solid var(--rule); }
.qa__q {
  list-style: none; cursor: pointer; padding: 24px 0;
  display: flex; gap: 24px; justify-content: space-between; align-items: baseline;
  font-size: clamp(17px, 1.5vw, 20px); font-weight: 400; color: var(--ink); letter-spacing: -.015em;
}
.qa__q::-webkit-details-marker { display: none; }
.qa__q:hover { color: var(--bronze-ink); }
.qa__q i { font-style: normal; font-size: 20px; color: var(--bronze); transition: transform .3s var(--ease); flex: none; }
.qa[open] .qa__q i { transform: rotate(45deg); }
.qa__a { padding: 0 0 26px; max-width: 68ch; }
.qa__a p { color: var(--mute); }

/* ---------- Boutons ---------- */
.btn {
  display: inline-flex; align-items: center; gap: 12px; min-height: 48px;
  padding: 14px 26px; border: 1px solid var(--ink); background: var(--ink); color: var(--sand);
  font-size: 13.5px; font-weight: 500; letter-spacing: .04em; cursor: pointer;
}
.btn:hover { background: var(--bronze); border-color: var(--bronze); color: #fff; }
.btn--ghost { background: transparent; color: var(--ink); }
.btn--ghost:hover { background: var(--ink); border-color: var(--ink); color: var(--sand); }
.dark .btn { background: var(--sand); border-color: var(--sand); color: var(--ink); }
.dark .btn:hover { background: var(--bronze); border-color: var(--bronze); color: #fff; }
.dark .btn--ghost { background: transparent; color: var(--sand); border-color: var(--d-rule); }
.dark .btn--ghost:hover { background: var(--sand); color: var(--ink); border-color: var(--sand); }
.btn .arw { transition: transform .35s var(--ease); }
.btn:hover .arw { transform: translateX(4px); }

/* ---------- Lien suivant ---------- */
.next { display: block; border-top: 1px solid var(--rule); padding-block: clamp(40px, 5vw, 72px); }
.next__in { display: flex; flex-wrap: wrap; gap: 18px; align-items: baseline; justify-content: space-between; }
.next small { font-size: 11px; letter-spacing: .3em; text-transform: uppercase; color: var(--mute); display: block; margin-bottom: 12px; }
.next b { font-size: clamp(28px, 4vw, 54px); font-weight: 500; letter-spacing: -.03em; color: var(--ink); line-height: 1; }
.next:hover b { color: var(--bronze-ink); }
.next .arw { font-size: 26px; color: var(--bronze); transition: transform .4s var(--ease); }
.next:hover .arw { transform: translateX(10px); }

/* ---------- Pied ---------- */
.foot { background: var(--ink); color: var(--d-mute); padding-block: clamp(52px, 6vw, 84px) 34px; }
.foot__grid { display: grid; gap: 40px; }
@media (min-width: 860px) { .foot__grid { grid-template-columns: 1.4fr 1fr 1fr; gap: 56px; } }
.foot .foot-h { font-size: 11px; line-height: 1.4; letter-spacing: .3em; text-transform: uppercase; color: var(--d-warm); font-weight: 500; margin-bottom: 18px; }
.foot a { color: var(--d-sand); font-size: 14.5px; }
.foot a:hover { color: var(--bronze); }
/* WCAG 2.2 — 2.5.8 : une cible tactile fait au moins 24 x 24 px.
   Les liens de pied de page n'y arrivaient pas avec leur seule ligne de texte. */
.foot__links { display: grid; gap: 6px; }
.foot__links a, .foot__links span { display: inline-block; padding-block: 5px; }
.foot__bar a { display: inline-block; padding-block: 5px; }
/* Logo détouré, sable sur transparent : pas de filtre, pas de rectangle blanc. */
.foot__brand img { width: 150px; height: auto; opacity: .95; }
.foot__brand p { color: var(--d-mute); font-size: 14.5px; margin-top: 20px; max-width: 34ch; }
.foot__bar {
  margin-top: clamp(40px, 5vw, 66px); padding-top: 24px; border-top: 1px solid var(--d-rule);
  display: flex; flex-wrap: wrap; gap: 10px 26px; align-items: center;
  font-size: 12.5px; color: var(--d-mute);
}
.foot__bar a { font-size: 12.5px; }

/* ---------- Révélations ----------
   Une montée courte et une sortie longue : --ease-lever démarre franchement
   puis pose l'élément sans rebond. Pas de flou, pas d'échelle sur le texte —
   sur une page en filets d'un pixel, le seul luxe permis est la durée.
   Le décalage des enfants va jusqu'à douze : la grille des lieux en a huit,
   et les deux derniers arrivaient sans décalage. */
.js .rise { opacity: 0; transform: translate3d(0, 26px, 0); }
.js .rise.on {
  opacity: 1; transform: none;
  transition: opacity 1s var(--ease-lever), transform 1s var(--ease-lever);
}
.js .rise-d1.on { transition-delay: .10s; }
.js .rise-d2.on { transition-delay: .20s; }
.js .rise-d3.on { transition-delay: .30s; }
.js .reveal > * { opacity: 0; transform: translate3d(0, 24px, 0); }
.js .reveal.on > * {
  opacity: 1; transform: none;
  transition: opacity .86s var(--ease-lever), transform .86s var(--ease-lever);
}
.js .reveal.on > *:nth-child(2)  { transition-delay: .075s; }
.js .reveal.on > *:nth-child(3)  { transition-delay: .150s; }
.js .reveal.on > *:nth-child(4)  { transition-delay: .225s; }
.js .reveal.on > *:nth-child(5)  { transition-delay: .300s; }
.js .reveal.on > *:nth-child(6)  { transition-delay: .375s; }
.js .reveal.on > *:nth-child(7)  { transition-delay: .450s; }
.js .reveal.on > *:nth-child(8)  { transition-delay: .525s; }
.js .reveal.on > *:nth-child(9)  { transition-delay: .600s; }
.js .reveal.on > *:nth-child(10) { transition-delay: .675s; }
.js .reveal.on > *:nth-child(11) { transition-delay: .750s; }
.js .reveal.on > *:nth-child(12) { transition-delay: .825s; }

/* Une photographie se pose : elle arrive un rien trop grande et se cale dans
   son cadre pendant que le bloc monte. Réservé aux conteneurs marqués .pose,
   pour ne pas entrer en conflit avec les grossissements au survol. */
.js .pose .frame img { transform: scale(1.06); }
.js .pose.on .frame img { transform: none; transition: transform 1.7s var(--ease-lever); }

/* ---------- Bandeau photo défilant ---------- */
.marquee { position: relative; overflow: hidden; padding-block: 8px; }
.marquee__track { display: flex; gap: 14px; width: max-content; will-change: transform; }
.marquee--a .marquee__track { animation: slide-l 78s linear infinite; }
.marquee--b .marquee__track { animation: slide-r 92s linear infinite; }
.marquee:hover .marquee__track, .marquee:focus-within .marquee__track { animation-play-state: paused; }
@keyframes slide-l { from { transform: translate3d(0,0,0); } to { transform: translate3d(-50%,0,0); } }
@keyframes slide-r { from { transform: translate3d(-50%,0,0); } to { transform: translate3d(0,0,0); } }
.marquee__item { flex: none; width: clamp(210px, 24vw, 340px); }
.marquee__item .frame { height: clamp(150px, 17vw, 232px); }
.marquee__item img { transition: transform 1.2s var(--ease); }
.marquee__item:hover img { transform: scale(1.05); }

/* ---------- Galerie ---------- */
.filters { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: clamp(26px, 3vw, 40px); }
.filter {
  border: 1px solid var(--rule); background: transparent; cursor: pointer; font: inherit;
  font-size: 12.5px; letter-spacing: .04em; color: var(--text);
  padding: 10px 16px; min-height: 44px; display: inline-flex; align-items: center; gap: 8px;
}
.filter:hover { border-color: var(--ink); color: var(--ink); }
.filter[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--sand); }
.filter span { font-size: 11px; color: var(--mute); }
.filter[aria-pressed="true"] span { color: var(--d-warm); }

.grid-photos { display: grid; gap: 14px; grid-template-columns: repeat(2, 1fr); }
@media (min-width: 760px)  { .grid-photos { grid-template-columns: repeat(3, 1fr); gap: 16px; } }
@media (min-width: 1180px) { .grid-photos { grid-template-columns: repeat(4, 1fr); gap: 18px; } }
.tile { position: relative; margin: 0; cursor: pointer; background: none; border: 0; padding: 0; font: inherit; text-align: left; display: block; }
/* Mosaïque strictement régulière : toutes les vignettes au même format.
   Sur une photo verticale, le plafond est en haut du cadre — on remonte
   donc le point d'ancrage plutôt que de couper le sujet. Le format réel
   est restitué dans la visionneuse. */
.tile .frame { aspect-ratio: 4 / 3; }
.tile.is-portrait img { object-position: center 34%; }
.tile img { transition: transform 1.1s var(--ease); }
.tile:hover img, .tile:focus-visible img { transform: scale(1.045); }
.tile__cap {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; padding: 14px 15px;
  font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase; color: #fff;
  opacity: 0; transform: translateY(6px); transition: opacity .35s var(--ease), transform .35s var(--ease);
  text-shadow: 0 1px 12px rgba(0,0,0,.7);
}
.tile:hover .tile__cap, .tile:focus-visible .tile__cap { opacity: 1; transform: none; }
.tile[hidden] { display: none; }

/* ---------- Visionneuse ---------- */
.lightbox {
  position: fixed; inset: 0; z-index: var(--z-lightbox); background: #0A0A0A;
  display: none; grid-template-rows: auto minmax(0, 1fr) auto; padding: 14px;
  height: 100dvh;
}
.lightbox[open], .lightbox.open { display: grid; }
.lightbox__top { display: flex; justify-content: space-between; align-items: center; gap: 16px; color: var(--d-sand); font-size: 12px; letter-spacing: .18em; text-transform: uppercase; padding: 6px 4px; }
.lightbox__stage { position: relative; display: grid; place-items: center; min-height: 0; overflow: hidden; }
/* Chaque niveau doit accepter d'être plus petit que son contenu, sinon
   l'image impose sa hauteur naturelle et sort de l'écran. */
/* Flex et non grid : dans une grille, le max-height:100% de l'image se
   calcule sur une piste dimensionnée par... l'image elle-même, et ne
   borne donc rien. En flex, il se calcule sur la hauteur du conteneur. */
.lightbox__stage__in {
  width: 100%; height: 100%; min-width: 0; min-height: 0;
  display: flex; align-items: center; justify-content: center;
}
.lightbox__stage__in picture { display: contents; }
.lightbox__stage img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; }
.lightbox__cap { color: var(--d-sand); font-size: 14px; line-height: 1.5; text-align: center; padding: 16px 8px 8px; max-width: 70ch; margin-inline: auto; }
/* La famille de chantier, au-dessus de la description : on sait où l'on est
   sans revenir à la grille. --d-warm et non --bronze : 7:1 sur le fond noir. */
.lightbox__cat {
  display: block; margin-bottom: 7px;
  font-size: 10.5px; letter-spacing: .22em; text-transform: uppercase; color: var(--d-warm);
}
.lb-btn {
  background: transparent; border: 1px solid var(--d-rule); color: var(--sand); cursor: pointer;
  width: 46px; height: 46px; display: inline-grid; place-items: center; font-size: 17px; font-family: inherit;
}
.lb-btn:hover { background: var(--sand); color: var(--ink); border-color: var(--sand); }
.lightbox__nav { position: absolute; inset: 0; display: flex; justify-content: space-between; align-items: center; pointer-events: none; }
.lightbox__nav .lb-btn { pointer-events: auto; }
body.lb-open { overflow: hidden; }

/* ---------- Bloc contact ---------- */
.call-card { display: block; border: 1px solid var(--d-rule); padding: 30px; }
.call-card:hover { border-color: var(--bronze); background: #151312; }
.call-card small { display: block; font-size: 11px; letter-spacing: .3em; text-transform: uppercase; color: var(--d-warm); margin-bottom: 12px; }
.call-card b { font-size: clamp(28px, 3.4vw, 42px); font-weight: 500; letter-spacing: -.02em; color: var(--sand); display: block; }
.contact__meta { display: grid; gap: 1px; background: var(--d-rule); border: 1px solid var(--d-rule); margin-top: 1px; }
@media (min-width: 560px) { .contact__meta { grid-template-columns: 1fr 1fr; } }
.contact__meta > div { background: var(--ink); padding: 24px 30px; }
.contact__meta small { display: block; font-size: 11px; letter-spacing: .3em; text-transform: uppercase; color: var(--d-warm); margin-bottom: 10px; }
.contact__meta a, .contact__meta p { color: var(--d-sand); font-size: 15px; word-break: break-word; }
.contact__meta a:hover { color: var(--bronze); }

/* ---------- Fil d'Ariane ---------- */
.crumb { font-size: 12px; letter-spacing: .05em; color: var(--mute); padding-top: 26px; }
.crumb a:hover { color: var(--bronze-ink); }
.crumb span { color: var(--rule); margin-inline: 8px; }

/* ---------- Réglage du mouvement ---------- */
.motion-toggle {
  position: fixed; right: 14px; bottom: 14px; z-index: 41;
  border: 1px solid var(--rule); background: rgba(255,255,255,.9); color: var(--mute);
  font: inherit; font-size: 11px; letter-spacing: .16em; text-transform: uppercase;
  padding: 0 14px; height: 40px; cursor: pointer; display: inline-flex; align-items: center; gap: 9px;
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
.motion-toggle:hover { color: var(--ink); border-color: var(--ink); }
.motion-toggle .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--bronze); }
.motion-toggle[aria-pressed="false"] .dot { background: var(--rule); }
@media (max-width: 700px) { .motion-toggle { display: none; } }

/* ---------- Accessibilité du mouvement ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important; scroll-behavior: auto !important;
  }
  .js .rise, .js .reveal > * { opacity: 1 !important; transform: none !important; }
  .js .rule-grow, .js .rule-grow::before { transform: none !important; }
  .js .rule-grow::after { animation: none !important; opacity: 0 !important; }
  .js .pose .frame img { transform: none !important; }
  /* Le filet de la carte reste, il se pose d'un coup : c'est un changement
     d'état, pas un mouvement. */
  .absorb .trace { stroke-dasharray: none !important; stroke-dashoffset: 0 !important; animation: none !important; }
  .js .absorb .fondu { opacity: 1 !important; animation: none !important; }
  .marquee__track { animation: none !important; }
  .marquee { overflow-x: auto; }
  /* Le diaporama du héros ne démarre pas (mmc.js s'en charge) ; ce filet
     couvre le cas où il tournerait déjà quand le réglage système bascule. */
  .diapo__v { transition: none !important; }
}

/* ---------- Transitions de page (natif, sans librairie) ---------- */
@view-transition { navigation: auto; }
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root) { animation: vt-out .28s var(--ease) both; }
  ::view-transition-new(root) { animation: vt-in .42s var(--ease) both; }
}
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; transform: translateY(10px); } }

/* ==========================================================================
   Blocs de page
   ========================================================================== */

.hero__bas { display: grid; gap: 30px; margin-top: clamp(30px, 4vw, 52px); }
@media (min-width: 900px) {
  .hero__bas { grid-template-columns: minmax(0, 1fr) auto; align-items: end; gap: 56px; }
}
.hero__bas .lede { max-width: 60ch; }
/* Sur l'accueil, le pied du héros reste dans la colonne de gauche : les
   boutons ne partent pas se poser au milieu de la photographie. */
@media (min-width: 900px) {
  .hero--grand .hero__bas { grid-template-columns: minmax(0, 1fr); gap: 30px; max-width: 54%; }
  .hero--grand .hero__bas .lede { max-width: 48ch; }
}
.hero__cta, .ctas { display: flex; flex-wrap: wrap; gap: 12px; }

.manifeste { display: grid; gap: 26px; }
.manifeste .lede { max-width: 62ch; }
.manifeste .lede:first-child { font-size: clamp(20px, 2vw, 27px); line-height: 1.4; color: var(--ink); letter-spacing: -.015em; }

.sand-block { background: rgba(244,241,237,.9); }
.surface { background: rgba(255,255,255,.88); }

.coupe svg, .absorb svg { width: 100%; height: auto; display: block; }

/* Le schéma d'absorption se dessine au lieu d'apparaître : chaque trajet et
   chaque courbe porte pathLength="1", donc un seul jeu de valeurs suffit
   quelle que soit la longueur réelle du tracé. Les trois retards suivent la
   lecture — la salle nue, la salle traitée, puis la décroissance. */
.absorb .trace { stroke-dasharray: 1; stroke-dashoffset: 1; }
.js .absorb.on .trace { animation: tracer 1.3s var(--ease-lever) both; animation-delay: .25s; }
.js .absorb.on .trace--1b { animation-delay: .42s; }
.js .absorb.on .trace--2 { animation-delay: .70s; }
.js .absorb.on .trace--3 { animation-delay: 1.05s; animation-duration: 1.6s; }
@keyframes tracer { to { stroke-dashoffset: 0; } }
/* La courbe de référence garde ses pointillés : elle ne se dessine pas, elle
   est déjà là — c'est l'état avant travaux. Elle se contente de paraître. */
.js .absorb .fondu { opacity: 0; }
.js .absorb.on .fondu { animation: paraitre .9s var(--ease-lever) both; animation-delay: 1.05s; }
@keyframes paraitre { to { opacity: 1; } }
.coupe { background: #141414; border: 1px solid var(--d-rule); padding: clamp(18px, 2.4vw, 34px); }
.absorb { background: var(--paper); border: 1px solid var(--rule); padding: clamp(18px, 2.4vw, 34px); }

/* Sommaire de l'accueil */
.sommaire { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); }
@media (min-width: 720px)  { .sommaire { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1120px) { .sommaire { grid-template-columns: repeat(4, 1fr); } }
.som {
  background: var(--paper); padding: 0 0 26px; display: grid; gap: 12px;
  align-content: start; position: relative;
}
.som .frame { border: 0; border-bottom: 1px solid var(--rule); margin-bottom: 14px; }
.som__n { font-size: 11px; letter-spacing: .3em; color: var(--bronze-ink); padding-inline: 24px; }
.som__t { font-size: clamp(21px, 2vw, 27px); font-weight: 500; letter-spacing: -.028em; color: var(--ink); line-height: 1.1; padding-inline: 24px; }
.som__d { font-size: 14.5px; color: var(--mute); line-height: 1.55; padding-inline: 24px; }
.som__a { font-size: 20px; color: var(--bronze); padding-inline: 24px; transition: transform .4s var(--ease); display: inline-block; }
.som:hover { background: var(--sand); }
.som:hover .som__a { transform: translateX(8px); }
.som:hover img { transform: scale(1.04); }
.som img { transition: transform 1.1s var(--ease); }
.sommaire--404 .som { padding-top: 26px; }
.som--nu { grid-template-columns: 1fr; }

/* Services de l'accueil — cartes au trait, une icône dessinée par service */
.services { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); }
@media (min-width: 720px)  { .services { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1120px) { .services { grid-template-columns: repeat(4, 1fr); } }
.srv {
  background: var(--paper); padding: clamp(26px, 2.6vw, 36px) 24px clamp(26px, 2.4vw, 32px);
  display: grid; gap: 13px; align-content: start;
  transition: background .3s var(--ease);
}
.srv__ico { display: block; width: 48px; height: 48px; color: var(--warm); margin-bottom: 8px; transition: color .3s var(--ease); }
.srv__ico svg { display: block; width: 100%; height: 100%; }
.srv__ico .acc { stroke: var(--bronze); }
.srv__n { font-size: 11px; letter-spacing: .3em; color: var(--bronze-ink); }
.srv__t { font-size: clamp(20px, 1.7vw, 25px); font-weight: 500; letter-spacing: -.028em; color: var(--ink); line-height: 1.14; }
.srv__d { font-size: 14.5px; color: var(--mute); line-height: 1.55; }
.srv__a { font-size: 20px; color: var(--bronze); display: inline-block; margin-top: 5px; transition: transform .35s var(--ease); }
.srv:hover, .srv:focus-visible { background: var(--sand); }
.srv:hover .srv__ico, .srv:focus-visible .srv__ico { color: var(--ink); }
.srv:hover .srv__a, .srv:focus-visible .srv__a { transform: translateX(8px); }

/* ---------- Certification du fabricant ----------
   Un cartouche de plan : le libellé tient la gauche à l'échelle des titres de
   section, un filet vertical ferme sa colonne, et le sceau est apposé à droite
   contre le bord du shell, entre deux filets horizontaux. La bande occupe donc
   toute la largeur au lieu de se tasser dans son tiers gauche, et le vide qui
   reste est tenu par ses deux bords.
   Le sceau garde le bleu de Rockfon, seule couleur de la page qui ne soit ni
   l'encre ni le bronze — il vient d'ailleurs et n'a pas à se fondre. Mesuré à
   6,1:1 sur le sable. Il ne peut pas grandir : l'actif du fabricant est un
   JPEG de 480x140 où le rond ne mesure que ~136 px, déjà monté à 256 dans le
   fichier servi. Au-delà de 116 px d'affichage, le texte qui court sur son
   pourtour devient mou à forte densité. C'est l'échelle du libellé qui porte
   la bande, pas celle du sceau.
   L'ordre du DOM place le sceau en premier : en colonne unique il ouvre la
   bande, et au-delà de 860 px la grille le renvoie à droite. */
.certif { display: grid; gap: clamp(26px, 3.4vw, 44px); }
@media (min-width: 860px) {
  .certif { grid-template-columns: minmax(0, 1fr) auto; align-items: stretch; }
  .certif__sceau { grid-area: 1 / 2; align-self: center; }
  .certif__txt {
    grid-area: 1 / 1;
    border-right: 1px solid var(--rule);
    padding-right: clamp(32px, 4vw, 64px);
  }
}
.certif__sceau { width: clamp(84px, 9vw, 116px); }
.certif__sceau img { display: block; width: 100%; height: auto; }
.certif__txt { display: grid; gap: 16px; align-content: center; }
.certif__h { font-size: clamp(27px, 3.6vw, 50px); line-height: 1.04; }
.certif sup { font-size: .44em; line-height: 0; vertical-align: super; }

/* Étapes numérotées */
.etapes { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; background: var(--d-rule); border: 1px solid var(--d-rule); }
@media (min-width: 760px) { .etapes { grid-template-columns: 1fr 1fr; } }
.etape { background: var(--ink); padding: clamp(24px, 2.6vw, 36px); display: grid; grid-template-columns: 52px 1fr; gap: 18px; align-items: start; }
.etape__n { font-size: 11px; letter-spacing: .28em; color: var(--bronze); padding-top: 5px; }
.etape h3 { color: var(--sand); }
.etape p { color: var(--d-mute); font-size: 15px; margin-top: 9px; }
.etapes--clair { background: var(--rule); border-color: var(--rule); }
.etapes--clair .etape { background: var(--paper); }
.etapes--clair .etape h3 { color: var(--ink); }
.etapes--clair .etape p { color: var(--mute); }
.etapes--clair .etape__n { color: var(--bronze-ink); }

/* Fiche descriptive */
.fiche { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); margin: 0; }
@media (min-width: 700px) { .fiche { grid-template-columns: 1fr 1fr; } }
.fiche > div { background: var(--paper); padding: 22px 26px; display: grid; gap: 7px; }
.fiche dt { font-size: 11px; letter-spacing: .24em; text-transform: uppercase; color: var(--mute); margin: 0; }
.fiche dd { margin: 0; font-size: 16px; color: var(--ink); }
.fiche dd a { border-bottom: 1px solid var(--rule); }
.fiche dd a:hover { border-color: var(--bronze); }
.fiche--serree > div { padding: 18px 22px; }

/* Sceaux matériau */
.seals { display: grid; gap: 1px; background: var(--d-rule); border: 1px solid var(--d-rule); margin-top: 30px; }
@media (min-width: 520px) { .seals { grid-template-columns: 1fr 1fr; } }
.seal { background: var(--ink); padding: 18px 22px; font-size: 13.5px; color: var(--d-sand); letter-spacing: .01em; }

/* Vidéos de chantier */
.videos { display: grid; gap: 20px; }
@media (min-width: 860px) { .videos { grid-template-columns: 1fr 1fr; } }
.video-card { margin: 0; border: 1px solid var(--rule); background: var(--sand); }
.video-card video { width: 100%; height: auto; display: block; background: #111; aspect-ratio: 16 / 9; object-fit: cover; }
.video-card figcaption { padding: 16px 18px; font-size: 14px; color: var(--mute); }

/* ---------- Avant / après ----------
   Deux vues du même ouvrage, une seule gouttière de 4 px : l'œil doit lire un
   bloc coupé en deux, pas deux photographies voisines. Même format et même
   recadrage sur les deux, sinon la comparaison ne veut plus rien dire.
   Sous 480 px on empile — côte à côte, ce sont deux timbres-poste ; c'est là
   que les étiquettes deviennent indispensables. */
.duel { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
@media (max-width: 479px) { .duel { grid-template-columns: 1fr; } }
.duel__vue { position: relative; z-index: var(--z-pur); }
.duel__vue .frame { border: 0; }
.duel__lb {
  /* .duel__vue est une surface pure (z-index 31) et crée son contexte : sans
     une valeur au-dessus de celle de .frame, l'étiquette passe sous la photo. */
  position: absolute; left: 0; top: 0; z-index: 40;
  padding: 9px 15px; font-size: 10.5px; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase;
}
.duel__lb--avant { background: var(--ink); color: var(--sand); }
.duel__lb--apres { background: var(--bronze-ink); color: #fff; }

/* ---------- Page Réalisations ----------
   C'est une page qui se regarde, pas qui se lit : le titre et le chapô se
   partagent une ligne pour que la barre de filtres et la première rangée de
   photographies entrent dans le premier écran. */
.gal__intro { display: grid; gap: clamp(24px, 3.2vw, 54px); }
@media (min-width: 980px) {
  .gal__intro { grid-template-columns: minmax(0, 1.08fr) minmax(0, .92fr); align-items: end; }
  .gal__intro h1 { font-size: clamp(44px, 6.1vw, 88px); }
}
.gal__intro .lede { max-width: 54ch; }
@media (min-width: 980px) { .gal__intro .lede { padding-bottom: 10px; } }

/* Barre de galerie — elle suit le défilement : dans un mur de 81 vignettes,
   on change d'avis en cours de route. Elle passe SOUS l'en-tête (--z-head)
   et AU-DESSUS des photographies (--z-pur), sinon elle disparaît derrière
   la grille dès la première rangée. */
.gal__sticky {
  position: sticky; top: calc(var(--head) + 1px); z-index: 34;   /* --head est la hauteur de la barre, +1 px pour sa bordure */
  background: rgba(255, 255, 255, .92);
  -webkit-backdrop-filter: saturate(1.4) blur(14px);
  backdrop-filter: saturate(1.4) blur(14px);
  border-bottom: 1px solid var(--rule);
  padding-block: 12px; margin-bottom: clamp(24px, 3vw, 38px);
}
.gal__bar { display: grid; gap: 16px; }
@media (min-width: 900px) { .gal__bar { grid-template-columns: 1fr auto; align-items: center; } }
.gal__bar .filters { margin: 0; }
/* Sous 900 px, les sept filtres prendraient quatre lignes collées sous
   l'en-tête : ils défilent horizontalement sur une seule, et le compte
   n'existe plus que pour les lecteurs d'écran. */
@media (max-width: 899px) {
  .gal__bar .filters {
    flex-wrap: nowrap; overflow-x: auto; overscroll-behavior-x: contain;
    scroll-snap-type: x proximity; scrollbar-width: none;
    margin-inline: calc(var(--gutter) * -1); padding-inline: var(--gutter);
    /* Sans cette ligne, le calage magnetique aligne le premier bouton sur le
       bord de la zone de defilement et mange le padding : au chargement, la
       bande arrive deja decalee de 20 px et « Toutes » colle au bord de
       l'ecran. Le scroll-padding remet la gouttiere dans le calcul du snap. */
    scroll-padding-inline: var(--gutter);
  }
  .gal__bar .filters::-webkit-scrollbar { display: none; }
  .gal__bar .filter { flex: none; scroll-snap-align: start; }
  .gal__bar .gal__compte {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
}
.gal__compte { font-size: 12px; letter-spacing: .2em; text-transform: uppercase; color: var(--mute); white-space: nowrap; }
.gal__note { margin-top: 28px; font-size: 13.5px; color: var(--mute); }

/* Contact */
.contact-grid { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); }
@media (min-width: 780px) { .contact-grid { grid-template-columns: 1fr 1fr; } }
.contact-card { background: var(--paper); padding: clamp(26px, 3vw, 44px); display: grid; gap: 14px; align-content: start; }
.contact-card small { font-size: 11px; letter-spacing: .3em; text-transform: uppercase; color: var(--mute); }
.contact-card b { font-size: clamp(26px, 3.2vw, 42px); font-weight: 500; letter-spacing: -.03em; color: var(--ink); line-height: 1.06; }
.contact-card__mail { font-size: clamp(17px, 1.8vw, 23px) !important; word-break: break-word; }
.contact-card__adr { font-size: clamp(20px, 2.2vw, 28px) !important; line-height: 1.2; }
.contact-card span { font-size: 14.5px; color: var(--mute); line-height: 1.55; }
a.contact-card:hover { background: var(--sand); }
a.contact-card:hover b { color: var(--bronze-ink); }
.contact-card--fort { background: var(--ink); }
.contact-card--fort small { color: var(--d-warm); }
.contact-card--fort b { color: var(--sand); }
.contact-card--fort span { color: var(--d-mute); }
a.contact-card--fort:hover { background: var(--ink-2); }
a.contact-card--fort:hover b { color: var(--bronze); }

/* Démonstration acoustique — le rapport du canevas suit js/demo-acoustique.js
   (600 × 470 : la salle en coupe, puis la bande de décroissance). */
.demo { display: grid; gap: 18px; }
@media (min-width: 840px) { .demo { grid-template-columns: 1fr 1fr; } }
.demo__cell {
  margin: 0; border: 1px solid var(--d-rule); background: #131313;
  transition: border-color .4s var(--ease);
}
.demo__cell:hover, .demo__cell:focus-within { border-color: #3D3833; }
.demo__cv { width: 100%; height: auto; display: block; aspect-ratio: 600 / 470; cursor: crosshair; }
.demo__cell figcaption { padding: 18px 20px; border-top: 1px solid var(--d-rule); display: grid; gap: 8px; }
.demo__cell figcaption b {
  font-size: 15px; font-weight: 500; color: var(--sand); letter-spacing: -.01em;
  display: flex; align-items: baseline; gap: 11px;
}
/* La pastille reprend la couleur du tracé de la salle : on relie la légende
   au dessin sans écrire « le trait bronze, c'est celle de droite ». */
.demo__cell figcaption b::before {
  content: ""; width: 14px; height: 2px; flex: none; background: var(--d-warm);
}
.demo__cell--trai figcaption b::before { background: var(--bronze); }
.demo__cell figcaption span { font-size: 14px; color: var(--d-mute); line-height: 1.5; }
.demo__cta { margin-top: 26px; display: flex; flex-wrap: wrap; align-items: center; gap: 16px 22px; }
.demo__note { font-size: 13px; color: var(--d-mute); }

/* Pages de texte */
.texte { max-width: 74ch; }
.texte section { margin-top: clamp(38px, 4.4vw, 62px); }
.texte h2 { font-size: clamp(22px, 2.2vw, 30px); margin-bottom: 20px; }
.texte p { margin-bottom: 16px; }
.texte a { border-bottom: 1px solid var(--rule); }
.texte a:hover { color: var(--bronze-ink); border-color: var(--bronze-ink); }
.texte .fiche { max-width: none; }

/* Ordre inversé sur les blocs alternés */
@media (min-width: 980px) {
  .duo--inv > *:first-child { order: 2; }
  .duo--inv > *:last-child { order: 1; }
}

/* ==========================================================================
   Utilitaires
   La CSP du site interdit les styles en ligne (style-src 'self') : tout ce
   qui aurait été un attribut style="" vit ici. Bénéfice annexe : ces règles
   sont mises en cache un an au lieu d'être répétées dans chaque page.
   ========================================================================== */
.ar-16-9 { aspect-ratio: 16 / 9; }
.ar-4-3  { aspect-ratio: 4 / 3; }
.ar-3-2  { aspect-ratio: 3 / 2; }

.mt-14 { margin-top: 14px; }
.mt-20 { margin-top: 20px; }
.mt-22 { margin-top: 22px; }
.mt-24 { margin-top: 24px; }
.mt-26 { margin-top: 26px; }
.mt-30 { margin-top: 30px; }
.mt-34 { margin-top: 34px; }
.mt-38 { margin-top: 38px; }
.mt-lg { margin-top: clamp(36px, 4vw, 60px); }
.mt-xl { margin-top: clamp(40px, 5vw, 72px); }

.items-start { align-items: start; }
.souligne { border-bottom: 1px solid currentColor; }

/* ==========================================================================
   Formulaire de contact
   ========================================================================== */
.form { display: grid; gap: 22px; }
.form__grille { display: grid; gap: 22px; }
@media (min-width: 640px) { .form__grille { grid-template-columns: 1fr 1fr; } }

.champ { display: grid; gap: 9px; margin: 0; }
.champ label { font-size: 12px; letter-spacing: .2em; text-transform: uppercase; color: var(--mute); }
.champ label span { color: var(--bronze-ink); }
.champ label small { font-size: 11px; letter-spacing: .1em; text-transform: none; color: var(--mute); margin-left: 6px; }
.champ input, .champ textarea {
  font: inherit; font-size: 16px; color: var(--ink);
  background: var(--paper);
  /* WCAG 1.4.11 : la bordure d'un champ est un élément d'interface, il lui
     faut 3:1 sur le fond. Le filet --rule du reste du site n'y suffit pas. */
  border: 1px solid var(--warm);
  padding: 14px 16px; min-height: 50px; width: 100%; border-radius: 0;
  transition: border-color .25s var(--ease), background-color .25s var(--ease);
}
.champ textarea { resize: vertical; min-height: 150px; line-height: 1.55; }
.champ input::placeholder, .champ textarea::placeholder { color: #7E756C; }
.champ input:hover, .champ textarea:hover { border-color: var(--text); }
.champ input:focus, .champ textarea:focus { border-color: var(--ink); outline: 2px solid var(--bronze-ink); outline-offset: 2px; }
/* Le style « invalide » n'apparaît qu'après une tentative d'envoi, jamais
   pendant la frappe : signaler une erreur dans un champ vide est hostile. */
.form.a-verifier .champ input:invalid, .form.a-verifier .champ textarea:invalid { border-color: #A33; }

/* Pots de miel : hors écran, hors tabulation, hors lecteurs d'écran. */
.leurre { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form__pied { display: grid; gap: 18px; align-items: start; }
@media (min-width: 720px) { .form__pied { grid-template-columns: auto 1fr; gap: 26px; } }
.form__rgpd { font-size: 13px; line-height: 1.55; color: var(--mute); max-width: 52ch; }
.form__rgpd a { border-bottom: 1px solid var(--rule); }
.form__rgpd a:hover { color: var(--bronze-ink); border-color: var(--bronze-ink); }

.form__alerte {
  border: 1px solid var(--bronze-ink); border-left-width: 3px;
  background: var(--sand); color: var(--ink);
  padding: 16px 20px; margin-bottom: 24px; font-size: 15px; line-height: 1.55;
}
.form__alerte[hidden] { display: none; }

/* ==========================================================================
   Renvoi de fin de bloc
   Un lien de texte, pas un bouton : sur la page Savoir-faire, quatre boutons
   d'affilée écraseraient le seul appel qui compte, celui du bas de page.
   Cible tactile : 44 px de haut malgré la taille de texte.
   ========================================================================== */
.renvoi {
  display: inline-flex; align-items: center; gap: 10px; min-height: 44px;
  margin-top: 22px;
  font-size: 13.5px; font-weight: 500; letter-spacing: .04em;
  /* Encre, pas bronze : sous le voile du canvas d'ondes (alpha composé .132)
     le bronze-ink tombe à 4,1:1, sous l'AA pour du 13,5 px. L'encre y tient
     10:1. Aucun petit texte permanent du site n'est en bronze — le filet
     porte l'affordance, le bronze arrive au survol. */
  color: var(--ink);
}
.renvoi__t { border-bottom: 1px solid var(--warm); padding-bottom: 4px; }
.renvoi:hover { color: var(--bronze-ink); }
.renvoi:hover .renvoi__t { border-color: var(--bronze-ink); }
.renvoi .arw { font-size: 17px; color: var(--bronze); transition: transform .35s var(--ease); }
.renvoi:hover .arw { transform: translateX(4px); }

/* Sommaire ancré : la flèche descend dans la page, elle ne part pas sur le
   côté. Posé après .som:hover — même spécificité, l'ordre tranche. */
.som--ancre .som__a { transform: none; }
.som--ancre:hover .som__a, .som--ancre:focus-visible .som__a { transform: translateY(6px); }
/* En colonne unique, quatre cartes pleine hauteur coûtaient 600 px avant le
   premier volet. La flèche passe à droite du titre, la carte se resserre. */
@media (max-width: 719px) {
  .som--ancre {
    grid-template-columns: 1fr auto; align-items: baseline;
    padding: 16px 0; gap: 4px 12px;
  }
  .som--ancre .som__n, .som--ancre .som__t { grid-column: 1; }
  .som--ancre .som__t { font-size: 20px; }
  .som--ancre .som__a { grid-column: 2; grid-row: 1 / span 2; align-self: center; }
}

/* >>> signature Odratta — ne pas éditer ici, source : ~/.odratta/signature/signature.css */
a.odratta-signature{display:inline-flex;align-items:baseline;gap:.4em;font:400 .8125rem/1.2 inherit;font-family:inherit;color:inherit;text-decoration:none;text-transform:none;letter-spacing:normal;padding:.45em 0;white-space:nowrap}
a.odratta-signature svg{height:.8em;width:auto;flex:none}
a.odratta-signature:hover,a.odratta-signature:focus-visible{text-decoration:underline;text-underline-offset:.3em}
a.odratta-signature:focus-visible{outline:2px solid currentColor;outline-offset:4px;border-radius:3px;text-decoration:none}
/* <<< fin signature Odratta */

/* ==========================================================================
   Le défilé des chantiers — ce sont les PROJETS qui défilent, pas les images
   Demande de Joseph, 21/08/2026. Avant, une piste montrait quatre
   photographies d'un seul chantier ; maintenant elle passe d'un chantier au
   suivant — nom, ville, état et photographie maîtresse changent ensemble.
   La mécanique est celle du bloc chantier (cadre collant, index lu sur la
   progression) : seule l'unité change, et le pilote de site/js/mmc.js compte
   des vues sans savoir ce qu'elles portent.
   Sous 900 px, sans JS et en mouvement réduit, la piste retombe à sa hauteur
   naturelle et les chantiers deviennent une liste de cartes — chaque
   photographie collée à son texte, aucun contenu perdu.
   ========================================================================== */
.projets { background: var(--sand); }
.projets__tete { padding-block: clamp(52px, 7vw, 92px) 0; }
.projets__tete h2 { font-size: var(--fs-h2); }
.projets__tete .lede { max-width: 60ch; }
.projets__piste { position: relative; }
.projets__in { padding-block: clamp(34px, 4.4vw, 56px) clamp(52px, 7vw, 92px); }

/* Une carte de chantier : le texte, puis la photographie. */
.pvue { display: grid; gap: clamp(18px, 2.6vw, 30px); margin: 0; }
.pvue + .pvue { margin-top: clamp(44px, 6vw, 72px); }
.pvue__txt { align-self: center; min-width: 0; }
.pvue__nom {
  font-size: clamp(29px, min(3.5vw, 5.6svh), 50px);
  line-height: 1.04; letter-spacing: -.018em; color: var(--ink);
}
.pvue__txt .rule { max-width: 240px; margin-top: clamp(16px, 2.4svh, 26px); }
.pvue__txt .lede {
  margin-top: clamp(14px, 2.2svh, 24px); max-width: 44ch;
  font-size: clamp(14.5px, min(1.08vw, 2.1svh), 17px);
}
.pvue__txt .btn { margin-top: clamp(18px, 2.8svh, 30px); }
.pvue__nb {
  margin-top: 14px;
  font-size: 11.5px; font-weight: 500; letter-spacing: .2em;
  text-transform: uppercase; color: var(--mute);
}
.pvue__img { min-width: 0; }
.pvue__img img { display: block; width: 100%; height: auto; }

/* En une seule colonne, la photographie passe AVANT son texte : sinon elle
   se retrouve collée au titre du chantier suivant et on la lui attribue. */
@media (max-width: 619px) {
  .pvue__img { order: -1; }
}
@media (min-width: 620px) and (max-width: 899px) {
  .pvue { grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); align-items: center; }
}

@media (min-width: 900px) and (prefers-reduced-motion: no-preference) {
  /* Un écran de défilement par chantier, plus un pour laisser le dernier
     s'installer. À REVOIR si la liste CHANTIERS change de taille : build.py
     lève une alerte au build quand le compte ne correspond plus. */
  .js .projets__piste { height: 900vh; }
  /* width:100% explicite : .shell porte margin-inline auto, et dans une
     grille un enfant qui l'a ne s'étire plus — il se dimensionne sur son
     contenu et les deux colonnes se retrouvent écrasées au centre.
     Même piège que .hero--grand > .sec--first et que .chantier__in. */
  .js .projets__in { width: 100%; padding-block: 0; }
  /* Le cadre collant crée son propre contexte d'empilement : les
     photographies ne peuvent plus remonter seules au-dessus du canvas
     d'ondes, on monte donc le cadre entier (même raison que .chantier). */
  .js .projets__piste .chantier__cadre {
    position: sticky; top: var(--head);
    height: calc(100svh - var(--head));
    display: grid; align-content: center;
    z-index: var(--z-pur);
  }
  .js .projets__scene {
    position: relative; width: 100%;
    height: min(calc(100svh - var(--head) - 118px), 640px);
  }
  /* visibility et pas seulement opacity : un chantier éteint ne doit rester
     ni survolable ni atteignable au clavier sous celui qui est affiché. */
  .js .pvue {
    position: absolute; inset: 0; margin: 0;
    grid-template-columns: minmax(290px, 36%) 1fr;
    align-items: center; gap: clamp(30px, 3.6vw, 48px);
    opacity: 0; visibility: hidden;
    /* Fondu DÉCALÉ, pas croisé : la vue sortante s'efface d'abord, l'entrante
       arrive après. Un fondu symétrique laissait les deux noms de chantier
       lisibles en même temps sur la colonne de gauche — l'image, elle, masquait
       le problème, mais pas le texte sur fond sable. */
    transition: opacity .26s var(--ease), visibility 0s linear .26s;
  }
  .js .pvue + .pvue { margin-top: 0; }
  .js .pvue.is-on {
    opacity: 1; visibility: visible;
    transition: opacity .55s var(--ease) .2s, visibility 0s;
  }
  .js .pvue__img { height: 100%; overflow: hidden; }
  /* <picture> est inline par défaut, donc sans hauteur : l'image y retombe
     sur son ratio et laisse une bande vide au bas du cadre. */
  .js .pvue__img picture { display: block; height: 100%; }
  .js .pvue__img img {
    width: 100%; height: 100%; object-fit: cover;
    transform: scale(1.055); transition: transform 1.6s var(--ease);
  }
  .js .pvue.is-on .pvue__img img { transform: none; }
  .js .projets__pts { display: flex; margin-top: 22px; }
}

/* Le bandeau sous la piste : les dix chantiers, atteignables au clavier et
   par un moteur. Les vues masquées du défilé sont en visibility: hidden, donc
   hors de l'ordre de tabulation — sans ce bandeau, un seul chantier sur dix
   serait accessible sans souris. Ne pas le retirer en croyant faire du ménage. */
.projets__tous {
  display: flex; flex-wrap: wrap; gap: 10px 26px;
  padding-block: 0 clamp(44px, 6vw, 72px);
}
.projets__lien {
  font-size: 13px; letter-spacing: -.004em; color: var(--mute);
  text-decoration: none; border-bottom: 1px solid transparent;
  padding-bottom: 2px; transition: color .35s var(--ease), border-color .35s var(--ease);
}
.projets__lien:hover, .projets__lien:focus-visible {
  color: var(--ink); border-bottom-color: var(--bronze);
}

/* La liste complète — elle dit tout ce que le défilé montre, et elle reste
   lisible sans JS, au clavier et sur un moteur de recherche. */
.chlist { list-style: none; margin: 0; padding: 0; }
.chlist__i + .chlist__i { border-top: 1px solid var(--rule); }
.chlist__a {
  display: grid; grid-template-columns: 1fr auto auto; align-items: baseline;
  gap: 10px 18px; padding: clamp(15px, 2vw, 21px) 0;
  color: var(--ink); text-decoration: none;
}
.chlist__n { font-size: clamp(18px, 1.9vw, 25px); letter-spacing: -.012em; }
.chlist__c {
  font-size: 11.5px; font-weight: 500; letter-spacing: .2em;
  text-transform: uppercase; color: var(--mute); white-space: nowrap;
}
.chlist__a .arw { transition: transform .5s var(--ease); color: var(--bronze-ink); }
.chlist__a:hover .arw, .chlist__a:focus-visible .arw { transform: translateX(6px); }
.chlist__a:hover .chlist__n { color: var(--bronze-ink); }

/* Le titre d'une page de chantier porte un nom de lieu, parfois long
   (« Cabinet médical, Villers-sous-Saint-Leu ») : la taille de l'accueil le
   casserait en quatre lignes. */
.ch__h1 { font-size: clamp(38px, 5.4vw, 78px); }
