/* ==========================================================================
   Atelier Loïck Gravier — feuille commune
   Jetons, réinitialisation, en-tête collant, pied de page, boutons flottants,
   rôles typographiques. Chargée en premier sur toutes les pages.
   ========================================================================== */

:root {
  /* Palette — le laiton --laiton est réservé aux boutons. */
  --charbon: #0e0e0d;
  --charbon-clair: #1b1b19;   /* survols et bande des matières en mobile uniquement */
  --zinc: #e9e8e5;
  --blanc: #ffffff;
  --laiton: #f0b429;          /* BOUTONS UNIQUEMENT */
  --or-profond: #cf9020;      /* sur-titres, numéros, filets sur fond sombre */
  --or-fonce: #8a5a12;        /* équivalent sur fond clair */

  --texte-sombre-1: #ffffff;
  --texte-sombre-2: #c9c3b8;
  --texte-sombre-3: #a39c8e;
  --texte-clair-1: #131313;
  --texte-clair-2: #5b5b58;

  --filet-sombre: #26241f;
  --filet-clair: rgba(19, 19, 19, .14);
  --filet-clair-fort: rgba(19, 19, 19, .16);

  --titre: 'Marcellus', serif;
  --courant: 'Barlow', sans-serif;

  /* Cadrage : contenu à 1460 px centré, fonds en pleine largeur. */
  --gouttiere: clamp(28px, 5vw, 80px);
  --cadre: max(var(--gouttiere), calc((100% - 1460px) / 2));
  --section-y: clamp(60px, 6.5vw, 104px);
}

/* --------------------------------------------------------------------------
   Réinitialisation
   -------------------------------------------------------------------------- */

html { scroll-padding-top: 140px; }

body {
  margin: 0;
  background: var(--charbon);
  font-family: var(--courant);
}

a { color: var(--laiton); text-decoration: none; }
a:hover { color: var(--or-profond); }

/* Les titres portaient des <span> dans la maquette : on neutralise les marges
   et l'héritage de graisse pour que la hiérarchie sémantique ne déplace rien. */
h1, h2, h3, h4 { margin: 0; font-weight: 400; }
p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }

