/* ==========================================================================
   Atout Vertical — Socle
   Direction « vertical éditorial ». Dépend de tokens.css, chargé AVANT.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Polices — auto-hébergées, jamais via l'API Google Fonts (RGPD)
   -------------------------------------------------------------------------- */

/* Titres — condensée de signalétique, sous-ensemble capitales : 11,9 Ko */
@font-face {
  font-family: 'Big Shoulders Display';
  src: url('../fonts/big-shoulders-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* Texte — grotesque, sous-ensemble français : 20,2 Ko */
@font-face {
  font-family: 'Archivo';
  src: url('../fonts/archivo-var-fr.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------------------
   2. Réinitialisation
   -------------------------------------------------------------------------- */

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

/* `.no-js` est posée sur <html> dans le balisage et retirée par le script.
   Aucune règle de style ne s'y accroche : c'est un crochet JavaScript qui
   permet aux états dégradés de n'exister que si le script répond. */

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

body {
  margin: 0;
  background: var(--creme);
  background-image:
    radial-gradient(80rem 40rem at 78% -12%, rgba(244,0,0,.045), transparent 60%),
    radial-gradient(60rem 34rem at 0% 4%,   rgba(180,140,90,.05), transparent 62%);
  background-repeat: no-repeat;
  background-attachment: scroll;
  color: var(--encre);
  font-family: var(--texte);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: var(--lh-texte);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;          /* filet : une future section ne casse pas la page */

  /* L'en-tête est en position fixe : il ne pousse plus rien. C'est au contenu
     de lui laisser sa place. Sur la page d'accueil le héros passe DESSOUS,
     d'où le retrait de cette marge quand le script a confirmé la superposition. */
  padding-top: var(--entete-h);
}
html.heros-plein body { padding-top: 0; }

/* Une ancre visée par un lien ne doit pas atterrir sous l'en-tête. */
[id] { scroll-margin-top: calc(var(--entete-h) + var(--sp-4)); }

h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; }
img, picture, svg, video { display: block; max-width: 100%; height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }

/* --------------------------------------------------------------------------
   3. Typographie
   -------------------------------------------------------------------------- */

h1, h2, h3, .geant, .display {
  font-family: var(--titres);
  font-weight: 800;
  line-height: var(--lh-titre);
  letter-spacing: var(--ls-titre);
  text-transform: uppercase;
  color: var(--encre);
  overflow-wrap: break-word;
}

.geant   { font-size: var(--fs-geant);   line-height: var(--lh-geant); }
.display { font-size: var(--fs-display); line-height: var(--lh-geant); }
h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); line-height: var(--lh-inter); }

/* Sous 34px, la condensée perd son intérêt : on repasse en Archivo */
h4, h5, h6 {
  font-family: var(--texte);
  font-weight: 600;
  line-height: var(--lh-inter);
  letter-spacing: 0;
  text-transform: none;
  color: var(--encre);
}
h4 { font-size: var(--fs-h4); }
h5, h6 { font-size: var(--fs-body); }

/* Surtitre : micro-capitales très espacées, précédées d'un tiret rouge */
.oeil {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  font-family: var(--texte);
  font-size: var(--fs-micro);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--ls-oeil);
  color: var(--encre-70);
}
.oeil::before {
  content: "";
  width: 28px; height: 2px;
  background: var(--rouge);
  flex-shrink: 0;
  transform-origin: left center;
}
/* Le tiret se trace en même temps que le bloc apparaît. Comme le rail, mais
   à l'horizontale, et sur une distance courte : c'est une ponctuation. */
html.reveler-actif .reveler .oeil::before,
html.reveler-actif .reveler.oeil::before {
  transform: scaleX(0);
  transition: transform .7s var(--reveler-courbe) .15s;
}
html.reveler-actif .reveler.vu .oeil::before,
html.reveler-actif .reveler.vu.oeil::before { transform: scaleX(1); }

.lead { font-size: var(--fs-lead); line-height: 1.5; color: var(--encre-70); max-width: 46ch; }

a { color: var(--rouge-action); text-decoration-thickness: 1px; text-underline-offset: .22em; }
a:hover { color: var(--rouge-hover); }

:focus-visible { outline: 3px solid var(--rouge); outline-offset: 3px; border-radius: 1px; }

/* --------------------------------------------------------------------------
   3 bis. Détails d'exécution
   --------------------------------------------------------------------------
   Rien d'indispensable ici, et c'est justement le sujet : ce sont les endroits
   où un site montre qu'il a été dessiné plutôt qu'assemblé. */

/* Le surlignage reprend le rouge de la marque plutôt que le bleu du système.
   Le texte reste en encre : mesuré, le crème sur ce rouge ne donne que
   4,12:1 — insuffisant pour du texte courant — quand l'encre atteint 4,50:1.
   C'est la même règle que partout sur les aplats rouges : jamais de clair
   dessus, sauf en très grande taille. */
::selection { background: var(--rouge); color: var(--nuit); }

/* Barre de défilement discrète, accordée au fond. Firefox d'abord, qui n'a
   que deux propriétés, puis WebKit. On ne la masque JAMAIS : une barre
   invisible est une barre qu'on ne sait plus attraper. */
html { scrollbar-color: var(--bordure) var(--creme-2); scrollbar-width: thin; }
@media (pointer: fine) {
  ::-webkit-scrollbar { width: 12px; height: 12px; }
  ::-webkit-scrollbar-track { background: var(--creme-2); }
  ::-webkit-scrollbar-thumb {
    background: var(--bordure);
    border: 3px solid var(--creme-2);
    border-radius: var(--rayon-plein);
  }
  ::-webkit-scrollbar-thumb:hover { background: var(--encre-70); }
}

/* --- Le trait sous un lien de texte -----------------------------------------
   `background-size` et non `border-bottom` : le trait s'épaissit en douceur et
   ne participe pas au calcul de la ligne de base.

   Il est TOUJOURS présent, jamais révélé au survol : dans du texte suivi, un
   lien doit se distinguer sans dépendre de la couleur ni du pointeur. Le
   survol l'épaissit, il ne le fait pas apparaître. */
.prose a:not(.btn),
.pied__liste a,
.fil a,
.article-carte__pied a {
  text-decoration: none;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 1px;
  padding-bottom: 1px;
  transition: background-size var(--t-moyen) var(--sortie),
              color var(--t-court) var(--sortie);
}
@media (hover: hover) and (pointer: fine) {
  .prose a:not(.btn):hover,
  .pied__liste a:hover,
  .fil a:hover,
  .article-carte__pied a:hover { background-size: 100% 2px; }
}

/* --------------------------------------------------------------------------
   4. Mise en page et rail vertical
   -------------------------------------------------------------------------- */

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

.section { padding-block: var(--sp-9); }
@media (min-width: 900px) { .section { padding-block: var(--sp-11); } }

/* Le rail : trait rouge vertical continu, signature de la charte.
   Il court le long du bord gauche du contenu et matérialise la verticalité. */
.raille { position: relative; }
.raille::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: var(--rail);
  border-radius: var(--rayon-plein);
  background: linear-gradient(180deg, var(--rouge), color-mix(in srgb, var(--rouge) 55%, transparent));
}
@media (min-width: 900px) {
  .raille { padding-left: var(--sp-7); }
}
@media (max-width: 899px) {
  .raille { padding-left: var(--sp-5); }
}

.saut-contenu {
  position: absolute; top: 0; left: var(--sp-4); z-index: 100;
  padding: var(--sp-3) var(--sp-5);
  background: var(--nuit); color: var(--creme);
  text-decoration: none; font-weight: 600;
  transform: translateY(-200%);
  transition: transform var(--t-court) var(--sortie);
}
.saut-contenu:focus { transform: translateY(0); color: var(--creme); }

/* --------------------------------------------------------------------------
   5. En-tête — logo centré, navigation à gauche, actions à droite
   -------------------------------------------------------------------------- */

/* POSITION FIXE, ET SURTOUT PAS `sticky`.

   Un en-tête `sticky` occupe sa place dans le flux. Or celui-ci change deux
   fois de hauteur au premier défilement : il quitte la superposition sur le
   héros (+121px de contenu poussé vers le bas) puis son logo se réduit
   (-29px). Le contenu sautait donc deux fois en quelques pixels de défilement,
   et la sentinelle placée dans le héros, déplacée par ce saut, rentrait dans
   le champ puis en ressortait — l'en-tête clignotait.

   En position fixe il ne participe plus au flux : sa hauteur peut changer
   autant qu'elle veut, plus rien ne bouge derrière lui. C'est `--entete-h`,
   mesurée, qui compense son emprise sur le contenu. */
.entete {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 60;
  padding-block: var(--sp-3);
  background: color-mix(in srgb, var(--creme) 90%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 1px 0 color-mix(in srgb, var(--filet) 60%, transparent),
              0 8px 28px rgba(40,28,20,.05);
  transition: background-color var(--t-moyen) var(--sortie),
              box-shadow var(--t-moyen) var(--sortie),
              color var(--t-moyen) var(--sortie),
              transform var(--t-moyen) var(--sortie);
}

/* Escamotage à la descente sur téléphone — classe posée par le script seul.
   Le menu plein écran est un FRÈRE de l'en-tête et non un descendant : c'est
   ce qui permet de translater l'en-tête sans que le `position:fixed` du menu
   ne se recale sur lui. Ne jamais imbriquer le menu dans l'en-tête. */
.entete--escamotee { transform: translate3d(0, -105%, 0); }

/* Trois colonnes égales de part et d'autre : le logo reste optiquement
   centré même si la navigation et les actions n'ont pas la même largeur. */
.entete__interieur {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--sp-4);
  min-height: 68px;
}