input::placeholder,
textarea::placeholder { color: #8f887a; opacity: 1; }

summary { list-style: none; }
summary::-webkit-details-marker { display: none; }

/* Libellé réservé aux lecteurs d'écran. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* État de focus visible, absent de la maquette. */
:focus-visible {
  outline: 2px solid var(--laiton);
  outline-offset: 3px;
}

/* --------------------------------------------------------------------------
   Rôles typographiques partagés
   -------------------------------------------------------------------------- */

.t-h1 { font: 400 clamp(40px, 4.6vw, 70px)/1.04 var(--titre); color: var(--texte-sombre-1); text-wrap: pretty; }
.t-h2 { font: 400 clamp(30px, 3.4vw, 46px)/1.08 var(--titre); color: var(--texte-sombre-1); text-wrap: pretty; }

.surtitre        { font: 500 11.5px/1 var(--courant); letter-spacing: .26em; color: var(--or-profond); }
.surtitre--clair { color: var(--or-fonce); }

.page {
  width: 100%;
  background: var(--charbon);
  display: flex;
  flex-direction: column;
}

/* Bande pleine largeur dont le contenu reste cadré à 1460 px. */
.bande { width: 100%; box-sizing: border-box; }
.bande--filet { border-bottom: 1px solid var(--filet-sombre); }

.centre {
  width: 100%;
  max-width: 1460px;
  margin: 0 auto;
  box-sizing: border-box;
}

/* --------------------------------------------------------------------------
   Révélation des gros titres, lettre par lettre

   Les <span class="lettre"> sont créés par titres.js : sans JavaScript, aucun
   n'existe et les titres s'affichent normalement. Le rang de chaque lettre est
   posé en variable par le script et sert à échelonner les départs.
   -------------------------------------------------------------------------- */

/* Le mot est insécable : la ligne ne peut se couper qu'entre les mots. */
.mot {
  display: inline-block;
  white-space: nowrap;
}

.lettre {
  display: inline-block;
  opacity: 0;
  filter: blur(12px);
  transform: translateY(40%) scale(1.1);
  will-change: transform, opacity, filter;
}

.est-revele .lettre {
  animation: revelation 1.05s cubic-bezier(.16, 1, .3, 1) forwards;
  /* Le retard est calculé lettre par lettre par titres.js, en millisecondes :
     plein sur le début de phrase, accéléré ensuite. */
  animation-delay: calc(var(--retard, 0) * 1ms);
}

@keyframes revelation {
  0% {
    opacity: 0;
    filter: blur(12px);
    transform: translateY(40%) scale(1.1);
  }
  50% {
    opacity: .7;
    filter: blur(4px);
  }
  100% {
    opacity: 1;
    filter: blur(0);
    transform: translateY(0) scale(1);
  }
}

/* Régime « frappe » : le caractère apparaît net, sans flou ni déplacement,
   comme s'il était tapé. Un curseur clignote en bout de phrase pendant la
   frappe, puis s'efface. */
.est-revele .titre-frappe__caret,
.titre-frappe .lettre { will-change: opacity; }

.titre-frappe.est-revele .lettre {
  /* Le retard fait partie du raccourci.

     Écrit à part, il était écrasé : la forme courte `animation:` réinitialise
     toutes ses sous-propriétés, y compris `animation-delay` posé plus haut
     pour le régime commun. Les cent neuf caractères partaient donc ensemble —
     d'où une phrase qui tombait d'un bloc au lieu de s'écrire. */
  animation: frappe .16s cubic-bezier(.2, .8, .3, 1)
             calc(var(--retard, 0) * 1ms) both;
}

/* Le caractère ne surgit pas tout à fait net : il se pose en un seul temps,
   très bref, avec un souffle de flou et deux pixels de chute. C'est ce qui
   sépare une frappe d'un simple affichage. */
@keyframes frappe {
  from { opacity: 0; filter: blur(3px); transform: translateY(2px); }
  to   { opacity: 1; filter: none;      transform: none; }
}

.titre-frappe::after {
  content: '';
  display: inline-block;
  width: .06em;
  height: .82em;
  margin-left: .06em;
  vertical-align: -.06em;
  background: var(--or-profond);
  opacity: 0;
}

.titre-frappe.est-revele::after {
  animation: curseur .8s steps(1) infinite,
             curseur-fin .01s linear var(--fin-frappe, 4s) forwards;
}

@keyframes curseur {
  0%, 50% { opacity: 1; }
  51%, 100% { opacity: 0; }
}

@keyframes curseur-fin {
  to { opacity: 0; animation-play-state: paused; }
}

@media (prefers-reduced-motion: reduce) {
  .lettre {
    opacity: 1;
    filter: none;
    transform: none;
    animation: none;
  }
  .titre-frappe::after { display: none; }
}

/* --------------------------------------------------------------------------
   En-tête collant
   -------------------------------------------------------------------------- */

.entete {
  position: sticky;
  top: 0;
  z-index: 80;
  background: var(--charbon);
  border-bottom: 1px solid var(--filet-sombre);
}

.entete__inner {
  width: 100%;
  max-width: 1460px;
  margin: 0 auto;
  box-sizing: border-box;
  padding: 26px var(--gouttiere);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px 28px;
}

.marque {
  font: 400 19px/1 var(--titre);
  letter-spacing: .14em;
  white-space: nowrap;
  color: var(--texte-sombre-1);
}
.marque a { color: var(--texte-sombre-1); }
.marque a:hover { color: var(--texte-sombre-1); }

.nav {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 14px 30px;
}

.nav__lien {
  font: 500 12.5px/1 var(--courant);
  letter-spacing: .14em;
  white-space: nowrap;
  color: var(--texte-sombre-1);
  transition: color .25s;
}
.nav__lien:hover { color: var(--laiton); }
.nav__lien a { color: inherit; }
.nav__lien a:hover { color: inherit; }

/* Page courante : l'or profond suffit à la distinguer sans alourdir, et un
   filet la souligne pour ne pas reposer sur la seule couleur. */
.nav__lien a[aria-current="page"] {
  color: var(--or-profond);
  border-bottom: 1px solid var(--or-profond);
  padding-bottom: 4px;
}
.nav__lien:hover a[aria-current="page"] { color: var(--or-profond); }

.nav__tel {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.nav__tel svg { display: block; }

/* --------------------------------------------------------------------------
   Boutons d'action — forme « ticket »

   Les quatre angles sont mordus par un cercle de la couleur du fond. Ces
   encoches ne sont pas des éléments ajoutés mais quatre dégradés radiaux
   posés sur le bouton lui-même : aucun balisage supplémentaire, et la forme
   suit le bouton quelle que soit sa taille.

   Conséquence à connaître : le bouton doit savoir sur quel aplat il repose.
   C'est le rôle de --fond, à redéfinir dans tout contexte clair.

   Le laiton reste réservé aux boutons ; c'est ici son seul emploi, et l'encre
   posée dessus est toujours le charbon.
   -------------------------------------------------------------------------- */

.bouton {
  --fond: var(--charbon);
  --encoche: 8px;

  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  box-sizing: border-box;
  padding: 16px 26px;
  font: 600 12.5px/1 var(--courant);
  letter-spacing: .1em;
  border: none;
  border-radius: 0;
  color: var(--charbon);
  cursor: pointer;

  background-color: var(--laiton);
  background-image:
    radial-gradient(circle var(--encoche) at 2px 2px,
      var(--fond) calc(var(--encoche) - .5px), transparent var(--encoche)),
    radial-gradient(circle var(--encoche) at calc(100% - 2px) 2px,
      var(--fond) calc(var(--encoche) - .5px), transparent var(--encoche)),
    radial-gradient(circle var(--encoche) at calc(100% - 2px) calc(100% - 2px),
      var(--fond) calc(var(--encoche) - .5px), transparent var(--encoche)),
    radial-gradient(circle var(--encoche) at 2px calc(100% - 2px),
      var(--fond) calc(var(--encoche) - .5px), transparent var(--encoche));

  transition: background-color .2s, transform .075s;
}

/* Découpe réelle plutôt que peinte, là où le navigateur sait composer les
   masques. Les encoches deviennent transparentes : le bouton se pose alors
   correctement sur n'importe quel fond, y compris une photo — ce que la
   version peinte ne sait pas faire. Les navigateurs plus anciens gardent les
   encoches peintes, qui restent justes sur un aplat. */
@supports (mask-composite: intersect) {
  .bouton {
    background-image: none;
    mask-image:
      radial-gradient(circle var(--encoche) at 2px 2px,
        transparent calc(var(--encoche) - .5px), #000 var(--encoche)),
      radial-gradient(circle var(--encoche) at calc(100% - 2px) 2px,
        transparent calc(var(--encoche) - .5px), #000 var(--encoche)),
      radial-gradient(circle var(--encoche) at calc(100% - 2px) calc(100% - 2px),
        transparent calc(var(--encoche) - .5px), #000 var(--encoche)),
      radial-gradient(circle var(--encoche) at 2px calc(100% - 2px),
        transparent calc(var(--encoche) - .5px), #000 var(--encoche));
    mask-composite: intersect;
  }

  /* Ni le contour ni l'entrée de menu ne portent d'encoches : ils ne
     doivent pas être masqués. */
  .bouton--contour,
  .bouton--bloc { mask-image: none; }
}

.bouton:hover { background-color: #ffffff; }
.bouton:active { transform: scale(.95); }

/* Le bouton peut être un lien : la couleur ne doit pas venir du <a>. */
a.bouton, a.bouton:hover { color: var(--charbon); }

/* Version haute : appel à l'action du héros et envois de formulaire. */
.bouton--large {
  padding: 22px 34px;
  font-size: 14px;
  --encoche: 9px;
}

/* Version pleine largeur : dernière entrée du menu mobile.

   Pas d'encoches ici : dans un panneau, les angles mordus donnaient
   l'impression d'une entrée détachée du menu. Aplat laiton plein. */
.bouton--bloc {
  display: flex;
  width: 100%;
  padding: 18px 20px;
  font-size: 13px;
  justify-content: flex-start;
  background-image: none;
}

/* Version secondaire, sans aplat ni encoches : les deux boutons de refus et
   d'acceptation globale des cookies, qui ne doivent pas peser autant que
   l'enregistrement des choix. */
.bouton--contour {
  background-color: transparent;
  background-image: none;
  color: var(--texte-sombre-1);
  border: 1px solid var(--texte-sombre-1);
  padding: 16px 22px;
}
.bouton--contour:hover { background-color: rgba(255, 255, 255, .08); }

.bouton--contour--gris {
  border-color: #6b665c;
  color: var(--texte-sombre-3);
}
.bouton--contour--gris:hover { background-color: rgba(255, 255, 255, .05); }

/* --------------------------------------------------------------------------
   Entrée en fondu des listes

   Même courbe que la révélation des titres, pour que tout le site bouge de
   la même façon. Les éléments d'un même groupe sont échelonnés.

   Sans JavaScript, aucun groupe ne reçoit --rang ni est-apparu : la règle
   ci-dessous ne s'applique qu'aux groupes effectivement pris en charge, et
   le contenu reste visible dans tous les cas.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  [data-apparition] > * {
    opacity: 0;
    transform: translateY(38px);
    transition: opacity 1.4s cubic-bezier(.16, 1, .3, 1),
                transform 1.4s cubic-bezier(.16, 1, .3, 1);
    transition-delay: calc(var(--rang, 0) * 200ms);
  }

  [data-apparition].est-apparu > * {
    opacity: 1;
    transform: none;
  }


  /* Mode « éléments » : chaque élément porte son propre état. */
  .apparition-element {
    opacity: 0;
    transform: translateY(38px);
    transition: opacity 1.4s cubic-bezier(.16, 1, .3, 1),
                transform 1.4s cubic-bezier(.16, 1, .3, 1);
    transition-delay: calc(var(--rang, 0) * 200ms);
  }
  .apparition-element.est-apparu {
    opacity: 1;
    transform: none;
  }
}

/* Élément explicitement tenu à l'écart de l'entrée de son groupe. Déclaré
   hors de toute requête de média, pour l'emporter sur les réglages mobiles. */
[data-apparition] > .apparition-exclu,
.apparition-exclu.apparition-exclu {
  opacity: 1;
  transform: none;
  transition: none;
  animation: none;
}

/* --------------------------------------------------------------------------
   En-tête qui se resserre au défilement
   -------------------------------------------------------------------------- */

.entete__inner {
  transition: padding .3s cubic-bezier(.16, 1, .3, 1);
}

.entete.est-condensee .entete__inner {
  padding-top: 14px;
  padding-bottom: 14px;
}

/* --------------------------------------------------------------------------
   Menu mobile — <details>/<summary>, sans JavaScript
   -------------------------------------------------------------------------- */

.burger {
  display: none;
  position: relative;
}

.burger__bouton {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  cursor: pointer;
}
.burger__bouton svg { display: block; }

.burger__panneau {
  position: absolute;
  right: 0;
  top: 50px;
  z-index: 60;
  min-width: 230px;
  /* Garde-fou : le panneau ne dépasse jamais la largeur de l'écran. */
  max-width: calc(100vw - 40px);
  background: var(--charbon);
  border: 1px solid var(--filet-sombre);
  display: flex;
  flex-direction: column;
  box-shadow: 0 18px 40px rgba(0, 0, 0, .5);
}

.burger__lien {
  font: 500 13px/1 var(--courant);
  letter-spacing: .14em;
  color: var(--texte-sombre-1);
  padding: 17px 20px;
  border-bottom: 1px solid var(--filet-sombre);
}
.burger__lien a { color: inherit; }
.burger__lien a:hover { color: inherit; }

/* Dans le panneau, la page courante se repère à sa couleur et à un repère
   vertical sur le bord — deux signaux plutôt qu'un seul. */
.burger__lien:has(a[aria-current="page"]) {
  position: relative;
  background: rgba(207, 144, 32, .08);
}
.burger__lien:has(a[aria-current="page"])::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--or-profond);
}
.burger__lien a[aria-current="page"] { color: var(--or-profond); }

/* --------------------------------------------------------------------------
   Pied de page — version desktop
   -------------------------------------------------------------------------- */

.pied {
  width: 100%;
  max-width: 1460px;
  margin: 0 auto;
  box-sizing: border-box;
  padding: clamp(48px, 5vw, 80px) var(--gouttiere) 44px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 40px 56px;
}

.pied__col {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.pied__col--marque { gap: 14px; }

.pied__nom { font: 400 22px/1.2 var(--titre); letter-spacing: .04em; color: var(--texte-sombre-1); }
.pied__baseline { font: 500 13.5px/1.8 var(--courant); color: var(--texte-sombre-3); max-width: 34ch; }
.pied__titre { font: 600 10px/1 var(--courant); letter-spacing: .2em; color: var(--or-profond); }

.pied__lien { font: 500 14px/1.6 var(--courant); color: var(--texte-sombre-1); }
.pied__lien a { color: inherit; }
.pied__lien a:hover { color: var(--laiton); }

.pied__tel { font: 600 17px/1.4 var(--courant); color: var(--laiton); }
.pied__tel a { color: inherit; }

.pied-bas { border-top: 1px solid var(--filet-sombre); }

.pied-bas__inner {
  width: 100%;
  max-width: 1460px;
  margin: 0 auto;
  box-sizing: border-box;
  padding: 22px var(--gouttiere);
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px 20px;
}

.pied-bas__texte { font: 500 12.5px/1.7 var(--courant); color: var(--texte-sombre-3); }
.pied-bas__texte a { color: var(--texte-sombre-3); }
.pied-bas__texte a:hover { color: var(--laiton); }

/* Pied allégé des pages internes : une seule ligne de mentions. */
.pied-interne {
  padding: 24px var(--cadre);
  border-top: 1px solid var(--filet-sombre);
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px 20px;
}

.pied-interne__texte { font: 500 12px/1.6 var(--courant); color: var(--texte-sombre-3); }
.pied-interne__texte a { color: var(--texte-sombre-3); }
.pied-interne__texte a:hover { color: var(--laiton); }

/* --------------------------------------------------------------------------
   Pied de page — version mobile (modèle « 14f »), masqué en desktop
   -------------------------------------------------------------------------- */

.pied-mobile {
  display: none;
  background: var(--charbon);
  border-top: 1px solid var(--filet-sombre);
  padding: 30px 22px;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  text-align: center;
}

.pied-mobile__tel { font: 600 19px/1 var(--courant); color: var(--laiton); }
.pied-mobile__tel a { color: inherit; }

.pied-mobile__liens {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  justify-content: center;
}
.pied-mobile__lien { font: 500 12px/1 var(--courant); color: var(--texte-sombre-1); }
.pied-mobile__lien a { color: inherit; }

.pied-mobile__mentions { font: 500 10.5px/1.7 var(--courant); color: var(--texte-sombre-3); }
.pied-mobile__mentions a { color: var(--texte-sombre-3); }

/* --------------------------------------------------------------------------
   Formulaire blanc à champs soulignés
   (accueil, savoir-faire, contact — traitement partagé)
   -------------------------------------------------------------------------- */

.formulaire {
  flex: 1.35 1 360px;
  display: flex;
  flex-direction: column;
  gap: 26px;
}

.honeypot { display: none; }

.formulaire__rang {
  display: flex;
  gap: 26px;
  flex-wrap: wrap;
}

.champ {
  flex: 1;
  min-width: 170px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.champ__label {
  font: 600 10px/1 var(--courant);
  letter-spacing: .2em;
  color: var(--or-fonce);
}

.champ__saisie {
  font: 500 15px/1.4 var(--courant);
  color: var(--texte-clair-1);
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--filet-clair-fort);
  padding: 14px 2px;
  width: 100%;
  box-sizing: border-box;
  outline: none;
  transition: border-color .25s;
}
.champ__saisie:focus { border-bottom-color: var(--or-fonce); }
.champ__saisie:focus-visible { outline: 2px solid var(--or-fonce); outline-offset: 2px; }

.champ__saisie--zone { line-height: 1.7; resize: vertical; }

/* --------------------------------------------------------------------------
   Bouton d'envoi — même entrée que l'appel à l'action du héros

   Il grandit jusqu'à sa taille, avec le léger dépassement de la courbe, quand
   le formulaire arrive à l'écran. Identique sur les quatre formulaires du
   site : c'est le même geste, il doit se reconnaître d'une page à l'autre.

   L'état masqué n'est posé qu'une fois la classe ajoutée par formulaire.js :
   sans JavaScript, le bouton est simplement là.
   -------------------------------------------------------------------------- */

.formulaire--anime button[type="submit"] {
  animation: entree-envoi .75s cubic-bezier(.34, 1.4, .5, 1) .35s both;
}

@keyframes entree-envoi {
  from { opacity: 0; transform: scale(.72); }
  to   { opacity: 1; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .formulaire--anime button[type="submit"] { animation: none; }
}

.formulaire__pied {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 22px;
  flex-wrap: wrap;
  margin-top: 4px;
}

.formulaire__rgpd {
  font: 500 12.5px/1.6 var(--courant);
  color: var(--texte-clair-2);
  max-width: 34ch;
}
.formulaire__rgpd a {
  color: var(--or-fonce);
  text-decoration: underline;
}

/* --------------------------------------------------------------------------
   Lumière qui parcourt le bord des formulaires

   Un cadre d'un pixel dessiné par un dégradé radial trois fois plus grand que
   le bloc : en déplaçant sa position, la zone claire du dégradé se promène le
   long du bord. Le masque évide le centre pour ne garder que le liseré.

   Encadré par @supports : sans composition de masque, le pseudo-élément
   remplirait tout le bloc d'un aplat. Mieux vaut alors ne rien afficher.
   -------------------------------------------------------------------------- */

@supports (mask-composite: exclude) {
  .bord-lumiere {
    position: relative;
    isolation: isolate;
  }

  .bord-lumiere::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    padding: 1px;
    pointer-events: none;

    background-image: radial-gradient(
      transparent, transparent,
      var(--or-profond), var(--laiton), var(--or-profond),
      transparent, transparent);
    background-size: 300% 300%;

    mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
    mask-composite: exclude;

    animation: lueur-bord 14s linear infinite;
  }

  @keyframes lueur-bord {
    0%   { background-position: 0% 0%; }
    50%  { background-position: 100% 100%; }
    100% { background-position: 0% 0%; }
  }

  @media (prefers-reduced-motion: reduce) {
    .bord-lumiere::before { animation: none; }
  }

  /* En mobile, le cadre ne bouge plus : un liseré doré constant. Le mouvement
     du formulaire, c'est l'écriture des champs. */
  @media (max-width: 680px) {
    .bord-lumiere::before {
      animation: none;
      background-image: linear-gradient(rgba(207, 144, 32, .55) 0 0);
    }
  }
}

/* --------------------------------------------------------------------------
   Demande de devis — panneau blanc
   -------------------------------------------------------------------------- */

.devis-clair {
  background: var(--blanc);
  border-bottom: 1px solid var(--filet-sombre);
}

.devis-clair__inner {
  padding: var(--section-y) var(--cadre);
  display: flex;
  gap: clamp(36px, 5vw, 72px);
  flex-wrap: wrap;
}

.devis-clair__texte {
  flex: 1;
  min-width: 260px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.devis-clair__titre {
  font: 400 clamp(30px, 3.2vw, 44px)/1.08 var(--titre);
  color: var(--texte-clair-1);
  text-wrap: pretty;
}

.devis-clair__ligne {
  font: 500 15.5px/1.8 var(--courant);
  color: var(--texte-clair-2);
  max-width: 34ch;
}

.devis-clair__tel {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
  padding-top: 24px;
  border-top: 1px solid var(--filet-clair);
}

.devis-clair__tel-libelle { font: 600 10px/1 var(--courant); letter-spacing: .2em; color: var(--or-fonce); }
.devis-clair__tel-numero  { font: 600 28px/1.2 var(--courant); color: var(--texte-clair-1); white-space: nowrap; }
.devis-clair__tel-numero a { color: inherit; }

/* Les champs de cette page portent le filet clair de la maquette. */
.devis-clair .champ__saisie { border-bottom-color: var(--filet-clair); }

/* Panneau blanc du bloc devis. */
.devis-clair .bouton { --fond: var(--blanc); }

/* Le liseré lumineux encadre le formulaire : il lui faut de la marge. */
.devis-clair .formulaire { padding: clamp(22px, 2.4vw, 34px); }

/* --------------------------------------------------------------------------
   Lampe du titre de page

   Deux cônes de lumière convergent au sommet, sous un trait incandescent :
   l'éclairage d'un poste de travail plutôt qu'un néon. Le cyan du composant
   d'origine est remplacé par l'or profond, et le cœur du trait est blanc —
   c'est ainsi que se lit une source chaude.

   Tout est décoratif : le bloc porte aria-hidden et ne contient aucun texte.
   -------------------------------------------------------------------------- */

.titrage--lampe {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  /* Toujours plus que la hauteur de la lampe, sinon les cônes passent
     derrière le titre au lieu de l'éclairer. */
  padding-top: clamp(180px, 20vw, 260px);
}

.titrage--lampe > *:not(.lampe) {
  position: relative;
  z-index: 1;
}

.lampe {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: clamp(150px, 18vw, 240px);
  pointer-events: none;
  z-index: 0;
}

.lampe__cone {
  position: absolute;
  top: 0;
  height: 100%;
  width: 15rem;
  opacity: 0;
  filter: blur(3px);
  /* Le cône s'éteint vers le bas : sans ce fondu, il se coupe net. */
  mask-image: linear-gradient(to top, transparent 4%, #000 62%);
}

.lampe__cone--gauche {
  right: 50%;
  background: conic-gradient(from 70deg at 100% 0,
    var(--or-profond) 0deg, transparent 18deg, transparent 360deg);
}

.lampe__cone--droite {
  left: 50%;
  background: conic-gradient(from 272deg at 0 0,
    transparent 0deg, transparent 342deg, var(--or-profond) 360deg);
}

/* Halo diffus juste sous la source. */
.lampe__halo {
  position: absolute;
  top: -46px;
  left: 50%;
  transform: translateX(-50%);
  width: min(28rem, 76vw);
  height: 108px;
  border-radius: 50%;
  background: var(--or-profond);
  opacity: 0;
  filter: blur(58px);
}

/* Le trait incandescent : cœur blanc, bords dorés. */
.lampe__trait {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  height: 2px;
  width: 15rem;
  opacity: 0;
  background: linear-gradient(90deg,
    transparent, var(--or-profond) 22%, #ffffff 50%, var(--or-profond) 78%, transparent);
  box-shadow: 0 0 18px rgba(240, 180, 41, .55);
}

/* Le texte monte pendant que la lampe s'ouvre.

   La montée porte sur le texte, pas sur le conteneur : posée sur le bloc,
   elle emportait la lampe avec elle, alors que celle-ci doit s'élargir sur
   place. C'est le mouvement du modèle — deux gestes distincts et simultanés. */
.titrage--lampe > *:not(.lampe) {
  animation: montee-titre .8s ease-in-out .3s both;
}

@keyframes montee-titre {
  from { opacity: .5; transform: translateY(64px); }
  to   { opacity: 1;  transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .titrage--lampe > *:not(.lampe) { animation: none; }
}

/* Allumage au chargement : la lampe est en haut de page, toujours à l'écran.
   C'est la largeur qui est animée, pas une variable : les propriétés
   personnalisées ne s'interpolent pas sans être déclarées, elles sauteraient. */
.lampe__cone,
.lampe__trait {
  animation: allumage 1.7s ease-in-out .3s forwards;
}
.lampe__halo {
  animation: halo 1.7s ease-in-out .3s forwards;
}

@keyframes allumage {
  from { opacity: .25; width: 15rem; }
  to   { opacity: 1;   width: min(30rem, 92vw); }
}

@keyframes halo {
  from { opacity: .1; }
  to   { opacity: .45; }
}

/* Sans animation, la lampe reste allumée : c'est un décor, pas une info. */
@media (prefers-reduced-motion: reduce) {
  .lampe__cone,
  .lampe__trait {
    animation: none;
    opacity: .9;
    width: min(30rem, 84vw);
  }
  .lampe__halo { animation: none; opacity: .38; }
}

/* --------------------------------------------------------------------------
   Reflet sur les grands chiffres

   Une lumière traverse le chiffre, de l'or foncé vers l'or profond. Réservé
   aux chiffres de grande taille : sur un corps de douze pixels, un dégradé
   animé produit du bruit et non un reflet.
   -------------------------------------------------------------------------- */

.reflet-chiffre {
  background-image: linear-gradient(100deg,
    var(--or-fonce) 0%,
    var(--or-fonce) 38%,
    var(--or-profond) 50%,
    var(--or-fonce) 62%,
    var(--or-fonce) 100%);
  background-size: 260% 100%;
  background-position: 100% 0;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: reflet-numero 3.4s linear infinite;
}

@keyframes reflet-numero {
  0%   { background-position: 100% 0; }
  55%  { background-position: 0% 0; }
  100% { background-position: 0% 0; }
}

/* Décalage par rang, pour que les chiffres ne brillent pas ensemble. */
.reflet-chiffre:nth-of-type(2n) { animation-delay: .5s; }

@media (prefers-reduced-motion: reduce) {
  .reflet-chiffre {
    background-image: none;
    color: var(--or-fonce);
    animation: none;
  }
}

/* --------------------------------------------------------------------------
   Boutons flottants — appel et remontée. Les seuls arrondis du site avec
   l'interrupteur de profil : ce sont des contrôles.

   Ils ne sont pas empilés : l'appel occupe le coin bas droit, la remontée le
   coin bas gauche. Aucun recouvrement, et l'action commerciale garde le côté
   du pouce. Ils restent présents en permanence.
   -------------------------------------------------------------------------- */

.flottant {
  position: fixed;
  bottom: 22px;
  z-index: 70;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;

  transition: transform .3s cubic-bezier(.16, 1, .3, 1),
              opacity .3s cubic-bezier(.16, 1, .3, 1),
              background .2s,
              box-shadow .2s;
}

.flottant svg { display: block; }

/* Appel — pastille laiton pleine, encre charbon : c'est un bouton, et le
   repère le plus visible de l'écran. */
.flottant--appel {
  right: 22px;
  width: 64px;
  height: 64px;
  background: var(--laiton);
  color: var(--charbon);
  border: none;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .6), 0 0 0 8px rgba(240, 180, 41, .16);
}
/* Pulsation discrete : un anneau laiton qui s ouvre et s efface, pour que le
   bouton d appel attire l oeil sans jamais bouger de place. */
.flottant--appel {
  animation: pulsation-appel 2.8s cubic-bezier(.22, 1, .36, 1) infinite;
}

@keyframes pulsation-appel {
  0%   { box-shadow: 0 10px 30px rgba(0, 0, 0, .6), 0 0 0 0 rgba(240, 180, 41, .5); }
  70%  { box-shadow: 0 10px 30px rgba(0, 0, 0, .6), 0 0 0 20px rgba(240, 180, 41, 0); }
  100% { box-shadow: 0 10px 30px rgba(0, 0, 0, .6), 0 0 0 0 rgba(240, 180, 41, 0); }
}

.flottant--appel:hover {
  /* L animation l emporterait sur l ombre de survol : on la coupe. */
  animation: none;
  background: var(--blanc);
  transform: translateY(-3px);
  box-shadow: 0 14px 34px rgba(0, 0, 0, .65), 0 0 0 10px rgba(240, 180, 41, .22);
}

@media (prefers-reduced-motion: reduce) {
  .flottant--appel { animation: none; }
}

/* Remontée — réservée au mobile : en desktop la molette et la touche Début
   font le travail, et le bouton encombrait le coin de l'écran. */
@media (min-width: 681px) {
  .flottant--haut { display: none; }
}

/* Pleine et contrastée elle aussi, mais sans laiton pour ne pas concurrencer
   l'appel. */
.flottant--haut {
  left: 22px;
  width: 54px;
  height: 54px;
  background: var(--zinc);
  color: var(--charbon);
  border: none;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .55);
}
.flottant--haut:hover {
  background: var(--blanc);
  transform: translateY(-3px);
}

/* ==========================================================================
   Mobile — point de rupture unique à 680 px
   ========================================================================== */

@media (max-width: 680px) {
  html { scroll-padding-top: 88px; }

  .page { padding-bottom: 24px; }

  /* En-tête : les liens cèdent la place à l'icône hamburger.

     Le retour à la ligne est interdit : s'il se produit, le hamburger tombe
     sous la marque et se cale à gauche — or son panneau s'ouvre aligné sur
     son bord droit, donc hors de l'écran. La marque est réduite pour que
     les deux tiennent sur une ligne même à 320 px. */
  .entete__inner {
    padding: 16px 20px;
    flex-wrap: nowrap;
    gap: 12px;
  }

  .marque {
    font-size: 16px;
    letter-spacing: .1em;
    min-width: 0;
  }

  .burger { flex: none; }
  .nav { display: none; }
  .nav__tel { display: none; }
  .burger { display: block; }

  .nav__lien {
    display: flex;
    align-items: center;
    min-height: 44px;
  }

  /* Cible tactile de 44 px minimum sur toute entrée de menu. */
  .burger__lien a {
    display: flex;
    align-items: center;
    min-height: 44px;
  }
  .burger__lien { padding: 10px 20px; }

  /* Le brief impose 44 px de haut sur tout lien de navigation : les trois
     liens du pied mobile n'en faisaient que 14. */
  .pied-mobile__liens { gap: 4px 10px; }
  .pied-mobile__lien a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 8px;
  }
  .pied-mobile__mentions a {
    display: inline-block;
    padding: 6px 0;
  }

  /* Bascule des deux pieds de page. */
  .pied,
  .pied-bas,
  .pied-interne { display: none; }
  .pied-mobile { display: flex; }

  .t-h1 { font-size: 31px; line-height: 1.1; }
  .t-h2 { font-size: 24px; line-height: 1.14; }

  /* Le mobile est le terrain principal du site : les entrées y sont plus
     amples et plus lisibles qu'en desktop. */
  [data-apparition] > *,
  .apparition-element {
    transform: translateY(28px);
    transition-duration: .8s;
    transition-delay: calc(var(--rang, 0) * 100ms);
  }
}

/* --------------------------------------------------------------------------
   Restitution de la version ordinateur

   Tout ce qui suit — lampe des héros, montée du titre, allumage ralenti — a
   été demandé pour le mobile. Ces réglages étaient écrits dans le tronc
   commun et débordaient donc sur l'ordinateur, qui doit rester tel qu'il
   était avant la campagne mobile. Ils y sont annulés ici.
   -------------------------------------------------------------------------- */

@media (max-width: 680px) {
  /* Un bouton doit rester atteignable au doigt : 42 px de haut passaient
     juste sous le seuil de 44 px. */
  .bouton--large { min-height: 44px; }

  [data-apparition] > .apparition-exclu-mobile,
  .apparition-exclu-mobile.apparition-exclu-mobile {
    opacity: 1;
    transform: none;
    transition: none;
    animation: none;
  }
}

@media (min-width: 681px) {
  [data-apparition] > .apparition-exclu-ordi,
  .apparition-exclu-ordi.apparition-exclu-ordi {
    opacity: 1;
    transform: none;
    transition: none;
    animation: none;
  }

  .lampe { display: none; }

  .titrage--lampe {
    padding-top: var(--section-y);
    overflow: visible;
  }

  .titrage--lampe > *:not(.lampe) { animation: none; }

  /* L'allumage retrouve sa durée d'origine : sans objet tant que la lampe est
     masquée, mais la règle reste juste si elle devait réapparaître. */
  .lampe__cone,
  .lampe__trait,
  .lampe__halo { animation-duration: .8s; }
}

/* --------------------------------------------------------------------------
   Légendes posées sur une photo

   Le voile fait l'essentiel du travail, mais une photo peut toujours révéler
   un aplat très clair sous le texte. Une ombre portée courte, invisible à la
   lecture, garantit le contraste quel que soit le cliché.
   -------------------------------------------------------------------------- */

.tuile__metal,
.tuile__titre,
.expo__metal,
.expo__titre {
  text-shadow: 0 1px 3px rgba(14, 14, 13, .85);
}