.entete__gauche { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.entete__actions { display: flex; align-items: center; justify-content: flex-end;
                   gap: var(--sp-2); min-width: 0; }

/* --- Logo central, nettement plus grand --- */
/* Le logo seul, sans le nom écrit dessous : il est déjà dans le mot-symbole.
   Ce qu'on gagne en hauteur, on le rend en taille de logo. */
.entete__logo {
  display: inline-flex; align-items: center;
  justify-self: center;
  text-decoration: none; color: var(--encre);
  transition: transform var(--t-court) var(--sortie);
}
.entete__logo:hover { transform: translateY(-2px) scale(1.03); }
.entete__logo img {
  width: 76px; height: 76px;
  filter: drop-shadow(0 4px 14px rgba(160,0,0,.24));
  transition: width var(--t-moyen) var(--sortie),
              height var(--t-moyen) var(--sortie),
              filter var(--t-court) var(--sortie);
}
.entete__logo:hover img { filter: drop-shadow(0 6px 18px rgba(160,0,0,.34)); }
@media (min-width: 900px)  { .entete__logo img { width: 96px; height: 96px; } }
@media (max-width: 460px)  { .entete__logo img { width: 60px; height: 60px; } }

/* Une fois défilé, le logo se réduit pour libérer de la hauteur */
.entete--reduite .entete__logo img { width: 54px; height: 54px; }
@media (min-width: 900px) { .entete--reduite .entete__logo img { width: 64px; height: 64px; } }

/* --- Navigation de bureau --- */
.nav-bureau { display: none; }
@media (min-width: 1000px) {
  .nav-bureau { display: flex; align-items: center; gap: var(--sp-5); }
  .burger { display: none !important; }
}
.nav-bureau a {
  position: relative;
  font-size: var(--fs-small); font-weight: 600;
  color: var(--encre); text-decoration: none;
  padding-block: var(--sp-2);
  transition: color var(--t-court) var(--sortie);
}
.nav-bureau a::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 100%; height: 2px; border-radius: var(--rayon-plein);
  background: var(--rouge);
  transform: scaleX(0); transform-origin: right;
  transition: transform var(--t-moyen) var(--sortie);
}
.nav-bureau a:hover::after,
.nav-bureau a[aria-current="page"]::after { transform: scaleX(1); transform-origin: left; }
.nav-bureau a[aria-current="page"] { color: var(--rouge-action); }

/* --- Bouton menu (mobile) : deux barres qui se croisent --- */
.burger {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--cible-tactile); height: var(--cible-tactile);
  padding: 0; background: none; border: 0; cursor: pointer;
  color: var(--encre);
}
.burger__boite { position: relative; width: 24px; height: 14px; }
.burger__trait {
  position: absolute; left: 0; width: 100%; height: 2px;
  border-radius: var(--rayon-plein); background: currentColor;
  transition: transform var(--t-moyen) var(--sortie),
              opacity .18s linear;
}
.burger__trait:nth-child(1) { top: 0; }
.burger__trait:nth-child(2) { top: 6px; }
.burger__trait:nth-child(3) { top: 12px; }
.burger[aria-expanded="true"] .burger__trait:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.burger[aria-expanded="true"] .burger__trait:nth-child(2) { opacity: 0; transform: scaleX(.2); }
.burger[aria-expanded="true"] .burger__trait:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

.entete__tel {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: var(--cible-tactile);
  padding-inline: var(--sp-2);
  font-weight: 600; font-size: var(--fs-small);
  color: var(--encre); text-decoration: none; white-space: nowrap;
  flex-shrink: 0;
  transition: color var(--t-court) var(--sortie);
}
.entete__tel:hover { color: var(--rouge-action); }
.entete__tel svg { width: 18px; height: 18px; fill: currentColor; flex-shrink: 0; }

@media (max-width: 859px) {
  .entete__actions .btn__long  { display: none; }
  .entete__actions .btn__court { display: inline; }
  .entete__actions .btn { padding-inline: var(--sp-4); }
}
@media (max-width: 700px) {
  .entete__tel span { position: absolute; width: 1px; height: 1px;
                      overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
}


/* --------------------------------------------------------------------------
   5 bis. Menu plein écran
   -------------------------------------------------------------------------- */

.menu-plein {
  position: fixed; inset: 0; z-index: 55;
  display: flex; flex-direction: column; justify-content: center;
  padding: var(--sp-10) var(--gouttiere) var(--sp-8);
  background: color-mix(in srgb, var(--nuit) 96%, transparent);
  backdrop-filter: blur(22px);
  -webkit-backdrop-filter: blur(22px);
  visibility: hidden;
  opacity: 0;
  transition: opacity var(--t-moyen) var(--sortie),
              visibility 0s linear var(--t-moyen);
}
.menu-plein.ouvert { visibility: visible; opacity: 1; transition-delay: 0s; }
body.menu-ouvert { overflow: hidden; }

.menu-plein__liste { list-style: none; padding: 0; margin: 0; }
.menu-plein__liste li { overflow: hidden; }
.menu-plein__liste a {
  display: block;
  padding-block: var(--sp-3);
  font-family: var(--titres); font-weight: 800;
  font-size: clamp(2rem, 1.4rem + 3vw, 3.5rem);
  line-height: 1.05; text-transform: uppercase; letter-spacing: -.01em;
  color: var(--creme); text-decoration: none;
  transform: translateY(110%);
  transition: transform .6s var(--sortie), color var(--t-court) var(--sortie);
}
.menu-plein.ouvert .menu-plein__liste a { transform: translateY(0); }
.menu-plein__liste li:nth-child(1) a { transition-delay: .06s; }
.menu-plein__liste li:nth-child(2) a { transition-delay: .12s; }
.menu-plein__liste li:nth-child(3) a { transition-delay: .18s; }
.menu-plein__liste li:nth-child(4) a { transition-delay: .24s; }
.menu-plein__liste li:nth-child(5) a { transition-delay: .30s; }
.menu-plein__liste a:hover { color: var(--rouge); }
.menu-plein__liste a[aria-current="page"] { color: var(--rouge); }

.menu-plein__pied {
  margin-top: var(--sp-8); padding-top: var(--sp-5);
  border-top: 1px solid rgba(251,249,246,.16);
  display: flex; flex-wrap: wrap; gap: var(--sp-5);
  align-items: center; justify-content: space-between;
  opacity: 0; transform: translateY(16px);
  transition: opacity .5s var(--sortie) .3s, transform .5s var(--sortie) .3s;
}
.menu-plein.ouvert .menu-plein__pied { opacity: 1; transform: none; }
.menu-plein__pied a { color: var(--creme); font-weight: 700; font-size: var(--fs-lead); }

/* En superposition sur le héros : fond effacé, texte clair.
   État appliqué UNIQUEMENT par le script. Sans lui, l'en-tête reste solide
   et lisible — jamais de texte clair sur fond crème.

   Seules l'apparence change ici : ni `position`, ni hauteur, ni quoi que ce
   soit qui déplace le contenu. C'est ce qui rend la bascule silencieuse. */
html.heros-plein .entete--sur-heros {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none;
}
html.heros-plein .entete--sur-heros .entete__logo,
html.heros-plein .entete--sur-heros .entete__tel,
html.heros-plein .entete--sur-heros .nav-bureau a,
html.heros-plein .entete--sur-heros .burger { color: var(--creme); }
html.heros-plein .entete--sur-heros .entete__tel:hover,
html.heros-plein .entete--sur-heros .nav-bureau a:hover { color: var(--rouge); }
html.heros-plein .entete--sur-heros .nav-bureau a[aria-current="page"] { color: var(--rouge); }
/* Filet : en superposition l'en-tête est en position absolue dans le héros,
   l'escamoter n'aurait aucun sens. Le script s'en garde déjà, la règle rend
   la faute impossible. */
html.heros-plein .entete--sur-heros.entete--escamotee { transform: none; }

/* --------------------------------------------------------------------------
   6. Boutons — pastille imbriquée pour la flèche
   -------------------------------------------------------------------------- */

/* Le bouton porte deux libellés, long et court. Sans cette règle, les DEUX
   s'affichent : « Demander un devis Devis ». Elle vit ici et non dans la
   section en-tête, pour ne pas disparaître si l'en-tête est réécrit. */
.btn__court { display: none; }

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--sp-3);
  min-height: var(--cible-tactile);
  padding: var(--sp-3) var(--sp-5);
  font-family: var(--texte);
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: .01em;
  line-height: 1;
  text-align: center; text-decoration: none;
  border: 1.5px solid transparent;
  border-radius: var(--rayon);
  box-shadow: var(--ombre-douce);
  cursor: pointer;
  transition: background-color var(--t-court) var(--sortie),
              border-color var(--t-court) var(--sortie),
              color var(--t-court) var(--sortie),
              transform var(--t-court) var(--sortie);
}
.btn:active { transform: scale(.985); }

.btn--principal {
  background: var(--rouge-action); color: #fff;
  box-shadow: 0 2px 6px rgba(160,0,0,.22), 0 10px 24px rgba(160,0,0,.18);
}
.btn--principal:hover {
  background: var(--rouge-hover); color: #fff;
  box-shadow: 0 3px 10px rgba(160,0,0,.28), 0 16px 34px rgba(160,0,0,.22);
  transform: translateY(-1px);
}

.btn--secondaire {
  background: var(--papier); color: var(--encre);
  border-color: color-mix(in srgb, var(--encre) 22%, transparent);
}
.btn--secondaire:hover { background: var(--encre); color: var(--creme); }

/* Flèche logée dans sa propre pastille, collée au bord intérieur du bouton */
.btn__fleche {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px;
  margin-right: calc(var(--sp-3) * -1 + 2px);
  border-radius: 50%;
  background: rgba(255,255,255,.18);
  font-size: .8em;
  transition: transform var(--t-court) var(--sortie),
              background-color var(--t-court) var(--sortie);
}
.btn--secondaire .btn__fleche { background: rgba(20,17,15,.08); }
/* La flèche part vers son coin — c'est la direction du lien, pas un
   frétillement décoratif. Le décalage est franc (4px) : en dessous, le geste
   se devine sans se voir. */
.btn:hover .btn__fleche { transform: translate(4px, -4px); background: rgba(255,255,255,.32); }
.btn:active .btn__fleche { transform: translate(1px, -1px); }

.btn--secondaire:hover .btn__fleche { background: rgba(255,255,255,.2); }

/* Sur aplat rouge : le rouge sur rouge est illisible, on inverse */
.plan--rouge .btn--principal { background: var(--nuit); color: var(--creme); }
.plan--rouge .btn--principal:hover { background: #000; }
.plan--rouge .btn--secondaire { color: var(--nuit); border-color: var(--nuit); }
.plan--rouge .btn--secondaire:hover { background: var(--nuit); color: var(--creme); }

.plan--nuit .btn--secondaire { color: var(--creme); border-color: rgba(251,249,246,.4); }
.plan--nuit .btn--secondaire:hover { background: var(--creme); color: var(--nuit); }

/* --------------------------------------------------------------------------
   7. Aplats
   -------------------------------------------------------------------------- */

.plan--rouge {
  background-color: var(--rouge);
  color: var(--nuit);
  background-image:
    linear-gradient(rgba(0,0,0,.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0,0,0,.045) 1px, transparent 1px);
  background-size: 32px 32px, 32px 32px, 100% 100%;
  background-image:
    linear-gradient(rgba(0,0,0,.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0,0,0,.045) 1px, transparent 1px),
    radial-gradient(60rem 40rem at 12% 0%, rgba(255,255,255,.16), transparent 62%);
}
.plan--rouge h1, .plan--rouge h2, .plan--rouge h3 { color: var(--nuit); }
.plan--rouge .oeil { color: rgba(14,12,11,.72); }
.plan--rouge .oeil::before { background: var(--nuit); }

.plan--nuit {
  position: relative;
  background: var(--nuit);
  background-image:
    radial-gradient(70rem 36rem at 88% 0%, rgba(244,0,0,.16), transparent 58%),
    linear-gradient(168deg, #16120F 0%, var(--nuit) 55%);
  color: var(--creme);
}
.plan--nuit h1, .plan--nuit h2, .plan--nuit h3 { color: var(--creme); }
.plan--nuit .lead { color: rgba(251,249,246,.76); }
.plan--nuit .oeil { color: rgba(251,249,246,.66); }

.plan--creme2 { background: var(--creme-2); }

/* --------------------------------------------------------------------------
   8. Cadre imbriqué — l'image ne repose jamais à plat sur le fond
   -------------------------------------------------------------------------- */

.cadre {
  padding: 8px;
  background: var(--papier);
  border: 1px solid color-mix(in srgb, var(--filet) 70%, transparent);
  border-radius: var(--rayon-carte);
  box-shadow: var(--ombre-douce);
  transition: box-shadow var(--t-moyen) var(--sortie),
              transform var(--t-moyen) var(--sortie);
}
.plan--nuit .cadre { background: var(--nuit-80); border-color: rgba(251,249,246,.14); }
.plan--rouge .cadre { background: rgba(255,255,255,.16); border-color: rgba(14,12,11,.16); }

.cadre__interieur {
  overflow: hidden;
  border-radius: var(--rayon-media);   /* concentrique avec la coque */
  background: var(--creme-2);
}
.cadre__interieur img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--t-long) var(--sortie);
}
.portrait { aspect-ratio: 3/4; }
.paysage  { aspect-ratio: 16/9; }

/* --------------------------------------------------------------------------
   9. Héros plein écran — la photo occupe tout l'écran, le texte est posé dessus
   -------------------------------------------------------------------------- */

.heros {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: flex-end;
  /* dvh et non vh : sur iOS Safari, vh inclut la barre d'adresse et la page
     saute au premier défilement. On retire la hauteur de l'en-tête tant que le
     script n'a pas confirmé la superposition — voir --entete-h. */
  min-height: calc(100dvh - var(--entete-h));
  padding-block: var(--sp-9);
  overflow: hidden;
}
@supports not (height: 100dvh) {
  .heros { min-height: calc(100vh - var(--entete-h)); }
}

/* Écran peu haut (portable 13 pouces, paysage sur téléphone) : on laisse le
   contenu commander la hauteur plutôt que d'imposer un plein écran qui coupe. */
@media (max-height: 760px) and (min-width: 760px) {
  html.heros-plein .heros { min-height: 0; padding-block: var(--sp-10) var(--sp-9); }
  .heros { min-height: 0; }
}

/* En superposition, le héros reprend toute la hauteur de l'écran */
html.heros-plein .heros { min-height: 100dvh; padding-top: var(--sp-11); }

.heros__media { position: absolute; inset: 0; z-index: -1; }
/* <picture> est un élément inline sans hauteur propre : sans display:block et
   height:100%, le « height:100% » de l'image se calcule contre rien et la photo
   garde ses proportions au lieu de couvrir le bloc. */
.heros__media picture { display: block; width: 100%; height: 100%; }
.heros__media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 42%; }

/* Double voile : vertical pour lester le bas, latéral pour asseoir le texte.
   Garantit la lisibilité quelle que soit la photo mise à la place. */
.heros__media::after {
  content: "";
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(9,7,6,.78) 0%, rgba(9,7,6,.34) 30%,
                            rgba(9,7,6,.62) 68%, rgba(9,7,6,.93) 100%),
    linear-gradient(96deg, rgba(9,7,6,.70) 0%, rgba(9,7,6,.20) 58%, transparent 78%);
}

.heros__contenu { position: relative; width: 100%; color: var(--creme); }
.heros__contenu .oeil { color: rgba(251,249,246,.82); }
.heros__contenu h1 { color: var(--creme); }
.heros__titre { margin-top: var(--sp-5); }
.heros__titre .rouge { color: var(--rouge); display: block; }
.heros__lead {
  margin-top: var(--sp-6);
  color: rgba(251,249,246,.9);
  max-width: 44ch;
}
.heros__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-6); }
.heros .btn--secondaire {
  background: rgba(255,255,255,.1);
  color: var(--creme);
  border-color: rgba(255,255,255,.42);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.heros .btn--secondaire:hover { background: var(--creme); color: var(--nuit); border-color: var(--creme); }
.heros .btn--secondaire .btn__fleche { background: rgba(255,255,255,.18); }

/* Repères chiffrés posés en bas du héros */
.heros__reperes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-3);
  margin-top: var(--sp-7);
  max-width: 30rem;
}
@media (min-width: 700px) {
  .heros__reperes { grid-template-columns: repeat(4, minmax(0, 1fr)); max-width: 52rem; }
}
.heros__reperes > div {
  min-width: 0;
  padding: var(--sp-3) var(--sp-4);
  background: rgba(255,255,255,.08);
  border: 1px solid rgba(255,255,255,.14);
  border-radius: var(--rayon-media);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.heros__reperes .repere__val { font-size: clamp(1.5rem, 1.3rem + 0.9vw, 2.125rem); }
.heros__reperes .repere__lib { color: rgba(251,249,246,.8); }

/* Invite à faire défiler */
.heros__defiler {
  position: absolute;
  right: var(--gouttiere); bottom: var(--sp-7);
  display: none;
  writing-mode: vertical-rl;
  font-size: var(--fs-micro); font-weight: 600;
  text-transform: uppercase; letter-spacing: var(--ls-oeil);
  color: rgba(251,249,246,.6);
  text-decoration: none;
}
@media (min-width: 1100px) { .heros__defiler { display: block; } }
.heros__defiler::after {
  content: ""; display: block;
  width: 1px; height: 56px; margin: var(--sp-3) auto 0;
  background: linear-gradient(180deg, var(--rouge), transparent);
}

.reperes {
  display: grid;
  /* minmax(0,1fr) et non 1fr : sans cela, un mot long comme « ÉCHAFAUDAGE »
     impose sa largeur minimale à la colonne et fait déborder la page. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-4);
  max-width: 34rem;          /* évite deux chiffres perdus aux deux bouts */
}
.reperes > div {
  min-width: 0;
  padding: var(--sp-5);
  background: var(--papier);
  border-radius: var(--rayon-media);
  box-shadow: var(--ombre-douce);
}
.plan--nuit .reperes > div {
  background: rgba(255,255,255,.055);
  border: 1px solid rgba(255,255,255,.09);
  box-shadow: none;
}
.repere__val {
  font-family: var(--titres); font-weight: 800;
  font-size: clamp(2.25rem, 1.8rem + 1.9vw, 3.25rem);
  line-height: .9; color: var(--rouge);
}
.repere__lib {
  margin-top: var(--sp-2);
  overflow-wrap: break-word;
  hyphens: auto;
  font-size: var(--fs-micro); font-weight: 600;
  text-transform: uppercase; letter-spacing: .1em;
  color: var(--encre-70);
}
.plan--nuit .repere__lib { color: rgba(251,249,246,.7); }

/* --------------------------------------------------------------------------
   10. Métiers — lignes éditoriales numérotées, pas une grille de cartes
   -------------------------------------------------------------------------- */

.metiers { margin-top: var(--sp-9); display: flex; flex-direction: column; }

.metier {
  display: grid; gap: var(--sp-5);
  grid-template-columns: 1fr;
  padding-block: var(--sp-7);
  border-top: 1px solid var(--filet);
}
.metier:last-child { border-bottom: 1px solid var(--filet); }

@media (min-width: 820px) {
  .metier {
    /* 22rem et non 15rem : à 240px la photo était une vignette posée à côté
       du texte. À 352px elle pèse dans la composition et le chantier se voit. */
    grid-template-columns: 6rem minmax(0, 1fr) minmax(0, 22rem);
    gap: var(--sp-7);
    align-items: start;
    padding-block: var(--sp-8);
  }
  /* La photo agrandie fait 470px de haut, le texte environ 230 : aligné en
     haut, il laisserait un vide de la moitié de la ligne. Le texte se centre
     sur la photo, le numéro reste en haut — c'est un repère, pas du contenu. */
  .metier__corps { align-self: center; }
  /* Une ligne sur deux décale l'image : le rythme cesse d'être mécanique.
     Le modificateur existe en plus de nth-child car sur /services chaque
     métier occupe sa propre section : il est fils unique, et nth-child(even)
     n'y sélectionne jamais rien. */
  .metier:nth-child(even),
  .metier--inverse { grid-template-columns: 6rem minmax(0, 22rem) minmax(0, 1fr); }
  .metier:nth-child(even) .metier__media,
  .metier--inverse .metier__media { order: 2; }
  .metier:nth-child(even) .metier__corps,
  .metier--inverse .metier__corps { order: 3; }
}

.metier__num {
  font-family: var(--titres); font-weight: 800;
  font-size: clamp(2rem, 1.6rem + 1.6vw, 3rem);
  line-height: .9; color: var(--rouge);
}
.metier__titre { margin-bottom: var(--sp-4); }
.metier__titre a { color: var(--encre); text-decoration: none; }
.metier__titre a:hover { color: var(--rouge-action); }
.metier__corps p { color: var(--encre-70); max-width: 54ch; }

.metier__liste {
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-5);
  margin-top: var(--sp-4); padding: 0; list-style: none;
  font-size: var(--fs-small); color: var(--encre-70);
}
.metier__liste li { display: flex; align-items: center; gap: var(--sp-2); }
.metier__liste li::before { content: "·"; color: var(--rouge); font-weight: 700; }

/* Au survol, toute la ligne se signale : un rail rouge se déploie dans la
   marge, le numéro avance, la photo se soulève. Trois signaux pour un seul
   geste — mais tous partent du même bord, donc la ligne reste une ligne. */
.metier { position: relative; }
.metier::before {
  content: "";
  position: absolute;
  left: calc(-1 * var(--sp-4)); top: var(--sp-7); bottom: var(--sp-7);
  width: var(--rail);
  border-radius: var(--rayon-plein);
  background: var(--rouge);
  transform: scaleY(0);
  transform-origin: center;
  transition: transform var(--t-moyen) var(--sortie);
}
.metier__num { transition: transform var(--t-moyen) var(--sortie); }

@media (hover: hover) and (pointer: fine) {
  .metier:hover::before { transform: scaleY(1); }
  .metier:hover .metier__num { transform: translateX(6px); }
  .metier:hover .cadre { box-shadow: var(--ombre-relief); transform: translateY(-3px); }
  .metier:hover .cadre__interieur img { transform: scale(1.04); }
}
/* Sous 1200px la marge extérieure disparaît : le rail se logerait hors écran. */
@media (max-width: 1199px) { .metier::before { display: none; } }

/* --------------------------------------------------------------------------
   11. Méthode — escalier vertical
   -------------------------------------------------------------------------- */

.etapes {
  margin-top: var(--sp-9);
  display: grid; gap: var(--sp-7);
  grid-template-columns: 1fr;
  list-style: none; padding: 0;
}
@media (min-width: 820px) { .etapes { grid-template-columns: repeat(3, 1fr); gap: var(--sp-8); } }
/* Quatre engagements : deux par deux plutôt que trois plus un orphelin. */
@media (min-width: 820px) { .etapes--quatre { grid-template-columns: repeat(2, minmax(0,1fr)) !important; } }

.etape {
  position: relative;
  padding: var(--sp-6);
  background: var(--papier);
  border-radius: var(--rayon-carte);
  box-shadow: var(--ombre-douce);
}
.etape::before {
  content: "";
  position: absolute; left: var(--sp-6); top: 0;
  width: 46px; height: var(--rail);
  border-radius: var(--rayon-plein);
  background: var(--rouge);
}
.plan--creme2 .etape { background: var(--papier); }
.etape__num {
  display: block;
  font-family: var(--titres); font-weight: 800;
  font-size: clamp(2rem, 1.6rem + 1.6vw, 2.75rem);
  line-height: .9; color: var(--rouge);
}
.etape__titre { margin-top: var(--sp-4); }
.etape p { margin-top: var(--sp-3); color: var(--encre-70); }
.plan--nuit .etape p { color: rgba(251,249,246,.76); }

/* --------------------------------------------------------------------------
   12. Galerie — portraits, hauteurs alternées
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   12. Galerie — mosaïque de chantiers
   --------------------------------------------------------------------------
   Une vraie mosaïque et non une rangée de vignettes : une vedette en paysage
   qui occupe deux colonnes, cinq portraits qui l'entourent. Les cellules
   commandent les proportions, les photos s'y recadrent — c'est ce qui permet
   de mêler paysage et portrait sans trouer la grille.

   `grid-auto-flow: dense` comble les creux : si un portrait ne tient pas
   après une vedette, la grille remonte le suivant au lieu de laisser un vide. */

.galerie {
  margin-top: var(--sp-9);
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-auto-rows: 7rem;
  grid-auto-flow: dense;
  list-style: none;
  padding: 0;
}
@media (min-width: 700px) {
  .galerie { grid-template-columns: repeat(4, minmax(0, 1fr)); grid-auto-rows: 8rem; gap: var(--sp-5); }
}
@media (min-width: 1100px) {
  /* Quatre colonnes et non cinq : la vedette occupe deux unités, les six
     portraits une chacune — huit au total, soit exactement deux bandes
     pleines. Sur cinq colonnes il restait un trou de trois cellules. */
  .galerie { grid-template-columns: repeat(4, minmax(0, 1fr)); grid-auto-rows: 9.5rem; }
}

.galerie__item { margin: 0; min-width: 0; }

/* Portrait : une colonne, trois rangées */
.galerie__item { grid-column: span 1; grid-row: span 3; }
/* Vedette en paysage : deux colonnes, deux rangées */
.galerie__item--vedette { grid-column: span 2; grid-row: span 3; }
@media (max-width: 699px) {
  .galerie__item          { grid-row: span 3; }
  .galerie__item--vedette { grid-column: span 2; grid-row: span 2; }
}

/* La photo remplit sa cellule quelles que soient ses proportions d'origine. */
.galerie__item .cadre,
.galerie__item .cadre__interieur { height: 100%; }
.galerie__item .cadre__interieur { aspect-ratio: auto; }
.galerie__item .cadre__interieur img { height: 100%; width: 100%; object-fit: cover; }

/* --- Le bouton d'agrandissement ---------------------------------------------
   Chaque vignette EST un bouton : elle porte son propre libellé accessible,
   elle est atteignable au clavier, et elle indique ce qu'elle va faire.
   Un `<div>` cliquable n'aurait rien de tout cela. */
.galerie__ouvrir {
  display: block;
  width: 100%; height: 100%;
  padding: 0; border: 0; background: none;
  font: inherit; color: inherit; text-align: left;
  cursor: zoom-in;
}
.galerie__ouvrir:focus-visible { outline: 3px solid var(--rouge); outline-offset: 4px; border-radius: var(--rayon-carte); }

/* --- Légende ----------------------------------------------------------------
   Posée sur la photo, sur un voile dégradé. Elle décrit ce qu'on voit et rien
   de plus : ni commune, ni client, ni date — aucune de ces informations n'a
   été fournie, et il n'est pas question de les inventer. */
.galerie__legende {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: var(--sp-6) var(--sp-4) var(--sp-3);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: #fff;
  background: linear-gradient(180deg, transparent, rgba(9,7,6,.82));
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--t-moyen) var(--sortie), transform var(--t-moyen) var(--sortie);
}
.galerie__item .cadre__interieur { position: relative; }

@media (hover: hover) and (pointer: fine) {
  .galerie__item:hover .cadre { box-shadow: var(--ombre-relief); transform: translateY(-4px); }
  .galerie__item:hover .cadre__interieur img { transform: scale(1.06); }
  .galerie__item:hover .galerie__legende,
  .galerie__ouvrir:focus-visible .galerie__legende { opacity: 1; transform: none; }
}
/* Sans survol possible, la légende est simplement toujours là. */
@media (hover: none) {
  .galerie__legende { opacity: 1; transform: none; }
}

/* --- Galerie complète : plus de colonnes, cellules plus petites ------------- */
@media (min-width: 1100px) {
  .galerie--complete { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* --------------------------------------------------------------------------
   12 quater. Index du blog
   --------------------------------------------------------------------------
   La carte entière est un lien : c'est un seul point d'entrée pour la souris
   comme pour le clavier, plutôt qu'une image cliquable doublée d'un titre
   cliquable qui mènent au même endroit — deux tabulations pour rien, et un
   lecteur d'écran qui annonce deux fois la même destination. */

.articles {
  display: grid;
  gap: var(--sp-7);
  grid-template-columns: 1fr;
  list-style: none;
  padding: 0;
}
@media (min-width: 700px)  { .articles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .articles { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-8); } }

.article-carte { min-width: 0; }
.article-carte__lien {
  display: block;
  color: inherit;
  text-decoration: none;
}
.article-carte .cadre__interieur { aspect-ratio: 16 / 9; }

.article-carte__titre {
  margin-top: var(--sp-3);
  font-size: var(--fs-h3);
  line-height: var(--lh-inter);
  transition: color var(--t-court) var(--sortie);
}
.article-carte__resume { margin-top: var(--sp-3); color: var(--encre-70); }
.article-carte__pied {
  margin-top: var(--sp-4);
  display: flex; flex-wrap: wrap; gap: var(--sp-2);
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--encre-70);
}

@media (hover: hover) and (pointer: fine) {
  .article-carte__lien:hover .article-carte__titre { color: var(--rouge-action); }
  .article-carte__lien:hover .cadre { box-shadow: var(--ombre-relief); transform: translateY(-4px); }
  .article-carte__lien:hover .cadre__interieur img { transform: scale(1.05); }
}
.article-carte__lien:focus-visible {
  outline: 3px solid var(--rouge);
  outline-offset: 6px;
  border-radius: var(--rayon-carte);
}

/* Date de publication d'un article */
.article__date {
  margin-top: var(--sp-5);
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--encre-70);
}

/* --------------------------------------------------------------------------
   12 ter. Filtre par métier
   --------------------------------------------------------------------------
   Masqué par défaut : le script pose `filtres-actifs` sur <html> une fois qu'il
   a pris la main. Sans JavaScript, aucun bouton inerte n'est proposé, et les
   vingt-et-une photographies restent toutes affichées — on perd le tri, jamais
   le contenu. */

.filtres { display: none; }
html.filtres-actifs .filtres {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-bottom: var(--sp-6);
}

.filtre {
  min-height: var(--cible-tactile);
  padding: var(--sp-2) var(--sp-5);
  font-family: var(--texte);
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--encre);
  background: var(--papier);
  border: 1.5px solid color-mix(in srgb, var(--encre) 18%, transparent);
  border-radius: var(--rayon-plein);
  cursor: pointer;
  transition: background-color var(--t-court) var(--sortie),
              border-color var(--t-court) var(--sortie),
              color var(--t-court) var(--sortie);
}
.filtre { position: relative; overflow: hidden; isolation: isolate; }
.filtre::before {
  content: "";
  position: absolute; inset: 0;
  background: var(--encre);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--t-moyen) var(--sortie);
  z-index: -1;
}
.filtre[aria-pressed="true"]::before { transform: scaleX(1); }
.filtre:hover { border-color: var(--encre); }
.filtre[aria-pressed="true"] {
  /* Pas de `background` ici : c'est ::before qui le pose, en s'animant. */
  border-color: var(--encre);
  color: var(--creme);
}
.filtre:focus-visible { outline: 3px solid var(--rouge); outline-offset: 3px; }

.filtres__compte { display: none; }
html.filtres-actifs .filtres__compte {
  display: block;
  margin-bottom: var(--sp-5);
  font-size: var(--fs-small);
  color: var(--encre-70);
}

/* Une vignette écartée par le filtre sort du flux ET du parcours clavier.
   `hidden` seul serait annulé par `display:grid` sur la grille parente ;
   l'attribut est donc doublé d'une règle explicite. */
.galerie__item[hidden] { display: none; }

/* --------------------------------------------------------------------------
   12 bis. Visionneuse
   --------------------------------------------------------------------------
   Un <dialog> natif : le navigateur gère le rendu au-dessus de tout, le
   verrouillage du focus à l'intérieur, la fermeture par Échap et le retour
   du focus au bouton d'origine. Rien de tout cela n'est à réécrire. */

.visionneuse {
  width: min(96vw, 1200px);
  max-width: none;
  max-height: 94dvh;
  padding: 0;
  border: 0;
  background: none;
  overflow: visible;
}
.visionneuse::backdrop {
  background: rgba(9, 7, 6, .92);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.visionneuse__figure { margin: 0; display: grid; gap: var(--sp-4); justify-items: center; }
.visionneuse__image {
  max-width: 100%;
  max-height: 78dvh;
  width: auto; height: auto;
  border-radius: var(--rayon-media);
  box-shadow: var(--ombre-nuit);
}
.visionneuse__legende {
  color: rgba(251,249,246,.86);
  font-size: var(--fs-small);
  text-align: center;
  max-width: 60ch;
}
.visionneuse__fermer {
  position: absolute; top: calc(-1 * var(--sp-7)); right: 0;
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--cible-tactile); height: var(--cible-tactile);
  border: 0; border-radius: var(--rayon-plein);
  background: rgba(255,255,255,.12); color: #fff;
  font-size: 1.5rem; line-height: 1; cursor: pointer;
}
.visionneuse__fermer:hover { background: var(--rouge); }
.visionneuse__fermer:focus-visible { outline: 3px solid var(--rouge); outline-offset: 3px; }

/* Le pas à pas d'une image à l'autre */
.visionneuse__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--cible-tactile); height: var(--cible-tactile);
  border: 0; border-radius: var(--rayon-plein);
  background: rgba(255,255,255,.12); color: #fff;
  font-size: 1.4rem; line-height: 1; cursor: pointer;
}
.visionneuse__nav:hover { background: var(--rouge); }
.visionneuse__nav:focus-visible { outline: 3px solid var(--rouge); outline-offset: 3px; }
.visionneuse__nav--prec { left: calc(-1 * var(--sp-7)); }
.visionneuse__nav--suiv { right: calc(-1 * var(--sp-7)); }
@media (max-width: 899px) {
  .visionneuse__nav--prec { left: var(--sp-2); }
  .visionneuse__nav--suiv { right: var(--sp-2); }
  .visionneuse__fermer { top: calc(-1 * var(--sp-6)); right: var(--sp-2); }
}

@media (prefers-reduced-motion: no-preference) {
  .visionneuse[open] { animation: visionneuse-entree .3s var(--sortie); }
  @keyframes visionneuse-entree {
    from { opacity: 0; transform: scale(.97); }
  }
}

.centre { margin-top: var(--sp-9); text-align: center; }
@media (min-width: 1100px) { .centre { margin-top: var(--sp-11); } }

/* --------------------------------------------------------------------------
   13. Appel à l'action
   -------------------------------------------------------------------------- */

.cta { display: grid; gap: var(--sp-6); grid-template-columns: 1fr; }
@media (min-width: 900px) {
  .cta { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); align-items: end; gap: var(--sp-9); }
}
.cta__texte { font-size: var(--fs-lead); line-height: 1.5; max-width: 42ch; }
.cta__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); }

/* --------------------------------------------------------------------------
   14. En-tête de page intérieure
   -------------------------------------------------------------------------- */

.page-entete { padding-block: var(--sp-8) var(--sp-7); }
@media (min-width: 900px) { .page-entete { padding-block: var(--sp-9) var(--sp-8); } }

.fil {
  display: flex; flex-wrap: wrap; gap: var(--sp-2);
  margin-bottom: var(--sp-5); padding: 0; list-style: none;
  font-size: var(--fs-micro); text-transform: uppercase; letter-spacing: .1em;
  color: var(--encre-70);
}
.fil li { display: flex; align-items: center; gap: var(--sp-2); }
.fil li + li::before { content: "/"; color: var(--rouge); }
.fil a { color: var(--encre-70); text-decoration: none; }
.fil a:hover { color: var(--rouge-action); }
.fil [aria-current="page"] { color: var(--encre); font-weight: 600; }

.page-entete__filet { width: 120px; height: var(--rail); margin-top: var(--sp-5); background: var(--rouge); }

/* --------------------------------------------------------------------------
   15. Contenu rédactionnel — pages légales et articles
   -------------------------------------------------------------------------- */

.prose { max-width: 68ch; overflow-wrap: break-word; }
.prose > * + * { margin-top: var(--sp-5); }
.prose h2 { margin-top: var(--sp-9); font-size: var(--fs-h3); }
.prose h3 { margin-top: var(--sp-7); font-size: var(--fs-h4); font-family: var(--texte);
            font-weight: 700; text-transform: none; letter-spacing: 0; }
.prose h2 + *, .prose h3 + * { margin-top: var(--sp-4); }
.prose ul, .prose ol { padding-left: var(--sp-5); }
.prose li + li { margin-top: var(--sp-2); }
.prose li::marker { color: var(--rouge); }
.prose dl > div + div { margin-top: var(--sp-4); }
.prose dt { font-weight: 700; }
.prose dd { margin: 0; color: var(--encre-70); }
.prose strong { font-weight: 700; }

.encadre {
  padding: var(--sp-5) var(--sp-6);
  background: var(--creme-2);
  border-left: var(--rail) solid var(--rouge);
  border-radius: var(--rayon) var(--rayon-media) var(--rayon-media) var(--rayon);
}
.encadre > * + * { margin-top: var(--sp-3); }

.tableau-enveloppe { overflow-x: auto; }
.tableau { width: 100%; border-collapse: collapse; font-size: var(--fs-small); }
.tableau th, .tableau td {
  padding: var(--sp-3); text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--filet);
}
.tableau th { font-weight: 700; border-bottom-color: var(--bordure); }
.tableau tbody tr:last-child td { border-bottom: none; }

.maj { font-size: var(--fs-small); color: var(--encre-70); }

/* --------------------------------------------------------------------------
   16. Emplacements en attente du client — volontairement criards
   -------------------------------------------------------------------------- */

.a-completer {
  display: inline;
  padding: .14em .5em;
  background: #FFF2A8;
  color: #5C4400;
  font-weight: 700;
  border: 1px dashed #A88300;
  border-radius: 6px;
}
.a-completer::before { content: "⚠ "; }

/* --------------------------------------------------------------------------
   17. Formulaire
   -------------------------------------------------------------------------- */

.contact { display: grid; gap: var(--sp-8); grid-template-columns: 1fr; }
@media (min-width: 900px) {
  .contact { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: var(--sp-9); }
}

.contact__form { min-width: 0; }

.form__obligatoire {
  margin: var(--sp-7) 0 var(--sp-5);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--filet);
  font-size: var(--fs-small); color: var(--encre-70);
}
.requis { color: var(--rouge-action); font-weight: 700; text-decoration: none; border: 0; }

.champs { display: grid; gap: var(--sp-5); grid-template-columns: 1fr; }
@media (min-width: 600px) { .champs { grid-template-columns: repeat(2, 1fr); } }
.champ--large { grid-column: 1 / -1; }

.champ { display: flex; flex-direction: column; gap: var(--sp-2); margin: 0; }
.champ label {
  font-size: var(--fs-micro); font-weight: 700;
  text-transform: uppercase; letter-spacing: .09em; color: var(--encre-70);
}
.champ input, .champ select, .champ textarea {
  width: 100%; min-height: var(--cible-tactile);
  padding: var(--sp-3) var(--sp-4);
  font-family: var(--texte); font-size: var(--fs-body); color: var(--encre);
  background: var(--papier);
  border: 1px solid var(--bordure);          /* 3,67:1 — seuil WCAG des contours */
  border-radius: var(--rayon);
  box-shadow: inset 0 1px 2px rgba(40,28,20,.05);
  transition: border-color var(--t-court) var(--sortie),
              box-shadow var(--t-court) var(--sortie);
}
.champ textarea { min-height: 10rem; resize: vertical; line-height: var(--lh-texte); }
.champ input:hover, .champ select:hover, .champ textarea:hover { border-color: var(--encre); }
.champ input:focus-visible, .champ select:focus-visible, .champ textarea:focus-visible {
  border-color: var(--rouge);
  box-shadow: 0 0 0 3px rgba(244,0,0,.16);
  outline: 2px solid var(--rouge); outline-offset: 1px;
}
.champ__aide { font-size: var(--fs-micro); color: var(--encre-70); text-transform: none; letter-spacing: 0; }

/* --- Champ en défaut --------------------------------------------------------
   Jamais la couleur seule : un contour épaissi et un message écrit portent
   l'information pour qui ne distingue pas le rouge. */
.champ input[aria-invalid="true"],
.champ select[aria-invalid="true"],
.champ textarea[aria-invalid="true"] {
  border-color: var(--rouge-action);
  border-width: 2px;
  background: color-mix(in srgb, var(--rouge) 4%, var(--papier));
}
.champ__erreur {
  display: block;
  margin-top: var(--sp-2);
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--rouge-hover);   /* 8,42:1 sur crème */
  text-transform: none;
  letter-spacing: 0;
}
.champ__erreur::before { content: "⚠ "; }

/* --- Retour du formulaire ---------------------------------------------------
   Le message qui succède à un envoi. Il reçoit le focus au chargement, d'où
   le contour visible : c'est un élément réellement focalisé, pas un artifice. */
.retour {
  margin-top: var(--sp-6);
  padding: var(--sp-5) var(--sp-6);
  border-radius: var(--rayon-carte);
  border-left: var(--rail) solid var(--rouge);
  background: var(--papier);
  box-shadow: var(--ombre-douce);
}
.retour:focus-visible { outline: 3px solid var(--rouge); outline-offset: 3px; }
.retour__titre {
  font-family: var(--titres);
  font-weight: 800;
  font-size: var(--fs-h3);
  line-height: var(--lh-inter);
  text-transform: uppercase;
  letter-spacing: var(--ls-titre);
}
.retour p + p { margin-top: var(--sp-2); color: var(--encre-70); }
/* Seul écart à la palette rouge/noir/crème : un envoi réussi doit se lire
   comme tel au premier coup d'œil. Ratios calculés : 5,35:1 pour le filet,
   8,13:1 pour le titre — AA et AAA. */
.retour--bon { border-left-color: #0F7B3F; }
.retour--bon .retour__titre { color: #0B5C2E; }
.retour--defaut .retour__titre { color: var(--rouge-hover); }

.champ--erreur input, .champ--erreur select, .champ--erreur textarea {
  border-color: var(--rouge-hover); border-width: 2px;
}
.champ__erreur {
  display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-micro); font-weight: 700; color: var(--rouge-hover);
}
.champ__erreur::before { content: "⚠"; }

.pot-de-miel {
  position: absolute !important; width: 1px; height: 1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

.champ-consentement {
  display: grid; grid-template-columns: auto 1fr; gap: var(--sp-3);
  align-items: start;
  margin-top: var(--sp-6); padding: var(--sp-5) var(--sp-6);
  background: var(--creme-2);
  border-left: var(--rail) solid var(--rouge);
  border-radius: var(--rayon) var(--rayon-media) var(--rayon-media) var(--rayon);
}
.champ-consentement input[type="checkbox"] {
  width: 22px; height: 22px; margin: 2px 0 0;
  accent-color: var(--rouge-action); flex-shrink: 0;
}
.champ-consentement label {
  font-size: var(--fs-small); font-weight: 400; line-height: var(--lh-texte);
  text-transform: none; letter-spacing: 0; color: var(--encre);
}
.champ-consentement .champ__aide { display: block; margin-top: var(--sp-2); }

.contact__infos h2 { font-size: var(--fs-h3); }
.coordonnees { margin-top: var(--sp-6); }
.coordonnees > div + div { margin-top: var(--sp-5); }
.coordonnees dt {
  font-size: var(--fs-micro); font-weight: 700;
  text-transform: uppercase; letter-spacing: .1em; color: var(--encre-70);
}
.coordonnees dd { margin: var(--sp-1) 0 0; font-size: var(--fs-lead); font-weight: 600; }

.message {
  padding: var(--sp-4) var(--sp-5); margin-bottom: var(--sp-6);
  border-left: var(--rail) solid;
}
.message--succes { background: #EDF6EF; border-color: #1E7B37; color: #14532B; }
.message--erreur { background: #FBEDEB; border-color: var(--rouge-hover); color: #7A0404; }

/* --------------------------------------------------------------------------
   18. Pied de page
   -------------------------------------------------------------------------- */

.pied { padding-block: var(--sp-9) var(--sp-6); background: var(--nuit); color: var(--creme); }

.pied__grille { display: grid; gap: var(--sp-7); grid-template-columns: 1fr; }
@media (min-width: 600px) { .pied__grille { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) { .pied__grille { grid-template-columns: 2fr 1fr 1fr; gap: var(--sp-8); } }

.pied__titre {
  font-family: var(--titres); font-weight: 800; font-size: 1.4rem;
  text-transform: uppercase; letter-spacing: .01em;
  color: var(--creme); margin-bottom: var(--sp-4);
}
/* Ces trois règles remplacent des styles en attribut `style` qui avaient
   divergé d'une page à l'autre — opacité et graisse du téléphone n'étaient
   déjà plus les mêmes sur /services et /contact. C'est exactement ce que la
   mutualisation en gabarit vient supprimer. */
.pied__accroche { color: rgba(251,249,246,.78); max-width: 34ch; }
.pied__ligne-tel { margin-top: var(--sp-5); }
.pied__tel { color: #fff; font-weight: 700; font-size: var(--fs-lead); }
.pied__tel:hover { color: var(--rouge); }

.pied__liste { list-style: none; padding: 0; }
.pied__liste li + li { margin-top: var(--sp-2); }
.pied__liste a {
  display: inline-flex; align-items: center; min-height: var(--cible-tactile);
  color: rgba(251,249,246,.78); text-decoration: none;
  transition: color var(--t-court) var(--sortie);
}
.pied__liste a:hover { color: var(--rouge); }

.pied__bas {
  margin-top: var(--sp-8); padding-top: var(--sp-5);
  border-top: 1px solid rgba(251,249,246,.15);
  display: flex; flex-wrap: wrap; gap: var(--sp-4); justify-content: space-between;
  font-size: var(--fs-small); color: rgba(251,249,246,.66);
}

/* --------------------------------------------------------------------------
   18 bis. Curseur métier — le mousqueton EST le curseur
   --------------------------------------------------------------------------
   Activé UNIQUEMENT par le script, et seulement sur pointeur fin. Sans lui,
   le curseur natif reste en place : on n'enlève jamais le curseur système
   sans avoir la certitude de pouvoir le remplacer.

   AUCUNE INERTIE, et c'est le point essentiel. Une version précédente
   interpolait la position sur cinq images environ : le curseur système étant
   masqué, il ne restait plus rien qui suive la main, et le mousqueton se
   lisait comme une traînée détachée du geste. Un curseur ne suit pas le
   pointeur, il EST le pointeur. Sa position est donc écrite telle quelle à
   chaque mouvement.

   Une corde descendait autrefois du haut de l'écran jusqu'au mousqueton. Elle
   n'a jamais rien affiché — mesurée à 1×0 pixel, faute de hauteur — et son
   principe même ajoutait au brouillage. Supprimée.
   -------------------------------------------------------------------------- */

.curseur { display: none; }

@media (hover: hover) and (pointer: fine) {
  html.curseur-actif,
  html.curseur-actif a,
  html.curseur-actif button,
  html.curseur-actif [role="button"],
  html.curseur-actif input,
  html.curseur-actif label,
  html.curseur-actif summary { cursor: none; }

  html.curseur-actif .curseur { display: block; }

  .curseur {
    position: fixed; left: 0; top: 0;
    z-index: 90;
    pointer-events: none;
    /* Pas de `transition` sur la position : elle réintroduirait le retard
       qu'on vient de supprimer. Seule l'opacité s'anime. */
    opacity: 1;
    transition: opacity .2s linear;
    will-change: transform;
  }

  /* Le décalage place le POINT du dessin sur le pixel visé, pas le centre de
     la boîte : le point est à (12,7 ; 14,7) dans un dessin de 24, soit
     (14,9 ; 17,2) une fois mis à l'échelle de 28px. */
  .curseur__mousqueton {
    position: absolute; left: 0; top: 0;
    width: 28px; height: 28px;
    margin: -17.15px 0 0 -14.82px;
    transform-origin: 14.82px 17.15px;   /* le grossissement part du point visé */
    transition: transform .16s var(--sortie);
  }
  .curseur__mousqueton svg { width: 100%; height: 100%; display: block;
                             filter: drop-shadow(0 1px 2.5px rgba(0,0,0,.4)); }
  .curseur__mousqueton .corps {
    fill: none; stroke: var(--rouge); stroke-width: 2.1;
    transition: stroke var(--t-court) linear;
  }
  .curseur__mousqueton .doigt {
    fill: none; stroke: var(--rouge); stroke-width: 2.1;
    transform-origin: 15.4px 3.9px;    /* la charnière du doigt */
    transition: transform .22s var(--sortie), stroke var(--t-court) linear;
  }
  /* Le point de visée est TOUJOURS présent : c'est lui qui rend le curseur
     précis. Discret au repos, franc au survol. */
  .curseur__point {
    fill: var(--rouge);
    opacity: .5;
    transition: opacity .16s linear, r .16s var(--sortie), fill var(--t-court) linear;
  }

  /* Au survol d'un élément cliquable : le doigt s'ouvre sur sa charnière,
     la forme grossit un peu, le point se pose franchement. */
  html.curseur-survol .curseur__mousqueton { transform: scale(1.3); }
  html.curseur-survol .curseur__mousqueton .doigt { transform: rotate(52deg); }
  html.curseur-survol .curseur__point { opacity: 1; r: 2.2; }

  /* Au clic, le mousqueton se referme d'un coup : le doigt claque, la forme
     se comprime. C'est le retour tactile qui manque quand on masque le
     pointeur du système. */
  html.curseur-presse .curseur__mousqueton { transform: scale(1.05); }
  html.curseur-presse .curseur__mousqueton .doigt { transform: rotate(0deg); }

  /* Sur les aplats sombres, le mousqueton passe en clair */
  html.curseur-clair .curseur__mousqueton .corps,
  html.curseur-clair .curseur__mousqueton .doigt { stroke: var(--creme); }
  html.curseur-clair .curseur__point { fill: var(--creme); }

  /* Masqué avant le premier mouvement, et quand la souris quitte la page.
     La classe est posée sur <html> et non sur l'élément : elle ne dépend
     donc d'aucun ordre de balisage — la règle précédente s'appuyait sur un
     sélecteur de frère qui ne correspondait à rien. */
  html.curseur-hors .curseur { opacity: 0; }
}

/* --------------------------------------------------------------------------
   18 ter. Progression de lecture — une corde qui se déroule
   -------------------------------------------------------------------------- */

.progression {
  position: fixed; left: 0; top: 0; bottom: 0;
  width: 3px; z-index: 58;
  pointer-events: none;
  background: color-mix(in srgb, var(--filet) 55%, transparent);
  opacity: 0;
  transition: opacity var(--t-moyen) var(--sortie);
}
html.progression-active .progression { opacity: 1; }
.progression__fil {
  position: absolute; inset: 0 0 auto 0;
  height: 0;
  border-radius: var(--rayon-plein);
  background: linear-gradient(180deg, var(--rouge), var(--rouge-hover));
  will-change: height;
}
@media (max-width: 899px) { .progression { display: none; } }

/* --------------------------------------------------------------------------
   18 quater. Compteurs et parallaxe
   -------------------------------------------------------------------------- */

.repere__val { font-variant-numeric: tabular-nums; }

/* Le média du héros se déplace légèrement moins vite que la page.
   transform uniquement : aucun recalcul de mise en page. */
.heros__media { will-change: transform; }

/* --------------------------------------------------------------------------
   19. Mouvement — apparition au défilement
   -------------------------------------------------------------------------- */

/* Le contenu n'est JAMAIS masqué par défaut.
   L'état initial n'est appliqué que si le script a confirmé qu'il saura le
   révéler : il pose lui-même « reveler-actif » sur <html>. Si le JavaScript
   échoue, est bloqué ou arrive trop tard, la page reste entièrement lisible. */

/* Amplitude du mouvement.

   L'entrée latérale vaut exactement la gouttière — 24px sur téléphone, 64px
   sur grand écran. Ce n'est pas un chiffre choisi au hasard : un bloc qui entre
   par la droite est décalé de la largeur de sa propre marge, donc son bord
   ne dépasse JAMAIS celui de l'écran, à aucune largeur et sans dépendre de
   `overflow-x: clip`. Le débordement horizontal est écarté par construction,
   pas par filet de sécurité. */
:root { --rev-y: 28px; --rev-x: var(--gouttiere); }
@media (max-width: 819px) { :root { --rev-y: 18px; } }

html.reveler-actif .reveler {
  opacity: 0;
  transform: translate3d(0, var(--rev-y), 0);
  transition: opacity var(--t-reveler) var(--reveler-courbe),
              transform var(--t-reveler) var(--reveler-courbe);
  /* Cascade : le script numérote les éléments d'un même groupe via --r-i.
     Le décalage n'est plus limité aux quatre premiers enfants comme avec
     nth-child, et il ne s'applique qu'aux groupes qui entrent ensemble. */
  transition-delay: calc(var(--r-i, 0) * 130ms);
}
html.reveler-actif .reveler.vu { opacity: 1; transform: none; }

/* --- Entrées latérales — deux blocs face à face se rejoignent -------------- */
html.reveler-actif .reveler--gauche { transform: translate3d(calc(-1 * var(--rev-x)), var(--rev-y), 0); }
html.reveler-actif .reveler--droite { transform: translate3d(var(--rev-x), var(--rev-y), 0); }

/* --- Titre : la ligne se dévoile par le bas, sans fondu ---------------------
   L'opacité reste à 1 : un grand titre qui apparaît en fondu paraît flou.
   Le clip-path final déborde de 30 % pour ne jamais rogner ni les jambages
   ni les accents des capitales françaises. */
html.reveler-actif .reveler--titre {
  opacity: 1;
  clip-path: inset(0 0 105% 0);
  transform: translate3d(0, .1em, 0);
  transition: clip-path var(--t-reveler-long) var(--reveler-courbe),
              transform var(--t-reveler-long) var(--reveler-courbe);
  transition-delay: calc(var(--r-i, 0) * 130ms);
}
html.reveler-actif .reveler--titre.vu {
  clip-path: inset(-30% -25% -30% -25%);
  transform: none;
}

/* --- Rail : le trait rouge se trace de haut en bas ------------------------- */
html.reveler-actif .reveler.raille::before,
html.reveler-actif .reveler .raille::before {
  transform: scaleY(0);
  transform-origin: top center;
  transition: transform var(--t-reveler-long) var(--reveler-courbe) 180ms;
}
html.reveler-actif .reveler.vu.raille::before,
html.reveler-actif .reveler.vu .raille::before { transform: scaleY(1); }

/* --- Filet horizontal d'en-tête de page : il se trace de la gauche --------- */
html.reveler-actif .reveler .page-entete__filet,
html.reveler-actif .reveler.page-entete__filet {
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--t-reveler) var(--reveler-courbe) .25s;
}
html.reveler-actif .reveler.vu .page-entete__filet,
html.reveler-actif .reveler.vu.page-entete__filet { transform: scaleX(1); }

/* --- Image : elle se pose en se rapprochant --------------------------------
   Le cadre reste immobile, seule la photo se dilate à l'intérieur.

   Animation et non transition, et SANS `both` : une règle de transition aurait
   une spécificité supérieure à `.metier:hover .cadre__interieur img` et
   empêcherait définitivement le zoom au survol. Une animation sans remplissage
   rend la main à la cascade normale dès qu'elle s'achève, sur l'état exact où
   elle se termine — donc sans saut visible. */
html.reveler-actif .reveler.vu .cadre__interieur img { animation: poser 1.8s var(--reveler-courbe); }
@keyframes poser { from { transform: scale(1.09); } to { transform: none; } }

/* --- Métier : la ligne se compose devant l'œil ------------------------------
   La ligne entière glisse latéralement, et à l'intérieur ses trois parties
   se posent l'une après l'autre : le numéro, le texte, puis la photo.

   `backwards` et non `both` : le remplissage arrière retient l'état de départ
   pendant le retard — sans lui, l'élément s'afficherait normalement puis
   sauterait au début de son animation. Et comme rien n'est retenu à l'arrivée,
   la cascade normale reprend la main : le survol de la photo fonctionne
   toujours après le passage. */
html.reveler-actif .metier.vu .metier__num {
  animation: venir var(--t-reveler) var(--reveler-courbe) .10s backwards;
}
html.reveler-actif .metier.vu .metier__corps {
  animation: venir var(--t-reveler) var(--reveler-courbe) .30s backwards;
}
html.reveler-actif .metier.vu .metier__media {
  animation: venir var(--t-reveler-long) var(--reveler-courbe) .52s backwards;
}
@keyframes venir { from { opacity: 0; transform: translate3d(0, 20px, 0); } }

/* --------------------------------------------------------------------------
   19 bis. Mouvement piloté par le défilement — sans écouteur JavaScript
   --------------------------------------------------------------------------
   `animation-timeline` lie l'animation à la position de défilement : le calcul
   se fait dans le compositeur, jamais sur le fil principal. Là où la propriété
   n'existe pas, le script prend le relais — voir site.js, module 7. */

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 700px) {

    /* Parallaxe du héros : la photo remonte moins vite que la page */
    .heros__media {
      animation: heros-parallaxe linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 100vh;
    }
    @keyframes heros-parallaxe { to { transform: translate3d(0, 14vh, 0); } }

    /* Le voile s'assombrit à mesure que le héros sort de l'écran :
       le bloc suivant n'est jamais concurrencé par la photo. */
    .heros__media::after {
      animation: heros-voile linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 90vh;
    }
    @keyframes heros-voile { to { filter: brightness(.55); } }
  }
}

/* Invite à faire défiler : battement lent, arrêté dès que la page bouge */
@media (prefers-reduced-motion: no-preference) {
  .heros__defiler::after { animation: filer 2.6s var(--entree) infinite; }
  @keyframes filer {
    0%   { transform: scaleY(0); transform-origin: top; }
    45%  { transform: scaleY(1); transform-origin: top; }
    55%  { transform: scaleY(1); transform-origin: bottom; }
    100% { transform: scaleY(0); transform-origin: bottom; }
  }
  .entete--reduite ~ * .heros__defiler::after { animation: none; }
}

/* --------------------------------------------------------------------------
   19 ter. Entrée du héros au chargement
   --------------------------------------------------------------------------
   PAS de garde `.no-js` ici, volontairement, et c'est un correctif.

   La classe `.no-js` est retirée par site.js, chargé en `defer` : le script
   s'exécute après l'analyse du document, mais le premier rendu peut avoir lieu
   AVANT. Le héros s'affichait alors figé, puis s'animait — pire que pas
   d'animation du tout.

   La garde était de toute façon inutile : une animation CSS n'a pas besoin de
   JavaScript pour s'achever. Si la feuille de style se charge, l'animation se
   déroule ; si elle ne se charge pas, rien n'est masqué. La règle « le CSS ne
   prend pas d'état dégradé sans confirmation du script » vise les états que le
   script doit défaire lui-même — la révélation au défilement — pas ceux qui se
   terminent seuls. */

@media (prefers-reduced-motion: no-preference) {
  .heros__contenu > .raille > * {
    animation: monter .95s var(--entree) both;
  }
  .heros__contenu .oeil        { animation-delay: .10s; }
  .heros__contenu .heros__titre{ animation-delay: .18s; }
  .heros__contenu .heros__lead { animation-delay: .30s; }
  .heros__actions              { animation-delay: .40s; }
  .heros__reperes              { animation-delay: .50s; }

  @keyframes monter {
    from { opacity: 0; transform: translate3d(0, 26px, 0); }
    to   { opacity: 1; transform: none; }
  }

  /* Les quatre repères se posent l'un après l'autre, après le bloc qui les
     contient. Les compteurs, eux, sont lancés par le script à l'entrée. */
  .heros__reperes > div { animation: monter .8s var(--entree) both; }
  .heros__reperes > div:nth-child(1) { animation-delay: .58s; }
  .heros__reperes > div:nth-child(2) { animation-delay: .66s; }
  .heros__reperes > div:nth-child(3) { animation-delay: .74s; }
  .heros__reperes > div:nth-child(4) { animation-delay: .82s; }

  /* Le rail du héros se trace en même temps que le surtitre */
  .heros__contenu .raille::before {
    animation: tracer 1.1s var(--entree) both .05s;
  }
  @keyframes tracer { from { transform: scaleY(0); } to { transform: scaleY(1); } }
  .heros__contenu .raille::before { transform-origin: top center; }
}

/* --------------------------------------------------------------------------
   19 quinquies. Transition entre les pages
   --------------------------------------------------------------------------
   L'API de transition de vue prend une photographie de la page qu'on quitte,
   une de celle qu'on rejoint, et anime le passage de l'une à l'autre. Le
   navigateur fait tout : pas une ligne de JavaScript, pas de navigation
   détournée, pas de bouton « précédent » cassé, et le référencement n'y voit
   rien — ce sont de vraies navigations, avec de vraies URL.

   Là où l'API n'existe pas, il ne se passe rien de particulier : la page
   change, comme avant. Aucun repli n'est nécessaire, aucun risque non plus.

   Les deux pages doivent y consentir. Toutes partagent cette feuille : c'est
   acquis pour tout le site, et pour les pages à venir. */

@view-transition { navigation: auto; }

::view-transition-old(root) {
  animation: page-sortie .22s var(--sortie) both;
}
::view-transition-new(root) {
  animation: page-entree .42s var(--entree) both;
}
@keyframes page-sortie {
  to { opacity: 0; transform: translate3d(0, -14px, 0); }
}
@keyframes page-entree {
  from { opacity: 0; transform: translate3d(0, 20px, 0); }
}

/* L'en-tête est nommé, donc capturé comme sa propre couche : il reste en place
   pendant que le contenu glisse. Sans cela, le logo partirait vers le haut avec
   le reste de la page et reviendrait — le repère visuel se perdrait à chaque
   navigation. Un nom de transition doit être unique dans le document : il n'y a
   qu'un seul en-tête, la règle tient. */
.entete { view-transition-name: entete; }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

/* --------------------------------------------------------------------------
   19 quater. Téléphone et écrans tactiles
   --------------------------------------------------------------------------
   Regroupé ici volontairement : ces règles corrigent le comportement mobile
   des composants définis plus haut, et doivent donc passer après eux. */

/* --- Encoche et coins arrondis ---------------------------------------------
   En paysage sur iPhone, la zone sûre mord de 44px sur chaque bord. Sans
   `max()`, le texte passe sous l'encoche. */
@supports (padding: max(0px)) {
  .conteneur {
    padding-left:  max(var(--gouttiere), env(safe-area-inset-left));
    padding-right: max(var(--gouttiere), env(safe-area-inset-right));
  }
  .menu-plein {
    padding-left:  max(var(--gouttiere), env(safe-area-inset-left));
    padding-right: max(var(--gouttiere), env(safe-area-inset-right));
    padding-bottom: max(var(--sp-8), env(safe-area-inset-bottom));
  }
}

/* --- Retour tactile ---------------------------------------------------------
   Le halo gris par défaut d'Android ne correspond à rien dans la charte. */
a, button, [role="button"], summary, label {
  -webkit-tap-highlight-color: color-mix(in srgb, var(--rouge) 14%, transparent);
}

@media (hover: none) {
  /* Le flou d'arrière-plan coûte une passe de composition par cadre. Sur un
     téléphone d'entrée de gamme, quatre panneaux flous dans le héros suffisent
     à faire tomber le défilement sous 60 images par seconde. On les remplace
     par des fonds plus opaques : rendu équivalent, coût nul. */
  .entete { background: color-mix(in srgb, var(--creme) 97%, transparent);
            backdrop-filter: none; -webkit-backdrop-filter: none; }
  .menu-plein { backdrop-filter: none; -webkit-backdrop-filter: none; }
  .heros .btn--secondaire { background: rgba(255,255,255,.16);
                            backdrop-filter: none; -webkit-backdrop-filter: none; }
  .heros__reperes > div { background: rgba(20,14,10,.42);
                          backdrop-filter: none; -webkit-backdrop-filter: none; }

  /* Le survol n'existe pas : ces effets ne feraient que rester collés après
     un appui. Le lien reste évidemment actif. */
  .metier:hover .cadre,
  .galerie__item:hover .cadre { transform: none; box-shadow: var(--ombre-douce); }
  .metier:hover .cadre__interieur img,
  .galerie__item:hover .cadre__interieur img { transform: none; }
}

/* --- Métiers sur téléphone --------------------------------------------------
   De 480 à 819px, le numéro passe dans la marge, à hauteur du titre, au lieu
   d'occuper une ligne entière. */
@media (min-width: 480px) and (max-width: 819px) {
  .metier {
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: var(--sp-4);
    row-gap: var(--sp-5);
    align-items: baseline;
  }
  .metier__num   { grid-column: 1; grid-row: 1; }
  .metier__corps { grid-column: 2; grid-row: 1; }
  .metier__media { grid-column: 2; grid-row: 2; }
}
/* Sous 480px la colonne du numéro amputerait la ligne de texte de 56px sur
   les 272 disponibles : il reprend sa place au-dessus du titre, en repère. */
@media (max-width: 819px) {
  /* Même largeur que sur grand écran : sur un téléphone de 390px, 22rem
     revient à toute la largeur utile. La photo n'est plus une vignette. */
  .metier__media { max-width: 22rem; }
}

/* --- Rythme vertical sur téléphone ------------------------------------------
   Six rem au-dessus ET en dessous d'une section, c'est 192px de vide entre
   deux blocs sur un écran qui en fait 844. Généreux sur un 27 pouces, mal
   tenu sur un téléphone. */
@media (max-width: 599px) {
  .section { padding-block: var(--sp-8); }
  .metiers, .etapes, .galerie, .centre { margin-top: var(--sp-7); }
  .page-entete { padding-block: var(--sp-7) var(--sp-6); }
}

/* --- Boutons du héros ------------------------------------------------------
   Empilés, deux boutons de largeurs différentes donnent un escalier. */
@media (max-width: 519px) {
  .heros__actions { flex-direction: column; align-items: stretch; }
  .heros__actions .btn { width: 100%; }
}

/* --- Écran court en paysage -------------------------------------------------
   Téléphone couché : 100dvh fait moins de 400px. Le héros plein écran devient
   une bande où plus rien ne tient. */
@media (max-height: 520px) and (orientation: landscape) {
  html.heros-plein .heros { min-height: 0; padding-block: var(--sp-9) var(--sp-7); }
  .heros__reperes { display: none; }
  .menu-plein { justify-content: flex-start; overflow-y: auto; }
  .menu-plein__liste a { font-size: clamp(1.5rem, 1rem + 3vw, 2rem); padding-block: var(--sp-2); }
}

/* --- Menu plein écran très haut ---------------------------------------------
   Cinq entrées plus le pied dépassent sur un écran de 568px de haut. */
@media (max-height: 640px) {
  .menu-plein { justify-content: flex-start; overflow-y: auto; padding-top: var(--sp-9); }
}

/* --------------------------------------------------------------------------
   19 sexies. Retour en haut
   --------------------------------------------------------------------------
   Sur une page de 21 photographies ou un article de mille mots, remonter à la
   navigation demande sinon un long geste. Le bouton est posé dans le balisage
   mais reste hors du parcours clavier tant qu'il n'est pas utile : la touche
   Début fait déjà le travail, un bouton toujours focalisable ne serait qu'un
   arrêt de plus dans la tabulation. */

.haut {
  position: fixed;
  right: var(--gouttiere);
  bottom: var(--gouttiere);
  z-index: 50;
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--cible-tactile); height: var(--cible-tactile);
  border: 0;
  border-radius: var(--rayon-plein);
  background: var(--encre);
  color: var(--creme);
  font-size: 1.1rem; line-height: 1;
  cursor: pointer;
  box-shadow: var(--ombre-relief);
  opacity: 0;
  visibility: hidden;
  transform: translateY(12px) scale(.9);
  transition: opacity var(--t-moyen) var(--sortie),
              transform var(--t-moyen) var(--sortie),
              background-color var(--t-court) var(--sortie),
              visibility 0s linear var(--t-moyen);
}
.haut--visible {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}
.haut:hover { background: var(--rouge-action); }
.haut:focus-visible { outline: 3px solid var(--rouge); outline-offset: 3px; }

@supports (padding: max(0px)) {
  .haut { bottom: max(var(--gouttiere), env(safe-area-inset-bottom)); }
}

/* --------------------------------------------------------------------------
   19 septies. Sommaire d'article
   --------------------------------------------------------------------------
   Posé par le script à partir des titres réellement présents dans l'article :
   aucune liste à tenir à jour à la main, aucun risque de décalage entre le
   sommaire et le texte. Il n'apparaît qu'à partir de 1200px, là où la colonne
   de texte laisse de la place à sa gauche, et seulement si l'article compte
   au moins trois sections. */

.sommaire { display: none; }

/* Largeur de lecture d'un article : 38rem, MESURÉE et non déduite.
   `ch` vaut la largeur du chiffre zéro, plus large que la moyenne des lettres :
   68ch donnaient 84 signes par ligne, très au-delà du confort de lecture.
   Relevé au rendu : 8,4px par signe, donc 38rem ≈ 72 signes.
   Centré par défaut, aligné à gauche quand le sommaire occupe la colonne. */
.article-avec-sommaire > .prose { max-width: 38rem; margin-inline: auto; }
html.sommaire-actif .article-avec-sommaire > .prose { margin-inline: 0; }

@media (min-width: 1200px) {
  html.sommaire-actif .article-avec-sommaire {
    display: grid;
    grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
    gap: var(--sp-8);
    align-items: start;
  }
  html.sommaire-actif .sommaire {
    display: block;
    position: sticky;
    top: calc(var(--entete-h) + var(--sp-5));
  }
}

.sommaire__titre {
  font-size: var(--fs-micro);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--ls-oeil);
  color: var(--encre-70);
  margin-bottom: var(--sp-4);
}
.sommaire ol { list-style: none; padding: 0; margin: 0; }
.sommaire li { margin-top: var(--sp-2); }
.sommaire a {
  display: block;
  padding: var(--sp-2) 0 var(--sp-2) var(--sp-4);
  border-left: 2px solid var(--filet);
  font-size: var(--fs-small);
  line-height: 1.35;
  color: var(--encre-70);
  text-decoration: none;
  transition: color var(--t-court) var(--sortie),
              border-color var(--t-court) var(--sortie);
}
.sommaire a:hover { color: var(--encre); border-color: var(--bordure); }
/* La section en cours de lecture. `aria-current` et pas seulement une classe :
   l'information vaut aussi pour qui n'a pas la couleur. */
.sommaire a[aria-current="true"] {
  color: var(--encre);
  border-color: var(--rouge);
  font-weight: 600;
}

/* --------------------------------------------------------------------------
   20. Accessibilité
   -------------------------------------------------------------------------- */

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

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  /* clip-path doit être neutralisé explicitement : la règle générique
     ci-dessous n'écourte que les durées, elle ne défait pas un masque. */
  .reveler, .reveler--titre {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    transition: none !important;
    animation: none !important;
  }
  .reveler::before, .reveler .raille::before,
  .reveler .page-entete__filet,
  .reveler .oeil::before, .reveler.oeil::before,
  .metier::before, .filtre::before { transform: none !important; }
  /* La pastille de filtre garde son fond, puisque son remplissage animé
     est neutralisé : sans cela l'état actif deviendrait invisible. */
  .filtre[aria-pressed="true"] { background: var(--encre) !important; }
  .haut { transition: none !important; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* --------------------------------------------------------------------------
   21. Impression
   -------------------------------------------------------------------------- */

@media print {
  .entete, .pied, .saut-contenu, .btn { display: none !important; }
  body { font-size: 11pt; color: #000; background: #fff; }
  .prose { max-width: none; }
  .reveler { opacity: 1 !important; transform: none !important; clip-path: none !important; }
  a { color: #000; text-decoration: underline; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; }
  .a-completer { border: 1px solid #000; background: none; }
}
