/* ═══════════════════════════════════════════════════════════════════════════
   AGENCE WEB V2 — la refonte demandée par Matt le 2026-08-10 :
   « comme le dernier travail sur la page drone : scroll animation, refais une
     mise en page, travaille les couleurs les plus stylées d'internet, que les
     cartes des infos soient plus grosses et bougent comme sur la page drone »

   Décisions prises avec lui avant d'écrire (2026-08-10) :
     · une COPIE (agence-web.html), la page d'origine reste intacte
     · néon actuel POUSSÉ À FOND — on ne change pas de palette
     · trois pages : aiguillage → offre → tarifs
     · TOUTES les cartes grossissent et bougent

   Ce fichier se pose PAR-DESSUS le <style> de la page, css/wow.css et
   css/metiers-cartes.css, sans en remplacer une seule règle : on ajoute des
   classes et on surcharge. Revenir en arrière = retirer une ligne du <head>.

   ⚠️ ORDRE DE CHARGEMENT IMPOSÉ, et il n'est pas négociable :
        <style> de la page  →  wow.css  →  metiers-cartes.css  →  CE FICHIER
   À spécificité égale, c'est la dernière règle lue qui gagne. Chargé plus tôt,
   la moitié de ce fichier ne s'applique pas — et sans la moindre erreur.

   100 % local : aucune bibliothèque, aucun CDN, aucune police distante.
   ═══════════════════════════════════════════════════════════════════════════ */


/* ═══════════════════════════════════════════════════════════════════════════
   0. CE QUI EST DÉJÀ OCCUPÉ SUR CES CARTES — À LIRE AVANT DE MODIFIER
   ───────────────────────────────────────────────────────────────────────────
   Quatre fichiers animent déjà .metier-card / .option-card / .price-card. Une
   propriété écrite ici en écrase une autre EN SILENCE : pas d'erreur, pas
   d'avertissement, juste un effet qui disparaît et qu'on met une heure à
   relier à sa cause. L'inventaire, mesuré le 2026-08-10 :

     `translate`   → PRIS par js/magnetique.js (--mag-x/--mag-y, boutons
                     aimantés au curseur). Déclaré dans metiers-cartes.css:31.
     `transform`   → PRIS par le survol (.price-card:hover translateY(-8px),
                     .metier-card:hover translateY(-3px)) ET par .wow-incline.
     `::before`    → PRIS par wow.css (halo qui suit le pointeur).
     `::after`     → PRIS par wow.css (bordure néon animée).
     `box-shadow`  → PRIS sur .featured (featuredGlow) et sur .selected.
     `animation`   → PRIS sur .selected (wow-respire).

     `scale` et `rotate` → LIBRES. Ce sont des propriétés CSS indépendantes de
     `transform` : elles s'ADDITIONNENT avec lui au lieu de l'écraser. C'est
     par là que passe tout le mouvement permanent ajouté ici.

   Le jour où quelqu'un « simplifie » un `scale:` de ce fichier en
   `transform: scale()`, le survol des cartes cesse de fonctionner. C'est écrit
   ici pour cette raison précise. (Même leçon que metiers-cartes.css:25.)
   ═══════════════════════════════════════════════════════════════════════════ */


/* ═══════════════════════════════════════════════════════════════════════════
   1. LA PALETTE, POUSSÉE À FOND
   ───────────────────────────────────────────────────────────────────────────
   On garde EXACTEMENT les trois couleurs de la marque — orange, turquoise,
   violet — et on ajoute ce qui leur manquait pour « faire internet » : des
   versions saturées réservées aux halos, et une lumière d'ambiance par teinte.
   Aucune couleur nouvelle n'est inventée : --aw-orange-vif est le même orange
   poussé en saturation, pas un autre orange. Deux oranges qui se ressemblent
   sans être identiques, c'est ce qui fait qu'une page paraît sale.
   ═══════════════════════════════════════════════════════════════════════════ */
body.v2 {
  --aw-orange:      #ff6a3d;
  --aw-orange-vif:  #ff8a52;
  --aw-teal:        #2fe0c2;
  --aw-teal-vif:    #6ff5dd;
  --aw-violet:      #b878ff;
  --aw-violet-vif:  #d7aeff;

  /* Les lueurs. Séparées des couleurs pleines : un halo se règle en opacité,
     pas en teinte, sinon chaque retouche demande de recalculer six valeurs. */
  --aw-halo-orange: rgba(255, 106,  61, .55);
  --aw-halo-teal:   rgba( 47, 224, 194, .50);
  --aw-halo-violet: rgba(184, 120, 255, .50);
}


/* ═══════════════════════════════════════════════════════════════════════════
   2. LE FOND VIVANT
   ───────────────────────────────────────────────────────────────────────────
   UN SEUL canvas WebGL pour toute la page, en position fixe derrière tout.
   Pourquoi un seul, et pas un par section : le navigateur plafonne le nombre
   de contextes WebGL simultanés et recycle les plus anciens sans prévenir
   (« Too many active WebGL contexts »). La première section perd alors son
   fond, en silence, et seulement chez certains visiteurs.
   ═══════════════════════════════════════════════════════════════════════════ */
/* Opacité montée de .55 à .72 le 2026-08-11 (« les couleurs encore plus
   fortes »). Au-delà, les nappes du shader passent DEVANT le texte au lieu de
   rester derrière : le gris des paragraphes tombe sous le seuil de lisibilité
   sur les zones claires du bruit. .72 est le point où la couleur se voit
   franchement sans que le texte commence à se battre avec elle. */
#fond-vivant {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: .72;
}

/* ⚠️ #gold-bg PORTE UN FOND OPAQUE (`background: var(--bg)`), et il est posé
   au même niveau que le canvas. Sans cette règle, le fond vivant tourne
   parfaitement… complètement masqué, et on va chercher l'erreur dans le
   shader. Les trois blobs flous, eux, RESTENT : ce sont eux qui donnent la
   profondeur de couleur, le shader ne fait que les faire respirer. */
body.v2 #gold-bg { background: transparent; }

/* Le noir de la page repasse derrière tout le monde, en couche séparée : une
   page dont le fond est transparent laisse voir le blanc du navigateur pendant
   le chargement, ce qui donne un éclair blanc à chaque visite. */
body.v2 { background: transparent; }
body.v2::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  background: #0a0912;
}

/* Le contenu passe AU-DESSUS du fond. `main` a déjà z-index:1 dans la page ;
   on le redit ici pour le pied de page.

   ⚠️ LE TICKET N'EST PAS DANS CETTE LISTE, ET C'EST VOLONTAIRE.
   Première version : j'y avais mis `#price-ticket` avec `position: relative`.
   Le ticket est `position: fixed` — flottant en bas à droite de l'écran. Le
   passer en `relative` l'a renvoyé dans le flux du document, tout en bas de la
   page : il devenait invisible, alors que la classe `.visible` était bien
   posée et que le contrôle du calculateur restait VERT. Trouvé en regardant
   une capture, pas en lisant le code.
   C'est exactement la faute écrite trois blocs plus bas à propos du header :
   une règle qui impose `position` ne sait pas ce que l'élément était avant.
   Le ticket a déjà son z-index (200) : il n'a besoin de rien d'ici. */
body.v2 main,
body.v2 footer { position: relative; z-index: 1; }


/* ═══════════════════════════════════════════════════════════════════════════
   3. LE VERRE LIQUIDE
   ───────────────────────────────────────────────────────────────────────────
   Trois couches font le verre, et il en faut trois :
     · backdrop-filter : ce qui est DERRIÈRE est flouté et saturé
     · un dégradé très clair : la lumière qui glisse sur la surface
     · une bordure haute plus claire que la basse : l'épaisseur du bord
   Retirer l'une des trois et l'effet retombe en « rectangle flou ».
   ═══════════════════════════════════════════════════════════════════════════ */

/* ⚠️ AUCUN `position` DANS CETTE CLASSE — leçon payée le 2026-08-10 sur drone.
   La première version portait `position: relative`, ce qui a écrasé le
   `position: fixed` du <header> : la barre de navigation s'est mise à défiler
   avec la page et disparaissait au deuxième écran. Aucune erreur, aucun
   avertissement. Une classe utilitaire ne doit JAMAIS imposer `position` :
   elle ne sait pas ce que l'élément était avant. */
.verre {
  background: linear-gradient(135deg,
    rgba(255,255,255,.10) 0%,
    rgba(255,255,255,.03) 45%,
    rgba(255,255,255,.07) 100%);
  backdrop-filter: blur(18px) saturate(180%);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
  border: 1px solid rgba(255,255,255,.14);
  border-top-color: rgba(255,255,255,.30);
  border-bottom-color: rgba(255,255,255,.07);
  box-shadow:
    0 8px 32px rgba(0,0,0,.45),
    inset 0  1px 0 rgba(255,255,255,.22),
    inset 0 -1px 0 rgba(0,0,0,.25);
}

/* La barre de navigation : même matière, teintée marque, et sans bords
   latéraux (elle fait toute la largeur, un liseré vertical y serait absurde). */
header.verre {
  border-left: 0;
  border-right: 0;
  border-radius: 0;
  background: linear-gradient(135deg,
    rgba(255,106,61,.10) 0%,
    rgba(20,16,12,.55)   45%,
    rgba(184,120,255,.09) 100%);
}

/* Le ticket d'estimation flottant. Il est déjà en verre dans la page ; ici on
   ne fait que le mettre au même niveau de matière que le reste. */
#price-ticket.verre { border-radius: 18px; }

/* Repli honnête : sans backdrop-filter (vieux navigateur), un fond sombre
   lisible plutôt qu'un cadre transparent illisible. */
@supports not (backdrop-filter: blur(2px)) {
  .verre { background: rgba(12,10,20,.90); }
  header.verre { background: rgba(12,10,20,.94); }
}


/* ═══════════════════════════════════════════════════════════════════════════
   4. LES CARTES — PLUS GROSSES, ET VIVANTES
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 4a. PLUS GROSSES ─────────────────────────────────────────────────────
   Matt : « que les cartes des infos soient plus grosses ». Trois familles, et
   la même logique pour les trois : moins de colonnes, plus de marge
   intérieure, un texte plus lisible. On agrandit la GRILLE avant la carte —
   élargir une carte dans une grille à 4 colonnes ne fait que rétrécir les
   gouttières et donne une page serrée, pas une page généreuse. */

/* Les 14 métiers : 272 px mini → 340 px. À 1180 px de large (la .wrap), la
   grille passe donc naturellement de 4 colonnes à 3. */
body.v2 .metier-grid {
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: 22px;
}
@media (max-width: 1000px) {
  body.v2 .metier-grid { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
}
@media (max-width: 620px) {
  body.v2 .metier-grid { grid-template-columns: 1fr; }
}
body.v2 .metier-card {
  padding: 28px 30px 26px;
  min-height: 200px;
  border-radius: 20px;
}
body.v2 .metier-card .metier-nom     { font-size: 1.22rem; }
body.v2 .metier-card .metier-promesse{ font-size: 0.94rem; line-height: 1.5; }

/* Les cartes de prix : plus d'air, un montant qui se lit de loin. */
body.v2 .price-grid { gap: 28px; }
body.v2 .price-card {
  padding: 46px 36px;
  border-radius: 22px;
}
body.v2 .price-card h3      { font-size: 1.42rem; }
body.v2 .price-card .amount { font-size: 3.4rem; }
body.v2 .price-card li      { font-size: 0.98rem; padding: 12px 0; }
/* La ligne « options incluses » hérite du `display:flex` des <li>, qui met le
   libellé, le texte et le montant CÔTE À CÔTE en trois colonnes étroites de
   quatre mots chacune. Vu en capture le 2026-08-10 : « Les 6 / options /
   incluses » sur trois lignes. C'est une phrase, elle se lit en ligne. */
body.v2 .price-card .li-options { display: block; }
body.v2 .price-card .li-options::before { content: none; }

/* Les options. */
body.v2 .option-grid { gap: 26px; }
body.v2 .option-card {
  padding: 38px 32px;
  border-radius: 20px;
}
body.v2 .option-card h3 { font-size: 1.28rem; }
body.v2 .option-card p  { font-size: 1.02rem; line-height: 1.58; }

/* Les quatre cartes de contact. */
body.v2 .contact-info { gap: 22px; }
body.v2 .contact-info a,
body.v2 .contact-info .card {
  padding: 28px 22px;
  border-radius: 18px;
}


/* ── 4b. VIVANTES, SANS DEVENIR DES CIBLES MOBILES ────────────────────────
   ⚠️ LA DÉCISION LA PLUS IMPORTANTE DE CE FICHIER. À lire avant d'« ajouter
   un peu de mouvement ».

   Matt veut que les cartes bougent comme sur drone. Sur drone, les cartes qui
   flottent (.feature) ne se cliquent pas : les faire monter et descendre ne
   coûte rien. ICI, les trois familles de cartes SONT des boutons — elles
   pilotent le calculateur de prix. Un flottement en translation recréerait
   exactement le défaut qu'il a signalé le 2026-08-06 (« la sélection se fait
   mal ») : le curseur arrive, la carte s'est déplacée, le clic tombe à côté.
   On ne clique pas une cible qui bouge.

   PREMIÈRE TENTATIVE, ET POURQUOI ELLE A ÉTÉ ABANDONNÉE (2026-08-10).
   J'avais fait respirer les cartes cliquables sur place, en `scale: 1.012` et
   `rotate: .15deg` : le centre ne bouge pas, la boîte ne varie que de 1,7 px,
   et le clic humain n'en souffre pas. Mesuré, vérifié, vert.
   Puis la capture d'écran a refusé de cliquer : Playwright attend qu'un
   élément soit IMMOBILE sur deux images consécutives avant de le cliquer, et
   une carte qui respire ne l'est jamais — « element is not stable », trente
   secondes d'attente, puis échec. Ce n'est pas un caprice d'outil : c'est la
   définition même d'une cible instable, et elle vaut aussi pour les
   technologies d'assistance et pour tout ce qui pilote la page autrement
   qu'à la main.
   Une carte-bouton ne doit donc RIEN animer de géométrique. Pas même 1 %.

   CE QUI BOUGE À LA PLACE : la lumière. Le halo respire, la bordure
   s'allume et s'éteint. Aucune de ces deux propriétés ne touche à la boîte —
   la carte est parfaitement immobile pendant qu'elle vit. C'est exactement ce
   que recommandait déjà le commentaire de la page d'origine : « le mouvement
   permanent est porté par le liseré, qui ne déplace rien. »

   Deux contrôles gardent la porte dans outils/test-agence-web-v2.mjs : la
   boîte ne doit pas varier de plus de 3 px, et un VRAI clic Playwright doit
   aboutir en moins de 5 secondes. Le second est né de ce bug précis. */
/* Le halo ET la bordure : deux propriétés qui ne touchent pas à la boîte.
   Le halo seul se remarque peu sur une carte déjà bordée ; c'est la bordure
   qui s'allume qui donne l'impression que la carte « respire ». */
@keyframes awHaloOrange {
  0%, 100% { box-shadow: 0 0 18px -6px rgba(255,106,61,.30), 0 18px 40px -24px rgba(0,0,0,.8);
             border-color: rgba(255,106,61,.22); }
  50%      { box-shadow: 0 0 46px -4px rgba(255,106,61,.66), 0 18px 40px -24px rgba(0,0,0,.8);
             border-color: rgba(255,148,102,.62); }
}
@keyframes awHaloTeal {
  0%, 100% { box-shadow: 0 0 18px -6px rgba(47,224,194,.28), 0 18px 40px -24px rgba(0,0,0,.8);
             border-color: rgba(47,224,194,.22); }
  50%      { box-shadow: 0 0 46px -4px rgba(47,224,194,.62), 0 18px 40px -24px rgba(0,0,0,.8);
             border-color: rgba(111,245,221,.60); }
}
@keyframes awHaloViolet {
  0%, 100% { box-shadow: 0 0 18px -6px rgba(184,120,255,.28), 0 18px 40px -24px rgba(0,0,0,.8);
             border-color: rgba(184,120,255,.22); }
  50%      { box-shadow: 0 0 46px -4px rgba(184,120,255,.62), 0 18px 40px -24px rgba(0,0,0,.8);
             border-color: rgba(215,174,255,.60); }
}

/* Le halo n'est PAS posé sur les cartes .featured ni .selected : les deux ont
   déjà leur propre box-shadow animé (featuredGlow, et l'anneau de sélection).
   Deux animations sur la même propriété ne s'additionnent pas — la dernière
   déclarée gagne, et on perdrait l'indication « carte choisie », qui est une
   information, pas une décoration. */
body.v2 .metier-card:not(.actif),
body.v2 .price-card:not(.featured):not(.selected),
body.v2 .option-card:not(.selected):not(.incluse) {
  animation: awHaloOrange 4.6s ease-in-out infinite;
}
body.v2 .price-card.tier-teal:not(.selected)   { animation: awHaloTeal   4.6s ease-in-out infinite; }
body.v2 .price-card.tier-violet:not(.selected) { animation: awHaloViolet 4.6s ease-in-out infinite; }

/* LA CARTE SE FIGE DÈS QU'ON LA VISE. */
body.v2 .metier-card:hover,
body.v2 .metier-card:focus-visible,
body.v2 .price-card:hover,
body.v2 .price-card:focus-visible,
body.v2 .option-card:hover,
body.v2 .option-card:focus-visible {
  animation-play-state: paused;
}

/* Les décalages. Sans eux, les quatorze cartes respirent EN CHŒUR et la page
   entière a l'air de trembler au lieu d'avoir des cartes vivantes. Les durées
   sont volontairement premières entre elles pour que le motif ne se
   resynchronise jamais à l'œil. */
body.v2 .metier-card:nth-child(3n+2),
body.v2 .option-card:nth-child(3n+2) { animation-duration: 6.7s; animation-delay: 1.1s; }
body.v2 .metier-card:nth-child(3n+3),
body.v2 .option-card:nth-child(3n+3) { animation-duration: 4.3s; animation-delay: .4s;  }
body.v2 .price-card:nth-child(2)     { animation-duration: 6.1s; animation-delay: .9s;  }
body.v2 .price-card:nth-child(3)     { animation-duration: 4.7s; animation-delay: .3s;  }

/* Les cartes de contact, elles, NE SE CLIQUENT PAS pour choisir quoi que ce
   soit : ce sont des liens qu'on lit avant de cliquer. Elles ont donc droit au
   vrai flottement de drone, en translation franche.
   ⚠️ `translate` est libre ICI, et seulement ici : la règle magnétique de
   metiers-cartes.css ne vise pas .contact-info. */
@keyframes awFlotte {
  0%, 100% { translate: 0 0;     }
  50%      { translate: 0 -10px; }
}
body.v2 .contact-info a,
body.v2 .contact-info .card {
  animation: awFlotte 4.4s ease-in-out infinite,
             awHaloOrange 5.1s ease-in-out infinite;
}
body.v2 .contact-info a:nth-child(2) { animation-duration: 5.9s, 4.3s; animation-delay: .6s, 1.2s; }
body.v2 .contact-info a:nth-child(3) { animation-duration: 4.7s, 6.1s; animation-delay: 1.1s, .4s; }
body.v2 .contact-info .card          { animation: awFlotte 6.3s ease-in-out infinite .3s,
                                                   awHaloViolet 5.5s ease-in-out infinite .8s; }
/* Même parade que plus haut : on ne vise pas un lien qui monte et descend. */
body.v2 .contact-info a:hover { animation-play-state: paused; }


/* ═══════════════════════════════════════════════════════════════════════════
   5. LES DEUX PORTES — l'aiguillage en split-screen
   ───────────────────────────────────────────────────────────────────────────
   Deux moitiés vivantes : au survol, celle qu'on vise prend la place de
   l'autre. Même motif que l'aiguillage de drone-v2 — il fait CHOISIR son
   chemin avant d'entrer, là où deux boutons plats se contentent de nommer.
   ⚠️ Sur téléphone les deux moitiés s'empilent, elles ne rétrécissent pas :
   deux demi-écrans de 190 px de large ne montrent plus rien.
   ═══════════════════════════════════════════════════════════════════════════ */
.portes {
  display: flex;
  min-height: 76vh;
  border-top: 1px solid rgba(243,238,247,.12);
  border-bottom: 1px solid rgba(243,238,247,.12);
  overflow: hidden;
  background: #0a0912;
}

.porte {
  position: relative;
  /* ⚠️ flex-basis à 0, PAS à 50 % — mesuré le 2026-08-10 sur drone.
     Avec `flex: 1 1 50%`, les deux portes réclament déjà 100 % de la largeur :
     il ne reste aucun espace libre à distribuer, et `flex-grow` n'a donc plus
     rien à faire. Le survol ne changeait strictement rien, alors que le CSS
     était « correct ». À 0, c'est grow seul qui décide du partage. */
  flex: 1 1 0%;
  display: flex;
  align-items: flex-end;
  min-height: 76vh;
  padding: clamp(24px, 3.4vw, 56px);
  text-decoration: none;
  color: #fff;
  overflow: hidden;
  isolation: isolate;
  /* `flex-grow` est la SEULE propriété animée pour l'agrandissement : animer
     `width` relance le calcul de mise en page de toute la section à chaque
     image, et le survol devient saccadé sur une machine modeste. */
  transition: flex-grow .65s cubic-bezier(.16, 1, .3, 1);
}
.porte + .porte { border-left: 1px solid rgba(243,238,247,.12); }

.porte-fond {
  position: absolute;
  inset: 0;
  z-index: -2;
  background-size: cover;
  background-position: center;
  transform: scale(1.06);
  transition: transform 1.1s cubic-bezier(.16, 1, .3, 1), filter .6s;
  /* Assombri, mais pas éteint : à brightness(.45) la photo de droite (une vue
     aérienne déjà sombre) devenait un rectangle noir et la porte perdait son
     image. Mesuré en capture le 2026-08-10. */
  filter: grayscale(.45) brightness(.62);
}
.porte--offre  .porte-fond { background-image: url('../medias/portes/porte-web-offre.jpg'); }
.porte--tarifs .porte-fond { background-image: url('../medias/portes/porte-web-tarifs.jpg'); }

/* Le voile : il rend le texte lisible ET donne à chaque porte sa couleur
   d'identité — orange pour ce qu'on fabrique, turquoise pour ce qu'on paie. */
.porte-voile {
  position: absolute;
  inset: 0;
  z-index: -1;
  transition: opacity .6s;
}
.porte--offre .porte-voile {
  background: linear-gradient(0deg, rgba(10,9,18,.94) 0%, rgba(10,9,18,.38) 55%, rgba(255,106,61,.22) 100%);
}
.porte--tarifs .porte-voile {
  background: linear-gradient(0deg, rgba(10,9,18,.94) 0%, rgba(10,9,18,.38) 55%, rgba(47,224,194,.20) 100%);
}

.porte-contenu { position: relative; max-width: 32ch; }

.porte-num {
  display: block;
  font-family: 'JetBrains Mono', monospace;
  font-size: .72rem;
  letter-spacing: .3em;
  opacity: .65;
  margin-bottom: 14px;
}
.porte-titre {
  display: block;
  font-family: 'Anton', sans-serif;
  font-weight: 400;
  text-transform: uppercase;
  font-size: clamp(2rem, 4.4vw, 4.2rem);
  line-height: .92;
  letter-spacing: .01em;
}
.porte--offre  .porte-titre em { font-style: normal; color: var(--aw-orange-vif, #ff8a52); }
.porte--tarifs .porte-titre em { font-style: normal; color: var(--aw-teal-vif, #6ff5dd); }
.porte-texte {
  display: block;
  margin-top: 16px;
  font-size: .98rem;
  line-height: 1.6;
  opacity: .85;
  /* Le texte n'apparaît qu'au survol sur grand écran : au repos, les deux
     portes ne montrent que leur titre, et le choix se fait d'un coup d'œil. */
  transition: opacity .5s, transform .5s;
}
.porte-lien {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: 24px;
  font-family: 'JetBrains Mono', monospace;
  font-size: .72rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  padding: 13px 24px;
  border: 1px solid rgba(255,255,255,.45);
  border-radius: 999px;
  transition: background .3s, color .3s, border-color .3s;
}
.porte-lien em { font-style: normal; transition: transform .3s; }

@media (hover: hover) and (min-width: 861px) {
  .porte-texte { opacity: 0; transform: translateY(10px); }
  .portes:hover .porte      { flex-grow: .72; }
  .portes .porte:hover      { flex-grow: 1.9; }
  .porte:hover .porte-fond  { transform: scale(1); filter: grayscale(0) brightness(.68); }
  .porte:hover .porte-texte { opacity: .92; transform: none; }
  .porte:hover .porte-lien  { background: #fff; color: #0a0912; border-color: #fff; }
  .porte:hover .porte-lien em { transform: translateX(5px); }
}

/* Sans survol (tactile) ou petit écran : tout est visible d'emblée, sinon le
   texte reste invisible pour de bon sur un téléphone. */
@media (hover: none), (max-width: 860px) {
  .porte-fond { filter: grayscale(.2) brightness(.55); }
}
@media (max-width: 860px) {
  .portes { flex-direction: column; }
  .porte  { min-height: 60vh; flex: 1 1 auto; }
  .porte + .porte { border-left: 0; border-top: 1px solid rgba(243,238,247,.12); }
}


/* ═══════════════════════════════════════════════════════════════════════════
   6. LES TITRES QUI SE DISPERSENT
   ───────────────────────────────────────────────────────────────────────────
   ⚠️ DIFFÉRENCE AVEC DRONE, et c'est ce qui a demandé le plus de précautions :
   sur drone.html les lettres étaient déjà écrites une par une dans le fichier
   (.sw > .sm > .sc). Ici les titres sont du texte simple : c'est le JS qui les
   découpe au chargement. Le découpage n'est donc JAMAIS enregistré dans le
   fichier — voir la protection dans js/agence-web-v2-effets.js.
   ═══════════════════════════════════════════════════════════════════════════ */
.js-eclat .aw-mot {
  display: inline-block;
  white-space: nowrap;   /* un mot ne se coupe jamais en deux lignes */
}
.js-eclat .aw-lettre {
  display: inline-block;
  will-change: transform, opacity, filter;
  /* aucune transition CSS : le JS écrit une position à chaque image, et deux
     animations sur la même propriété rendent le mouvement mou. */
}


/* ═══════════════════════════════════════════════════════════════════════════
   7. LE RYTHME DE LA PAGE
   ───────────────────────────────────────────────────────────────────────────
   Refaire « la mise en page » ne veut pas dire déplacer des blocs au hasard :
   c'est donner à l'œil un rythme régulier — un grand titre, de l'air, un bloc
   dense, de l'air. Les sections d'origine étaient toutes au même écart.
   ═══════════════════════════════════════════════════════════════════════════ */
/* ⚠️ 104 px MAXIMUM, et pas davantage. Deux sections voisines ADDITIONNENT
   leur marge : à 132 px, cela faisait 264 px de vide entre la dernière carte
   d'une section et le titre de la suivante — mesuré en capture, et c'est
   précisément le reproche que Matt a déjà formulé deux fois sur cette page
   (« trop d'espace vide en haut / en bas », 2026-08-05). Aérer n'est pas
   espacer : ce qui donne de l'air ici, ce sont les cartes plus grandes et le
   trait lumineux, pas des centaines de pixels de noir. */
body.v2 section { padding: clamp(64px, 6.5vw, 104px) 32px; }
@media (max-width: 900px) { body.v2 section { padding: 64px 20px; } }

/* ⚠️ LE TITRE PASSAIT SOUS LA BARRE DE NAVIGATION. Vu à l'écran le
   2026-08-10, jamais par un test.
   Le <header> est `position: fixed` et haut de 82 px. Il ne fait donc pas
   partie du flux : quand le navigateur saute à une ancre, il place le haut de
   la section au haut de la FENÊTRE — c'est-à-dire derrière la barre. On
   perdait la première ligne de chaque titre, et sur une section courte on
   arrivait carrément au milieu du texte.
   `scroll-margin-top` ne déplace rien à l'écran : il dit seulement au
   navigateur où s'arrêter en arrivant. 100 px = les 82 de la barre, plus un
   peu d'air pour que le titre ne colle pas dessous. */
body.v2 section[id] { scroll-margin-top: 100px; }
@media (max-width: 900px) { body.v2 section[id] { scroll-margin-top: 78px; } }

/* ⚠️ DÉFAUT D'ORIGINE, mesuré le 2026-08-10 et corrigé ici.
   `.wrap` porte `margin: 0 auto`, et `.hero` est un conteneur flex. Or dans
   une boîte flex, une marge latérale `auto` ABSORBE l'espace libre au lieu de
   centrer un bloc étiré : l'enfant cesse de s'étirer et se réduit à la largeur
   de son contenu. Mesure sur la page d'origine à 1440 px de large : le bloc du
   hero faisait 705 px au lieu de 1180, et démarrait à 368 px du bord au lieu
   de 130 — tout le titre était poussé vers la droite, dans un vide qui
   ressemblait à un choix de mise en page.
   `width: 100%` rend l'étirement, `max-width` continue de plafonner, et les
   marges auto retrouvent leur seul rôle : centrer. */
body.v2 .wrap { width: 100%; }

/* Les titres de section montent d'un cran : ce sont eux qui portent la
   dispersion au scroll, un titre trop petit rend l'effet invisible. */
body.v2 section h2 {
  font-size: clamp(2.6rem, 6vw, 5rem);
  max-width: 20ch;
  margin-bottom: 30px;
}
body.v2 .section-intro { font-size: 1.16rem; max-width: 680px; }

/* L'ACCENT DES TITRES — le détail qui sépare une page « sombre » d'une page
   « néon ». Poussé le 2026-08-11 : au lieu d'une couleur unie avec un halo,
   c'est un DÉGRADÉ qui glisse lentement d'un bout à l'autre du mot.

   ⚠️ Le dégradé passe par `background-clip: text`, donc `color` devient
   transparent — et un `text-shadow` sur un texte transparent ne dessine PLUS
   le contour des lettres, il projette une tache derrière le bloc. La lueur est
   donc portée par `drop-shadow` sur un filtre, qui suit la forme réelle du
   texte. Remplacer l'un par l'autre éteint le néon sans rien signaler. */
/* ⚠️ LE DÉGRADÉ NE VA PAS SUR UN TITRE QUI SE DISPERSE. Mesuré à l'écran le
   2026-08-11 : l'accent de « Le site, et ce qui le fait vivre. » avait
   PUREMENT DISPARU de la page — pas pâli, disparu.
   La cause : `background-clip: text` peint le fond du parent à travers la
   forme de son texte, mais il ne traverse pas un enfant qui possède son
   propre contexte d'empilement. Or chaque lettre découpée porte
   `will-change: transform, opacity, filter` — et `will-change` crée ce
   contexte, exactement comme le ferait la propriété finale. Les lettres
   héritaient donc de `color: transparent` sans jamais recevoir le dégradé.
   Rien dans la console, rien dans les styles calculés : juste un titre à
   moitié absent.
   Les titres découpés gardent donc une couleur PLEINE, avec un halo renforcé
   — voir plus bas `.js-eclat .accent`. Le dégradé qui glisse reste sur les
   titres entiers. */
body.v2 section h2 .accent,
body.v2 .hero h1.display .accent,
body.v2 .offre-hero h1.display .accent {
  background: linear-gradient(100deg,
    var(--aw-orange-vif, #ff8a52) 0%,
    #ffd0a8 22%,
    var(--aw-orange, #ff6a3d) 46%,
    var(--aw-violet-vif, #d7aeff) 72%,
    var(--aw-orange-vif, #ff8a52) 100%);
  background-size: 260% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 10px rgba(255,106,61,.5)) drop-shadow(0 0 34px rgba(255,106,61,.3));
}
@media (prefers-reduced-motion: no-preference) {
  body.v2 section h2 .accent,
  body.v2 .hero h1.display .accent,
  body.v2 .offre-hero h1.display .accent { animation: awDegrade 9s ease-in-out infinite; }
}
@keyframes awDegrade {
  0%, 100% { background-position:   0% 50%; }
  50%      { background-position: 100% 50%; }
}

/* LES TITRES QUI SE DISPERSENT : couleur pleine, et le néon passe par le halo.
   `.js-eclat` est posée par le JS au moment du découpage — la règle ne
   s'applique donc qu'aux titres réellement découpés, et le jour où l'on
   n'éclate plus un titre, il retrouve son dégradé sans qu'on touche à rien. */
/* ⚠️ LES TROIS SÉLECTEURS REPRENNENT EXACTEMENT CEUX DU DÉGRADÉ, avec
   `.js-eclat` en plus. Une règle plus courte (`body.v2 .js-eclat .accent`) est
   MOINS spécifique que `body.v2 .offre-hero h1.display .accent` : elle perdait
   la cascade, et le titre de la page offre restait invisible alors que celui
   des autres pages s'affichait. Vu par le contrôle « aucun morceau de titre
   n'est invisible », pas à l'œil — sur une capture, un titre absent ressemble
   à un titre qui n'a pas encore été révélé. */
body.v2 section h2.js-eclat .accent,
body.v2 .hero h1.display.js-eclat .accent,
body.v2 .offre-hero h1.display.js-eclat .accent {
  background: none;
  -webkit-background-clip: border-box;
  background-clip: border-box;
  color: var(--aw-orange-vif, #ff8a52);
  filter: none;
  animation: none;
  text-shadow:
    0 0 10px rgba(255,148,102,.75),
    0 0 40px rgba(255,106,61,.45),
    0 0 90px rgba(255,106,61,.25);
}

/* ⚠️ EXCEPTION : le mot « OFFERT » du hero garde sa pulsation d'origine
   (`offertPulse`, posée le 2026-08-03 à la demande de Matt : « que le OFFERT
   se voie plus, c'est mon argument de vente »). Un dégradé qui glisse
   l'aurait remplacée en silence, puisque les deux animent le même élément.
   Le dégradé s'arrête donc à la porte du hero de l'aiguillage. */
body.v2 .hero:not(.offre-hero) h1.display .accent {
  background: none;
  -webkit-background-clip: border-box;
  background-clip: border-box;
  color: var(--aw-orange, #ff6a3d);
  filter: none;
}

/* ⚠️ LA PULSATION EST SÉPARÉE DES PROPRIÉTÉS VISUELLES, ET CE N'EST PAS
   COSMÉTIQUE. Les cinq lignes ci-dessus annulent le dégradé glissant : elles
   doivent s'appliquer À TOUT LE MONDE, y compris à qui a réduit les
   animations, sinon le mot « OFFERT » reprendrait un dégradé qui bouge.
   Seule l'`animation` dépend de la préférence.

   Mesuré le 2026-08-16 par `outils/test-mouvement-reduit.mjs`, qui a viré au
   rouge dès sa première exécution : `animation` était déclarée ICI hors de
   toute media query, alors que la règle jumelle de `agence-web.html:234` est,
   elle, correctement enfermée sous `no-preference`. Le visiteur ne voyait rien
   pulser quand même — mais seulement parce que le balai
   `*{animation-duration:0.01ms !important; animation-iteration-count:1
   !important}` (agence-web.html:575) l'écrasait au passage. Une protection qui
   ne tient qu'à un balai `!important` posé ailleurs n'est pas une protection :
   elle tombe le jour où ce balai bouge, et personne ne fait le lien. */
@media (prefers-reduced-motion: no-preference) {
  body.v2 .hero:not(.offre-hero) h1.display .accent {
    animation: offertPulse 3.6s ease-in-out infinite;
  }
}

/* Le trait lumineux qui sépare deux sections. Posé sur la section elle-même et
   pas sur un élément en plus : l'éditeur de pages efface les éléments neufs
   qu'il ne reconnaît pas, un séparateur ajouté en HTML disparaîtrait au
   premier enregistrement. */
body.v2 main > section + section::before {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  translate: -50% 0;
  width: min(680px, 82%);
  height: 1px;
  background: linear-gradient(90deg,
    transparent,
    rgba(255,106,61,.42) 28%,
    rgba(184,120,255,.42) 72%,
    transparent);
  pointer-events: none;
}


/* ── LA NAVIGATION DU PIED DE PAGE ────────────────────────────────────────
   Seule navigation disponible sur téléphone, où le menu du haut est masqué
   sous 900 px. Elle doit donc être lisible et cliquable au doigt : 44 px de
   hauteur de cible minimum, pas un lien de 12 px comme le reste du pied. */
.pied-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 28px;
  margin: 0 auto 22px;
  padding-bottom: 20px;
  border-bottom: 1px solid rgba(243,238,247,.12);
  max-width: 720px;
}
.pied-nav a,
.pied-nav .pied-ici {
  font-family: 'Space Grotesk', sans-serif;
  font-size: .84rem;
  letter-spacing: .06em;
  text-transform: none;
  text-decoration: none;
  padding: 11px 4px;          /* la cible fait 44 px de haut, au doigt */
  opacity: .82;
  transition: color .2s, opacity .2s;
}
.pied-nav a:hover { color: var(--aw-orange-vif, #ff8a52); opacity: 1; }
.pied-nav .pied-ici { color: var(--aw-orange-vif, #ff8a52); opacity: 1; }

/* La page courante se signale dans le menu. À trois pages, un menu qui ne dit
   pas où l'on est oblige à lire l'URL. */
body.v2 nav a.actif {
  color: var(--aw-orange-vif, #ff8a52);
  opacity: 1;
  text-shadow: 0 0 18px rgba(255,106,61,.5);
}


/* ═══════════════════════════════════════════════════════════════════════════
   7 bis. LES BLOCS DE LA PAGE « OFFRE »
   ───────────────────────────────────────────────────────────────────────────
   Ces cartes-là NE SE CLIQUENT PAS : elles expliquent, on ne les sélectionne
   pas. Elles ont donc droit au vrai flottement de drone, en translation
   franche — `translate` est libre ici, la règle magnétique de
   metiers-cartes.css ne vise que .btn/.metier-card/.option-card/.price-card.
   ═══════════════════════════════════════════════════════════════════════════ */
.offre-hero {
  min-height: 72vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 168px 32px 84px;
}
.offre-hero h1.display {
  font-size: clamp(2.8rem, 7vw, 5.6rem);
  max-width: 16ch;
  margin-bottom: 28px;
}
.offre-hero h1.display .accent {
  color: var(--aw-orange-vif, #ff8a52);
  text-shadow: 0 0 10px rgba(255,148,102,.75), 0 0 42px rgba(255,106,61,.45);
}
@media (max-width: 900px) { .offre-hero { padding: 130px 20px 64px; } }

.aw-grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: 26px;
  margin-top: 12px;
}
.aw-grille--large { grid-template-columns: repeat(auto-fill, minmax(420px, 1fr)); }
@media (max-width: 720px) {
  .aw-grille, .aw-grille--large { grid-template-columns: 1fr; }
}

.aw-carte {
  position: relative;
  padding: 38px 34px 34px;
  border-radius: 20px;
  border: 1px solid rgba(243,238,247,.14);
  background: linear-gradient(158deg, rgba(255,106,61,.08), rgba(23,20,38,.72) 58%);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  overflow: hidden;
  transition: border-color .3s, background .35s;
  animation: awFlotte 5.4s ease-in-out infinite,
             awHaloOrange 4.8s ease-in-out infinite;
}
/* Une carte sur trois vire au violet, une sur trois au turquoise : trois
   couleurs de marque et une grille de six, c'est ce qui fait « néon » plutôt
   que « orange partout ». Le halo suit la même famille que le fond, sinon la
   carte a une bordure d'une couleur et une lueur d'une autre. */
.aw-carte:nth-child(3n+2) {
  background: linear-gradient(158deg, rgba(184,120,255,.10), rgba(23,20,38,.72) 58%);
  animation: awFlotte 6.7s ease-in-out infinite .5s,
             awHaloViolet 5.9s ease-in-out infinite 1.1s;
}
.aw-carte:nth-child(3n+3) {
  background: linear-gradient(158deg, rgba(47,224,194,.10), rgba(23,20,38,.72) 58%);
  animation: awFlotte 4.3s ease-in-out infinite 1.3s,
             awHaloTeal 6.1s ease-in-out infinite .4s;
}
.aw-carte:hover              { border-color: rgba(255,106,61,.55); animation-play-state: paused; }
.aw-carte:nth-child(3n+2):hover { border-color: rgba(184,120,255,.6); }
.aw-carte:nth-child(3n+3):hover { border-color: rgba(47,224,194,.6); }

.aw-num {
  display: block;
  font-family: 'JetBrains Mono', monospace;
  font-size: .72rem;
  letter-spacing: .28em;
  color: var(--aw-orange-vif, #ff8a52);
  margin-bottom: 16px;
}
.aw-carte:nth-child(3n+2) .aw-num { color: var(--aw-violet-vif, #d7aeff); }
.aw-carte:nth-child(3n+3) .aw-num { color: var(--aw-teal-vif, #6ff5dd); }

.aw-carte h3 {
  font-family: 'Space Grotesk', sans-serif;
  font-size: 1.34rem;
  line-height: 1.2;
  margin-bottom: 12px;
  color: #f3eef7;
}
.aw-carte p {
  font-size: 1.02rem;
  line-height: 1.6;
  opacity: .84;
}

/* Le bloc de rappel en bas de l'aiguillage : centré, court, un seul bouton. */
.aw-rappel { text-align: center; }
.aw-rappel .wrap { max-width: 860px; }
.aw-rappel h2 { margin-left: auto; margin-right: auto; max-width: 22ch; }
.aw-rappel .section-intro { margin-left: auto; margin-right: auto; }

/* La section des portes n'a pas de marge intérieure : le split-screen touche
   les deux bords de l'écran, c'est ce qui lui donne sa force. */
body.v2 .portes-section { padding: 0; }


/* ═══════════════════════════════════════════════════════════════════════════
   9. LE HERO À DEUX COLONNES — les trois cartes de verre
   ───────────────────────────────────────────────────────────────────────────
   Choisi par Matt le 2026-08-11 : la droite du premier écran était vide. Trois
   cartes flottent en diagonale et disent ce qu'on vend avant tout défilement.
   Elles ne se cliquent pas (`aria-hidden`, aucun lien) : elles ont donc droit
   au vrai flottement, en `translate`.
   ═══════════════════════════════════════════════════════════════════════════ */
.hero-duo .wrap {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
  gap: clamp(28px, 4vw, 72px);
  align-items: center;
}
.hero-duo .hero-texte { min-width: 0; }

.hero-cartes {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 8px 0;
}

/* ⚠️ LARGEUR BORNÉE, ET C'EST CE QUI REND L'AGRANDISSEMENT POSSIBLE.
   Mesuré le 2026-08-11 : la carte 01 faisait 533 px dans une colonne qui en
   mesure 534. À ×2 elle occupait 1066 px et recouvrait le titre sur 475 px —
   « qu'elles restent à leur place à droite » (Matt). Aucun zoom, même 1,25×,
   ne tenait : la carte remplissait DÉJÀ sa colonne. On la rétrécit donc au
   repos, et la place libérée devient la course du zoom.
   La diagonale se fait par des largeurs décroissantes, PAS par un décalage :
   les trois bords droits restent alignés, donc chaque carte grandit vers la
   gauche depuis le même mur — sans quoi la plus décalée mordrait la première. */
.hero-carte {
  width: 100%;
  max-width: var(--hc-large, 320px);
  margin-left: auto;
  position: relative;
  padding: 22px 26px;
  border-radius: 18px;
  border: 1px solid rgba(255,255,255,.16);
  border-top-color: rgba(255,255,255,.32);
  background: linear-gradient(135deg, rgba(255,255,255,.11) 0%, rgba(255,255,255,.03) 48%, rgba(255,255,255,.07) 100%);
  backdrop-filter: blur(18px) saturate(180%);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
  box-shadow: 0 18px 44px -18px rgba(0,0,0,.75), inset 0 1px 0 rgba(255,255,255,.22);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* La diagonale : chaque carte est un cran plus courte que la précédente, bords
   droits alignés. Le décalage se lit sur le bord GAUCHE, exactement comme
   avant — mais cette fois il ne coûte rien à l'agrandissement.
   (L'ancienne version décalait par `margin-left` en % ; la carte 3 avait alors
   son bord droit au même endroit mais partait de plus loin, et une fois
   agrandie elle traversait la colonne du titre.) */
.hero-carte--1 { --hc-large: 320px; }
.hero-carte--2 { --hc-large: 292px; }
.hero-carte--3 { --hc-large: 264px; }

.hc-icone {
  font-family: 'JetBrains Mono', monospace;
  font-size: .68rem;
  letter-spacing: .28em;
  opacity: .7;
}
.hero-carte--1 .hc-icone { color: var(--aw-orange-vif, #ff8a52); }
.hero-carte--2 .hc-icone { color: var(--aw-teal-vif,   #6ff5dd); }
.hero-carte--3 .hc-icone { color: var(--aw-violet-vif, #d7aeff); }

.hero-carte strong {
  font-family: 'Space Grotesk', sans-serif;
  font-size: 1.12rem;
  font-weight: 700;
  line-height: 1.25;
}
.hero-carte span:last-child { font-size: .94rem; line-height: 1.5; opacity: .82; }

/* Elles flottent chacune à son rythme, et une lueur de sa couleur respire. */
.hero-carte--1 { animation: awFlotte 5.2s ease-in-out infinite,      awHaloOrange 4.6s ease-in-out infinite; }
.hero-carte--2 { animation: awFlotte 6.4s ease-in-out infinite .7s,  awHaloTeal   5.7s ease-in-out infinite .4s; }
.hero-carte--3 { animation: awFlotte 4.5s ease-in-out infinite 1.2s, awHaloViolet 6.3s ease-in-out infinite .9s; }

/* ═══ L'AIMANT DU HERO — demandé par Matt le 2026-08-11 ════════════════════
   « Dès que je passe la souris dessus, je veux que les cartes 1 2 3 bougent
   plus et s'agrandissent ×2 avec un effet aimant à la souris, et dès que la
   souris ne bouge plus, la carte ne bouge plus. »

   ⚠️ LE FLOTTEMENT S'ARRÊTE AU SURVOL, ET C'EST LE CŒUR DE LA DEMANDE.
   « Dès que la souris ne bouge plus, la carte ne bouge plus » est impossible
   tant que `awFlotte` tourne : la carte continuerait sa montée-descente sous
   un curseur immobile. On met donc l'animation en pause, et c'est l'aimant —
   qui ne bouge que sur `pointermove` — qui prend seul la main. Souris
   immobile, aucun événement, carte figée. Exactement ce qu'il décrit.

   ⚠️ ELLE GRANDIT VERS LA GAUCHE, PAS DEPUIS SON CENTRE.
   Ces cartes vivent dans la colonne droite du hero. À ×2 depuis le centre,
   une carte de 450 px en occuperait 900 et dépasserait de 320 px hors de
   l'écran : le navigateur ajoute une barre horizontale et toute la page se
   met à glisser latéralement. `transform-origin: right center` la fait
   grandir vers l'intérieur de la page, là où il y a de la place.

   ⚠️ `scale` ET `translate` SONT DES PROPRIÉTÉS SÉPARÉES ici : le zoom et
   l'aimant doivent s'additionner. Écrire les deux dans `transform` ferait que
   le dernier écrase l'autre — la carte grandirait sans suivre, ou l'inverse. */
.hero-carte {
  transform-origin: right center;
  will-change: translate, scale;
}
/* ⚠️ `body.v2` EN TÊTE — SANS LUI, RIEN NE SE PASSE. La règle générale
   « toutes les cartes grandissent au survol » s'écrit `body.v2 .hero-carte:hover`
   et vaut (0,3,0) ; un `.hero-carte:hover` seul ne vaut que (0,2,0) et perd la
   cascade. Mesuré le 2026-08-11 : le scale restait à 1.035 au lieu de 2, la
   règle était pourtant bien dans le fichier. Quatrième fois que ce piège se
   referme ici — quand on surcharge, on repart du sélecteur complet.
   L'aimant : le JS écrit --aim-x / --aim-y à chaque mouvement du pointeur.
   Repli à 0 : sans script, la carte grandit sur place et rien ne casse. */
/* ⚠️ LE ZOOM EST CALCULÉ, PAS CHOISI. `--hc-zoom` est écrit par le script
   (`calibrerZoomHero`) à partir de la place réellement disponible entre le
   bord droit de la carte et le bord droit de la colonne du titre. Un chiffre
   fixe serait juste à 1440 px et faux partout ailleurs : à 1024 px de large,
   la même carte n'a plus que 1,4× de course. Le repli 1.35 s'applique si le
   script ne tourne pas — il ne mord sur le titre à aucune largeur testée. */
body.v2 .hero-carte:hover {
  animation-play-state: paused;   /* le flottement se tait */
  scale: var(--hc-zoom, 1.35);
  z-index: 6;
  transition: scale .38s cubic-bezier(.16, 1, .3, 1);
  translate: var(--aim-x, 0px) var(--aim-y, 0px);
}
/* ⚠️ LE TEXTE GRANDIT AVEC LA CARTE — NE PAS LE COMPENSER.
   Première version : je réduisais les tailles de police au survol pour que la
   carte « gagne en surface et pas en grossissement typographique ». Résultat
   vu en photo : une carte deux fois plus grande avec un texte minuscule au
   milieu, illisible. Matt : « que les touches s'agrandissent et non pas plus
   petites. »
   Un `scale: 2` agrandit déjà tout proportionnellement — c'est exactement le
   comportement voulu. Il n'y a rien à corriger, et toute compensation ici
   annule l'effet qu'on vient de créer. */

@media (prefers-reduced-motion: reduce) {
  body.v2 .hero-carte:hover { scale: 1; translate: 0 0; }
}
/* Sur écran tactile, il n'y a pas de survol : l'agrandissement se
   déclencherait au premier effleurement et resterait collé. */
@media (hover: none) {
  .hero-carte:hover { scale: 1; translate: 0 0; animation-play-state: running; }
}

/* Sous 1000 px, deux colonnes donnent deux moitiés trop étroites : le titre se
   casse en bouillie et les cartes ne se lisent plus. On repasse en une colonne
   et les cartes viennent SOUS le bouton, sans décalage en diagonale. */
@media (max-width: 1000px) {
  .hero-duo .wrap { grid-template-columns: 1fr; gap: 40px; }
  /* Plus de colonne à côté : la carte reprend toute la largeur et perd la
     diagonale, sinon elle se retrouve étroite et perdue au milieu du vide. */
  .hero-carte { max-width: none; margin-left: 0; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   10. LA SECTION QUI RESTE COLLÉE
   ───────────────────────────────────────────────────────────────────────────
   Le titre tient en place pendant que les six cartes défilent à côté. C'est le
   « scroll animation » le plus lisible qui soit : on garde sous les yeux ce
   dont on parle pendant qu'on en déroule les preuves.

   ⚠️ `position: sticky` et JAMAIS un pin GSAP — le pin fabrique un conteneur
   dans le DOM, que l'éditeur de pages réenregistre ensuite dans le fichier.
   ⚠️ Et surtout : le sticky ne fonctionne QUE si aucun parent n'est un
   conteneur de défilement. C'est pour ça que la page porte `overflow-x: clip`
   et non `hidden` (voir le commentaire ligne 148 de agence-web.html) : avec
   `hidden`, tout sticky de la page cesse de coller, sans une seule erreur.
   ═══════════════════════════════════════════════════════════════════════════ */
.aw-colle-duo {
  display: grid;
  grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: start;
}
.aw-colle-titre {
  position: sticky;
  top: 132px;              /* la barre fixe (100) + de l'air */
  align-self: start;
}
.aw-colle-titre .section-intro { margin-bottom: 0; font-size: 1.04rem; }
.aw-colle-corps { min-width: 0; }   /* sans ça, une grille large fait déborder la colonne */

/* ⚠️ LE TITRE COLLÉ EST PLUS PETIT QUE LES AUTRES, et ce n'est pas un détail
   esthétique. Mesuré en capture le 2026-08-11 : à la taille pleine page
   (jusqu'à 5 rem), « Des formules claires, un seul engagement : votre succès »
   tenait sur SIX lignes dans une colonne de 440 px et occupait 780 px de haut,
   sur un écran de 900. Un bloc qui remplit l'écran ne peut pas « rester
   collé » : il n'a plus de course, et le sticky ne se voit jamais.
   Le titre doit tenir dans la moitié haute de l'écran pour que l'effet existe. */
/* ⚠️ LE SÉLECTEUR REPREND CELUI QU'IL SURCHARGE, `body.v2 section h2`, avec la
   classe en plus. Écrit court (`.aw-colle-titre h2`), il perdait la cascade :
   (0,1,1) contre (0,1,3). Mesuré le 2026-08-11 — le titre restait à 80 px et
   occupait 746 px de haut sur un écran de 900, alors que la règle « corrigée »
   était bien dans le fichier. C'est la deuxième fois dans ce fichier qu'une
   surcharge trop courte passe inaperçue : quand on annule une règle, on repart
   TOUJOURS de son sélecteur. */
body.v2 section .aw-colle-titre h2 {
  font-size: clamp(1.9rem, 3.2vw, 3.1rem);
  max-width: 15ch;
  margin-bottom: 20px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   L'ALTERNANCE — demandée par Matt le 2026-08-11
   ───────────────────────────────────────────────────────────────────────────
   Une section sur deux inverse le sens de lecture, et chaque section fait
   entrer ses blocs par un côté différent. C'est ce qui empêche 10 000 px de
   page de se lire comme une seule longue colonne.

   ⚠️ ON INVERSE PAR `order`, JAMAIS EN ÉCHANGEANT LES BLOCS DANS LE HTML.
   Le titre doit rester AVANT son contenu dans le document : c'est l'ordre que
   suivent un lecteur d'écran et la navigation au clavier. Un titre annoncé
   après les six cartes qu'il introduit rend la section incompréhensible sans
   les yeux, alors que rien ne change à l'écran.
   ═══════════════════════════════════════════════════════════════════════════ */
.aw-titre-droite .aw-colle-titre { order: 2; }
.aw-titre-droite .aw-colle-corps { order: 1; }
.aw-titre-droite.aw-colle .aw-colle-duo {
  grid-template-columns: minmax(0, 1.18fr) minmax(0, .82fr);
}

/* ── PAR OÙ LES BLOCS ARRIVENT ────────────────────────────────────────────
   Le CSS pose la position de DÉPART, GSAP ramène à zéro (voir la cascade dans
   le script de la page, qui remet x ET y à 0).
   ⚠️ Ces règles remplacent le `translateY(28px)` de `.js-reveal` : elles
   doivent donc être plus spécifiques que lui, d'où `body.v2` en tête. */
body.v2 .aw-entree-bas    .aw-colle-corps .js-reveal { transform: translateY(64px);  }
body.v2 .aw-entree-haut   .aw-colle-corps .js-reveal { transform: translateY(-64px); }
body.v2 .aw-entree-droite .aw-colle-corps .js-reveal { transform: translateX(80px);  }
body.v2 .aw-entree-gauche .aw-colle-corps .js-reveal { transform: translateX(-80px); }

/* ⚠️ SUR TÉLÉPHONE, TOUT ARRIVE PAR LE BAS. Une entrée latérale de 80 px sur
   un écran de 390 px pousse le bloc hors du cadre : le navigateur ajoute une
   barre de défilement horizontale, et la page se lit en biais. Mesuré comme
   défaut bloquant par le contrôle « rien ne déborde ». */
@media (max-width: 900px) {
  body.v2 .aw-entree-droite .aw-colle-corps .js-reveal,
  body.v2 .aw-entree-gauche .aw-colle-corps .js-reveal { transform: translateY(48px); }
  .aw-titre-droite .aw-colle-titre,
  .aw-titre-droite .aw-colle-corps { order: 0; }
}

/* ═══ LA SECTION DES MÉTIERS COLLE SON TITRE AU BORD DE L'ÉCRAN ════════════
   Matt le 2026-08-11 : « dans la section votre métier, mets le titre le plus à
   gauche possible qu'il y ait plus d'espace pour voir les cartes métier ».

   Les autres sections gardent la colonne centrée à 1180 px — c'est la mise en
   page validée. Celle-ci en sort : son `.wrap` prend toute la largeur et le
   titre se pose à ~28 px du bord. Les 130 px de marge gauche et la colonne de
   titre rétrécie repartent aux cartes.

   ⚠️ LA COURSE DU RUBAN EST RECALCULÉE TOUTE SEULE. Elle vient de
   `ruban.scrollWidth - cadre.clientWidth`, mesurés à chaud : élargir le cadre
   raccourcit mécaniquement la course, sans qu'aucun chiffre soit à corriger
   ici. C'est la raison pour laquelle rien n'est écrit en dur dans le script.
   ═══════════════════════════════════════════════════════════════════════════ */
.aw-parcours > .wrap.aw-colle-duo {
  max-width: none;
  width: auto;
  margin-left: clamp(18px, 2vw, 30px);
  margin-right: 0;
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  gap: clamp(20px, 2.2vw, 34px);
}
/* Dans 300 px, le titre de section doit descendre d'un cran, sinon « Chaque
   métier a ses propres besoins » part sur six lignes et reprend en hauteur ce
   qu'on vient de gagner en largeur. */
body.v2 .aw-parcours .aw-colle-titre h2 { font-size: clamp(1.5rem, 2vw, 2rem); }
body.v2 .aw-parcours .aw-colle-titre .section-intro { font-size: .96rem; }
/* Sous 1100 px, la colonne unique reprend la main : un titre de 300 px collé
   au bord n'aurait plus de cartes à côté de lui. */
@media (max-width: 1100px) {
  .aw-parcours > .wrap.aw-colle-duo {
    margin-left: auto;
    margin-right: auto;
    max-width: 1180px;
  }
}

/* ── LES GRILLES DANS LA COLONNE DE DROITE ────────────────────────────────
   La colonne fait environ 640 px sur un écran de 1440. Les grilles d'origine
   sont à 3 colonnes : trois cartes de 200 px de large, avec un prix de
   3,4 rem dedans, ça ne tient pas — le texte se casse mot par mot.
   Elles passent donc en COLONNE, ce qui est exactement le motif que Matt a
   validé : les blocs descendent pendant que le titre reste. */
body.v2 .aw-colle-corps .price-grid,
body.v2 .aw-colle-corps .option-grid,
body.v2 .aw-colle-corps .aw-grille {
  grid-template-columns: 1fr;
  gap: 22px;
}
/* ⚠️ EXCEPTION : LES 14 MÉTIERS RESTENT SUR DEUX COLONNES.
   Une seule colonne ferait 14 cartes de 200 px empilées, soit près de 3 000 px
   à parcourir avant même de voir un prix — on abandonne avant d'avoir choisi.
   Deux colonnes coupent la hauteur de moitié et la grille reste lisible : ce
   sont des boutons de sélection, pas un récit. */
body.v2 .aw-colle-corps .metier-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
}
/* Les cartes métier restent GROSSES : deux colonnes les élargissent, il ne
   faut pas en profiter pour les aplatir. Mesuré le 2026-08-11 : à
   `min-height: 0` elles perdaient en hauteur ce qu'elles gagnaient en largeur,
   et le contrôle « plus grosse qu'avant » tombait à +6 % — sous le seuil. */
body.v2 .aw-colle-corps .metier-card { min-height: 190px; padding: 26px 28px; }
@media (max-width: 1180px) {
  body.v2 .aw-colle-corps .metier-grid { grid-template-columns: 1fr; }
}
/* ═══ LES TROIS FORMULES SUR UNE SEULE LIGNE ═══════════════════════════════
   Matt le 2026-08-11 : « les cartes tarif sont beaucoup trop grosses, je veux
   que les 3 tiennent sur la même ligne. »

   Trois cartes dans la colonne de droite standard (640 px) feraient 200 px
   chacune : le montant à 3,4 rem se casse en morceaux et la liste des
   prestations passe à un mot par ligne. La section des tarifs reçoit donc une
   colonne de titre BEAUCOUP plus étroite — le titre y tient sur quatre lignes
   courtes, et les formules récupèrent 860 px, soit 275 px par carte.

   ⚠️ Sous 1200 px, on repasse en colonne : trois cartes de 230 px, ce n'est
   plus « une ligne », c'est trois colonnes illisibles. */
.aw-tarifs-ligne.aw-colle .aw-colle-duo {
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
}
.aw-tarifs-ligne.aw-titre-droite.aw-colle .aw-colle-duo {
  grid-template-columns: minmax(0, 1fr) minmax(0, 300px);
}
body.v2 .aw-tarifs-ligne .aw-colle-corps .price-grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}
/* Les cartes redeviennent compactes : à 275 px de large, le rembourrage de
   46 px ne laissait plus que 180 px de texte utile. */
body.v2 .aw-tarifs-ligne .aw-colle-corps .price-card { padding: 30px 22px; }
body.v2 .aw-tarifs-ligne .aw-colle-corps .price-card h3      { font-size: 1.18rem; }
body.v2 .aw-tarifs-ligne .aw-colle-corps .price-card .amount { font-size: 2.5rem; }
body.v2 .aw-tarifs-ligne .aw-colle-corps .price-card li      { font-size: .88rem; padding: 9px 0; }
body.v2 .aw-tarifs-ligne .aw-colle-titre h2 { font-size: clamp(1.6rem, 2.2vw, 2.2rem); }

@media (max-width: 1200px) {
  .aw-tarifs-ligne.aw-colle .aw-colle-duo,
  .aw-tarifs-ligne.aw-titre-droite.aw-colle .aw-colle-duo { grid-template-columns: 1fr; }
  body.v2 .aw-tarifs-ligne .aw-colle-corps .price-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 860px) {
  body.v2 .aw-tarifs-ligne .aw-colle-corps .price-grid { grid-template-columns: 1fr; }
}

/* La grille à deux colonnes des formules (les métiers qui n'en ont que deux)
   garde ses deux colonnes ici — elles tiennent largement sur une ligne. */
body.v2 .aw-tarifs-ligne .aw-colle-corps .price-grid.deux {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  max-width: none;
}

/* Le champ de recherche et le bandeau d'essai vivent dans la colonne : ils ne
   doivent pas garder la largeur qu'ils avaient en pleine page. */
body.v2 .aw-colle-corps .metier-recherche { max-width: none; }

/* Le contact était centré en pleine largeur ; à droite d'un titre, il se lit
   aligné à gauche comme le reste. */
body.v2 .aw-colle.contact { text-align: left; }
body.v2 .aw-colle.contact .wrap { max-width: 1180px; }
body.v2 .aw-colle.contact h2 { margin-left: 0; margin-right: 0; text-align: left; }
body.v2 .aw-colle-corps .contact-info { grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 0 0 28px; }
/* ⚠️ LA DEUXIÈME LIGNE NE DOIT PAS TRAVERSER LA PREMIÈRE — photo de Matt,
   2026-08-11 : la carte « Zone » se posait sur la carte « Téléphone ».
   Mesuré : elle portait `translateY(-63,75 px)`. La section contact entre
   « par le haut » (cycle d'alternance) ; sur une grille d'UNE ligne c'est
   joli, mais celle-ci en a DEUX — et une entrée par le haut fait passer la
   ligne du bas par-dessus celle du haut, à chaque chargement.
   Les quatre cartes gardent leur apparition en fondu (c'est GSAP qui anime
   l'opacité), elles ne se croisent simplement plus. Le défaut ne se voyait
   qu'en arrivant PAR LE BAS de la page : par le haut, tout était en place —
   c'est pour ça qu'aucune capture ne l'avait montré avant la sienne. */
body.v2 .aw-entree-haut .aw-colle-corps .contact-info .js-reveal,
body.v2 .aw-entree-bas  .aw-colle-corps .contact-info .js-reveal { transform: none; }
@media (max-width: 620px) {
  body.v2 .aw-colle-corps .contact-info { grid-template-columns: 1fr; }
}
/* En colonne, les cartes se suivent au lieu de former une grille : c'est ce
   défilement vertical qui donne au titre collé quelque chose à accompagner. */
.aw-grille--colonne { grid-template-columns: 1fr; }

@media (max-width: 1000px) {
  .aw-colle-duo { grid-template-columns: 1fr; }
  /* Collé sur un téléphone, le titre mangerait la moitié de l'écran pendant
     tout le défilement. On le laisse simplement défiler. */
  .aw-colle-titre { position: static; }
  .aw-grille--colonne { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }

  /* ⚠️ TROISIÈME FOIS DANS CE FICHIER — mesuré sur téléphone le 2026-08-12.
     La ligne ci-dessus, `.aw-colle-duo` (0,1,0), NE SUFFIT PAS : deux sections
     posent leurs colonnes avec un sélecteur plus fort, qui gagne donc à toutes
     les largeurs.
       .aw-parcours > .wrap.aw-colle-duo        (0,3,0)  ligne ~1094
       .aw-titre-droite.aw-colle .aw-colle-duo  (0,3,0)  ligne ~1055
     Conséquence MESURÉE sur iPhone 15 : la colonne de titre gardait ses 300 px
     sur un écran de 393, et il restait **33 px** pour le ruban des 14 métiers.
     Les cartes tombaient à 58 px de large — illisibles. Rien ne se voyait au
     contrôle « rien ne déborde » parce que `body` porte `overflow-x: clip` :
     le dégât était CACHÉ, pas absent.
     La leçon est écrite deux fois plus haut (lignes ~1027 et ~1102) et elle
     s'applique encore ici : QUAND ON ANNULE UNE RÈGLE, ON REPART DE SON
     SÉLECTEUR — jamais d'une version raccourcie. */
  .aw-parcours > .wrap.aw-colle-duo,
  .aw-titre-droite.aw-colle .aw-colle-duo {
    grid-template-columns: 1fr;
  }
  /* Le titre du parcours retrouve la taille commune : les 300 px de colonne qui
     justifiaient de le réduire n'existent plus. */
  body.v2 .aw-parcours .aw-colle-titre h2 { font-size: clamp(2rem, 5.4vw, 2.6rem); }
}


/* ═══════════════════════════════════════════════════════════════════════════
   12 bis. LE RUBAN DES MÉTIERS — deux lignes qui glissent au scroll
   ───────────────────────────────────────────────────────────────────────────
   Matt le 2026-08-11 : « les cartes métier, qu'elles défilent horizontalement
   de droite à gauche, toujours au scroll, sur deux ou trois lignes ». Il a
   choisi deux lignes de sept et la mécanique « la page se fige, le ruban
   glisse, puis la page repart ».

   ⚠️ CE QUI BOUGE N'EST PAS CE QUI SE CLIQUE — la règle du fichier tient
   toujours. On ne fige PAS les cartes, on ne les anime pas non plus : c'est le
   RUBAN, leur parent commun, qui se déplace. Les cartes gardent une position
   fixe dans le ruban, et surtout elles cessent totalement de bouger dès que le
   défilement s'arrête. Sans ça, Playwright refuserait de cliquer (« element is
   not stable ») et le visiteur raterait sa cible — le même défaut que le
   `scale: 1.012` du 11/08.

   ⚠️ `transform` ET PAS `left` NI `margin` : une transformation ne demande
   aucun recalcul de mise en page. À 14 cartes avec un flou d'arrière-plan
   chacune, l'écart se mesure directement en images par seconde.

   ⚠️ LES TROIS MESURES SONT ÉCRITES PAR LE SCRIPT (`rubanDesMetiers`) : la
   course dépend de la largeur réelle du ruban et de la fenêtre, elle ne peut
   pas être écrite en dur. Les replis ci-dessous donnent une page correcte,
   simplement sans glissement, si le script ne tourne pas.
   ═══════════════════════════════════════════════════════════════════════════ */
.metier-piste {
  height: var(--piste-h, auto);
  position: relative;
}
.metier-cadre {
  position: sticky;
  /* ⚠️ LE RUBAN EST CENTRÉ DANS L'ÉCRAN, PAS COLLÉ SOUS LA BARRE.
     Matt le 2026-08-11, photo à l'appui : « le bandeau est trop en bas, je le
     veux centré ». Il était calé à 150 px comme le titre — mais le titre fait
     300 px de haut et le ruban près de 700 : aligner leurs bords HAUTS les
     décentre tous les deux. On centre donc le ruban dans la hauteur utile
     (l'écran moins la barre de 95 px), et le script vise la même ligne quand
     il pilote le glissement. */
  /* ⚠️ LA LIGNE DE COLLAGE EST ÉCRITE PAR LE SCRIPT, EN PIXELS.
     Matt : « le bandeau est trop en bas, je le veux centré ». Le centrage
     dépend de la hauteur réelle de la grille — première version : le script
     posait une variable et le CSS faisait le calcul. Mesuré : la variable ne
     valait pas ce qu'on croyait au moment du rendu, le plancher se déclenchait
     et le ruban se collait à 6 px du haut. Deux endroits pour un seul calcul,
     c'est un endroit de trop : le script mesure et pose la valeur finale.
     Ce 132 px n'est qu'un repli, pour que la page reste correcte sans script. */
  top: 132px;
  /* ⚠️ `clip` sur X et `visible` sur Y : les cartes ont un halo et une ombre
     portée qui débordent vers le haut et le bas. Avec `hidden` sur les deux
     axes ils seraient tranchés net, et la grille aurait l'air posée dans une
     boîte. `overflow-x: clip` est le seul couple qui laisse Y libre — avec
     `hidden`, le navigateur force l'autre axe à `auto` et fabrique une barre
     de défilement. */
  overflow-x: clip;
  overflow-y: visible;
  /* ⚠️ LES CARTES S'ESTOMPENT, ELLES NE SONT PLUS TRANCHÉES — 2026-08-11,
     photo de Matt : « quand les cartes disparaissent, que ce soit plus joli,
     pas aussi brusque, et qu'elles disparaissent en entier ». Mesuré : quatre
     cartes étaient coupées par le bord du cadre, dont deux en plein milieu
     (41 % visibles) — un trait net en travers d'un mot.
     Un masque en dégradé remplace la coupe : la carte perd sa matière
     progressivement et a fini de disparaître AVANT d'atteindre le bord. Le
     fondu de gauche vaut une carte entière plus son écart (340 px), donc
     aucune carte ne peut être tranchée ; celui de droite est plus court, les
     cartes doivent se deviner en arrivant.
     ⚠️ `mask` COMPOSE AVEC `overflow: clip`, il ne le remplace pas : au-delà
     du dégradé, la découpe reste nécessaire pour que rien ne déborde de la
     fenêtre. Et il est purement visuel — les cartes masquées gardent leur
     position, c'est le rattrapage au clavier qui les rend atteignables. */
  /* ⚠️ LE FONDU SUIT LA COURSE, IL N'EST PAS CONSTANT — corrigé le 11/08 sur
     photo de Matt : « on ne voit pas les deux cartes, le scrolling commence là
     et elles disparaissent vite ». Un fondu fixe de 340 px effaçait la
     PREMIÈRE colonne alors que le ruban n'avait pas encore bougé d'un pixel :
     deux métiers sur quatorze étaient illisibles d'entrée.
     Le script fait grandir `--fondu-g` de 0 à 340 px au fur et à mesure du
     glissement, et fondre `--fondu-d` à zéro quand on arrive au bout. On
     n'estompe donc que ce qui est réellement en train de sortir. */
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--fondu-g, 0px), #000 calc(100% - var(--fondu-d, 0px)), transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 var(--fondu-g, 0px), #000 calc(100% - var(--fondu-d, 0px)), transparent 100%);
  /* Le ruban va jusqu'au bord droit de la fenêtre : les cartes sortent de
     l'écran au lieu de disparaître au milieu de la colonne. La valeur est
     mesurée par le script — `100vw` mentirait de la largeur de la barre de
     défilement et fabriquerait une barre horizontale. */
  margin-right: calc(-1 * var(--ruban-fuite, 0px));
  /* ⚠️ CE REMBOURRAGE EST LA PLACE DU ZOOM AU SURVOL, pas de la décoration.
     Vu en capture le 2026-08-11 : à ×1,5, une carte de 334 px en réclame 167
     de plus, soit ~84 de chaque côté — et son bord supérieur était tranché net
     par le haut du cadre. Les 14 px d'origine ne servaient qu'aux halos.
     Les marges négatives rendent à la page la hauteur qu'on vient de prendre :
     sans elles, la section gagnerait 150 px de vide. */
  padding: 92px 0;
  margin-block: -78px;
}
.metier-ruban {
  transform: translate3d(calc(-1 * var(--ruban-x, 0px)), 0, 0);
  will-change: transform;
}

/* Deux rangées, sept colonnes : `column` remplit d'abord la colonne, donc les
   14 métiers tombent naturellement en 2 × 7 sans qu'on ait à les découper. */
/* ⚠️ `.aw-colle-corps` DANS LE SÉLECTEUR, ET CE N'EST PAS DU BAVARDAGE.
   La règle « les 14 métiers restent sur deux colonnes » plus haut s'écrit
   `body.v2 .aw-colle-corps .metier-grid` et vaut (0,3,0) — exactement autant
   que `body.v2 .metier-ruban .metier-grid`. À égalité, c'est la dernière
   écrite qui gagne : le ruban marcherait par accident d'ordre, et tomberait le
   jour où quelqu'un déplace un bloc. Cinquième fois que ce piège se présente
   dans ce fichier ; on sépare les cas au lieu de surenchérir.
   ⚠️ `grid-template-columns: none` est OBLIGATOIRE : la règle d'origine pose
   deux colonnes explicites. Avec `grid-auto-flow: column`, les cartes
   rempliraient d'abord ces deux colonnes-là et le ruban serait bancal. */
body.v2 .aw-colle-corps .metier-ruban .metier-grid {
  display: grid;
  grid-auto-flow: column;
  grid-template-columns: none;
  grid-template-rows: repeat(2, minmax(0, 1fr));
  grid-auto-columns: var(--metier-large, 320px);
  gap: 20px;
  width: max-content;
  /* ⚠️ CETTE RESPIRATION EST DE LA COURSE, PAS DE LA DÉCORATION — et elle est
     posée sur la GRILLE, jamais sur le ruban. Sur le ruban, deux choses
     échouaient : Chrome n'inclut pas le `padding-right` d'un bloc dans son
     `scrollWidth` (la course ne bougeait pas d'un pixel), et en mode ouvert
     les 210 px se retranchaient de la colonne — la carte du métier tombait de
     391 à 181 px de large. Sur la grille en `max-content`, le rembourrage
     entre dans sa largeur, donc dans la course.
     Ce qu'elle règle : sans elle, la dernière colonne s'arrête à 320 px du
     bord ; une carte survolée y grandit de moitié (480 px) et ressort de
     l'écran, sans que le ruban puisse aller plus loin — il est au bout. */
  padding-right: 210px;
  /* ⚠️ CES 20 PX SONT LA RÉSERVE DE L'AIMANT, pas une marge décorative —
     mesuré le 2026-08-11 (7ᵉ passe). `js/magnetique.js` tire chaque carte
     jusqu'à 15 px vers le curseur (`MAX = 15`). La première carte commençait à
     6 px du bord du cadre : survolée, elle en ressortait de 2 px et le cadre
     la rognait. Aucun ancrage de zoom ne peut rattraper ça — quand la carte
     est déjà au bord, il n'y a plus rien à décaler. Il faut de la place.
     Ces 20 px entrent dans la course (la grille est en `max-content`), donc
     rien d'autre n'est à corriger. */
  padding-left: 20px;
}
/* Dans le ruban, une carte n'a plus à remplir une colonne large : elle a sa
   largeur propre, et la hauteur suit son contenu. */
body.v2 .aw-colle-corps .metier-ruban .metier-card { min-height: 210px; }

/* ═══ LA QUINZIÈME CARTE — « un métier qui n'est pas dans la liste » ═══════
   Matt le 2026-08-11 : « si le métier n'est pas annoncé on peut également le
   faire, juste à nous contacter, un truc plus pro ».

   Elle doit se lire comme une carte du ruban — même taille, même place, même
   agrandissement au survol — et se distinguer assez pour qu'on comprenne
   qu'elle ne se choisit pas : elle emmène ailleurs. D'où le trait discontinu
   (une case à remplir, pas une case à cocher) et la teinte turquoise, celle
   qui sert déjà de « on vous répond » dans le contact.

   ⚠️ ELLE GARDE LA CLASSE `.metier-card` EXPRÈS : tout ce qui a été mesuré sur
   les cartes du ruban (l'ancrage du zoom, le clic Playwright, la réserve de
   l'aimant, le masque des bords) vaut alors pour elle sans une ligne de plus.
   Lui donner un style à part aurait signifié re-prouver tout ça. */
body.v2 .aw-colle-corps .metier-ruban .metier-appel {
  border-style: dashed;
  border-color: rgba(111,245,221,.5);
  /* ⚠️ FOND LARGEMENT OPAQUE — vu en capture le 2026-08-11 : à .22/.55, la
     carte ressortait GRIS CLAIR au milieu de treize cartes bleu nuit. Ces
     cartes sont en verre (`backdrop-filter`) : un fond trop transparent ne
     colore pas, il laisse remonter le flou de ce qu'il y a derrière. */
  background: linear-gradient(160deg, rgba(15,122,104,.34), rgba(9,14,22,.94));
  text-decoration: none;
  /* Quinze cartes sur deux rangées laissent la dernière colonne à moitié vide.
     Celle-ci la prend en entier : le ruban se termine sur un bloc plein, et
     l'invitation à écrire a le poids d'une conclusion, pas d'un reste. */
  grid-row: span 2;
  justify-content: center;
}
/* ⚠️ ELLE NE ZOOME PAS, ET C'EST LA CONSÉQUENCE DIRECTE DU `span 2`.
   Sur deux rangées elle fait ~700 px : agrandie de moitié, elle en réclamerait
   1 050 dans un écran qui n'en offre que 900 — aucun ancrage ne peut rattraper
   ça, la carte serait forcément coupée. Elle n'a d'ailleurs rien à révéler :
   tout son texte est déjà lisible au repos. Seule la lumière répond au survol,
   comme sur les cartes qui ne se cliquent pas. */
body.v2 .aw-colle-corps .metier-ruban .metier-grid:not(.grille-ouverte) .metier-appel:hover,
body.v2 .aw-colle-corps .metier-ruban .metier-grid:not(.grille-ouverte) .metier-appel:focus-visible {
  scale: 1 !important;
  border-color: #6ff5dd;
  border-style: solid;
}
body.v2 .metier-appel .metier-nom { color: #8ff8e4; }
body.v2 .metier-appel .metier-appel-lien {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: .78rem;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: #6ff5dd;
}
/* La flèche est posée par le CSS et pas écrite dans le texte : un lecteur
   d'écran annoncerait « flèche vers la droite » au milieu d'une phrase. */
body.v2 .metier-appel .metier-appel-lien::after {
  content: '→';
  transition: transform .2s ease-out;
}
body.v2 .metier-appel:hover .metier-appel-lien::after { transform: translateX(4px); }

/* ⚠️ LE ZOOM DU SURVOL S'ANCRE OÙ IL PEUT, PAS TOUJOURS AU CENTRE — corrigé le
   2026-08-11 (7ᵉ passe), photo de Matt : « la carte restaurant zoome bien mais
   elle est cachée ». Mesuré : agrandie de moitié, elle débordait de 140 px à
   gauche du cadre, qui la tranchait net (`overflow-x: clip`).
   Le cadre ne peut pas s'élargir — le titre collé est juste à sa gauche. C'est
   donc le point fixe de l'agrandissement qui se déplace : le script écrit
   `--zoom-ancre` (en pixels depuis le bord gauche de la carte) de façon que la
   carte agrandie tienne entièrement dans la partie NON estompée du cadre.
   50 % est le repli : sans script, on retrouve exactement le comportement
   d'avant, centre compris.
   ⚠️ `transform-origin` était libre sur ces cartes — `translate`, `transform`,
   `::before` et `::after` ne le sont pas (voir l'inventaire en tête de
   fichier). Vérifié avant d'écrire cette ligne. */
body.v2 .aw-colle-corps .metier-ruban .metier-card {
  transform-origin: var(--zoom-ancre, 50%) var(--zoom-ancre-y, 50%);
}

/* ═══ LE SURVOL D'UNE CARTE MÉTIER — néon vivant et agrandissement ═════════
   Matt le 2026-08-11 : « quand tu passes la souris dessus, je veux que les
   bords deviennent néon, que la lumière bouge, que le texte prenne de la
   couleur, et que la carte grossisse ×1,5 juste en passant la souris, pour
   mieux voir les infos de la carte sélectionnée ».

   ⚠️ CECI EST UNE EXCEPTION ASSUMÉE À LA RÈGLE « UNE CARTE-BOUTON N'ANIME
   RIEN DE GÉOMÉTRIQUE ». Cette règle est née d'une respiration PERMANENTE
   (`scale: 1.012` en boucle) : la cible bougeait sans arrêt, Playwright
   refusait de cliquer et le défaut valait aussi pour un humain. Ici, le zoom
   est DÉCLENCHÉ par le survol et se pose en 0,3 s ; sous le curseur immobile,
   la carte l'est aussi. C'est exactement ce qui marche déjà sur les trois
   cartes du hero. Le contrôle « un VRAI clic aboutit » garde la porte — s'il
   tombe un jour, c'est ce bloc qu'il faut revoir en premier.

   ⚠️ NI `::before`, NI `::after`, NI `translate` : les deux premiers portent
   le halo et la bordure de `css/wow.css`, le troisième les boutons aimantés
   de `js/magnetique.js`. Écrire dessus efface leur effet EN SILENCE. Il reste
   `scale`, `border-color`, `box-shadow` et `animation` — c'est avec ça que
   tout ce qui suit est fait.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ⚠️ CETTE ANIMATION A ÉTÉ RETIRÉE DU SURVOL LE 2026-08-11 — Matt : « le cadre
   de la carte ne bouge pas mais le texte vibre, on dirait un bug ».
   Elle changeait `box-shadow` et `border-color` à CHAQUE image. Or repeindre
   une carte oblige le navigateur à redessiner son texte — et un texte mis à
   l'échelle 1,5 se redessine avec un arrondi sous-pixel différent à chaque
   fois. Résultat : un cadre parfaitement immobile et des lettres qui
   grésillent. Le halo respirait bien ; c'est le texte qui payait.
   Elle reste définie ici, sans emploi : la remettre un jour supposerait de la
   porter sur un élément SANS texte — ce qui demande un pseudo-élément libre,
   et `::before` comme `::after` sont pris par css/wow.css. */
@keyframes awNeonVivant {
  0%, 100% {
    border-color: rgba(255,138,82,.95);
    box-shadow: 0 0 0 1px rgba(255,138,82,.5),
                0 0 20px rgba(255,138,82,.55),
                0 0 52px rgba(255,106,61,.3),
                0 26px 60px -22px rgba(0,0,0,.85);
  }
  50% {
    border-color: rgba(111,245,221,.95);
    box-shadow: 0 0 0 1px rgba(111,245,221,.55),
                0 0 30px rgba(111,245,221,.6),
                0 0 74px rgba(47,224,194,.34),
                0 26px 60px -22px rgba(0,0,0,.85);
  }
}

/* ⚠️⚠️ LE SEUL `!important` DU FICHIER, ET IL EST MÉRITÉ — mesuré le
   2026-08-11. GSAP révèle chaque bloc `.js-reveal` et laisse derrière lui un
   style INLINE : `transform: translate(0px,0px); translate: none; rotate:
   none; scale: none`. Un style inline bat toute règle CSS, quelle que soit sa
   spécificité — mon `scale: 1.5` était donc ignoré, sans la moindre erreur, et
   la carte gardait sa taille pendant que le reste du survol fonctionnait.
   DÉCOUVERTE AU PASSAGE : le `scale: 1.035` du survol général (bloc 17) était
   mort de la même façon sur TOUTES les cartes révélées par GSAP. Personne ne
   l'avait vu : 3,5 % ne se remarquent pas quand ils manquent.
   Surenchérir en spécificité ne sert à rien contre un style inline ; seul
   `!important` passe devant. La vraie alternative serait un `clearProps` dans
   le script de révélation — il vit dans `agence-web.html`, qui doit rester
   intacte. À revoir le jour où cette page sera reprise.

   ⚠️ `:not(.grille-ouverte)` : sans lui, survoler une carte pendant que le
   parcours s'ouvre la figerait à 1,5 en `!important` et l'aspiration des
   treize autres (`scale: .35`) ne pourrait plus la ramener. */
body.v2 .aw-colle-corps .metier-ruban .metier-grid:not(.grille-ouverte) .metier-card:hover,
body.v2 .aw-colle-corps .metier-ruban .metier-grid:not(.grille-ouverte) .metier-card:focus-visible {
  scale: 1.5 !important;
  z-index: 30;                 /* elle passe devant ses voisines, pas dessous */
  /* ⚠️ `animation: none` ET PAS `animation-play-state: paused` — mesuré le
     2026-08-11, faux vert à l'appui. Les cartes portent en permanence
     `awHaloOrange` (bloc 6), que le survol se contentait de METTRE EN PAUSE.
     Or une animation en pause continue d'imposer la valeur où on l'a arrêtée,
     et une animation bat toujours une déclaration normale : ma bordure néon
     était ignorée en silence. Le relevé affichait
     `rgba(255,147,101,.604) → rgba(255,148,102,.61)` — deux teintes
     identiques à un point près, et le contrôle passait au vert.
     On coupe donc l'animation pour reprendre la main. La carte se fige
     toujours quand on la vise, ce qui était le but de la pause. */
  animation: none;
  /* La lumière bouge À L'ARRIVÉE — la bordure et le halo montent en 0,45 s
     avec l'agrandissement, puis se posent. Un mouvement qu'on voit, sans le
     repaint permanent qui faisait grésiller le texte. */
  border-color: rgba(111,245,221,.95);
  box-shadow: 0 0 0 1px rgba(111,245,221,.5),
              0 0 26px rgba(111,245,221,.55),
              0 0 66px rgba(47,224,194,.3),
              0 26px 60px -22px rgba(0,0,0,.85);
  /* ⚠️ La carte prend sa propre couche pendant le survol : le texte est
     rastérisé une fois, pas à chaque image. Sans ça, même une transition
     unique fait tressauter les lettres pendant sa durée. */
  will-change: transform, scale;
  /* ⚠️ ELLE DEVIENT OPAQUE, ET C'EST INDISPENSABLE — Matt, photo à l'appui :
     « que les cartes qui zooment ne soient pas transparentes ». Les cartes
     sont en verre : au repos, sur un fond sombre et uni, ça se voit à peine.
     Agrandie de moitié, la carte recouvre ses voisines — et on lisait le titre
     du dessous À TRAVERS celui du dessus. Un fond plein, plus le flou
     d'arrière-plan qui reste pour l'effet de matière. */
  background: linear-gradient(168deg, #241a2e 0%, #17131f 52%, #1d1526 100%);
  /* ⚠️ ET C'EST LUI QUI FAISAIT VIBRER LE TEXTE — mesuré le 2026-08-11, huit
     captures de la même carte immobile : huit rendus différents.
     `backdrop-filter` re-échantillonne ce qu'il y a DERRIÈRE la carte à chaque
     image. Or derrière, il y a un canvas animé qui ne s'arrête jamais : la
     carte était donc repeinte 60 fois par seconde, et son texte — agrandi de
     moitié — se redessinait à chaque fois avec un arrondi sous-pixel
     différent. Le cadre ne bougeait pas d'un pixel ; les lettres grésillaient.
     Une fois la carte opaque, ce filtre ne montre plus rien : il ne coûtait
     que le défaut. */
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
/* ⚠️ LE TEXTE GRANDIT AVEC LA CARTE, ON NE LE COMPENSE PAS. Même leçon que
   pour les cartes du hero : réduire les tailles au survol donnait une grande
   carte au texte minuscule. « Pour mieux voir les infos » veut dire plus
   grand, pas plus dense. */
body.v2 .aw-colle-corps .metier-ruban .metier-card:hover .metier-promesse,
body.v2 .aw-colle-corps .metier-ruban .metier-card:focus-visible .metier-promesse {
  opacity: 1;
  color: rgba(255,225,205,.96);
}
body.v2 .aw-colle-corps .metier-ruban .metier-card:hover .metier-meta,
body.v2 .aw-colle-corps .metier-ruban .metier-card:focus-visible .metier-meta {
  color: var(--aw-teal-vif, #6ff5dd);
  opacity: 1;
}
/* Le filet au-dessus du nom s'allume avec le reste. */
body.v2 .aw-colle-corps .metier-ruban .metier-card:hover .metier-nom,
body.v2 .aw-colle-corps .metier-ruban .metier-card:focus-visible .metier-nom {
  border-top-color: rgba(111,245,221,.8);
}

/* Mouvement réduit : le néon cesse de respirer et la carte grandit moins —
   l'information reste accessible, l'effet ne s'impose plus. */
/* Les deux garde-fous doivent porter le même `!important`, pour la même
   raison : ils luttent contre le même style inline. Sans lui, ils seraient
   les seules règles à ne pas s'appliquer. */
@media (prefers-reduced-motion: reduce) {
  body.v2 .aw-colle-corps .metier-ruban .metier-grid:not(.grille-ouverte) .metier-card:hover,
  body.v2 .aw-colle-corps .metier-ruban .metier-grid:not(.grille-ouverte) .metier-card:focus-visible {
    scale: 1.06 !important;
    border-color: rgba(255,138,82,.95);
  }
}
/* Sur écran tactile, il n'y a pas de survol : l'agrandissement resterait
   collé à la première carte effleurée. */
@media (hover: none) {
  body.v2 .aw-colle-corps .metier-ruban .metier-grid:not(.grille-ouverte) .metier-card:hover {
    scale: none !important;
  }
}

/* ── LE NOM DU MÉTIER, PLUS GROS ET PLUS LISIBLE ──────────────────────────
   Matt le 2026-08-11 : « les titres des cartes métier, plus gros, plus mis en
   valeur, plus lisibles ». Il était à 1,22 rem dans la police de texte, au
   même niveau visuel que la promesse juste en dessous : rien ne disait lequel
   des deux on devait lire en premier.
   Trois leviers, aucun géométrique : la taille, la police de titre (celle de
   toute la page), et un filet de couleur au-dessus qui sépare le nom du reste.
   ⚠️ PAS DE `text-transform: uppercase` : « Hôtel · chambre d'hôtes » en
   capitales dans une carte de 320 px passe à trois lignes et coûte la place
   qu'on vient de gagner. */
body.v2 .aw-colle-corps .metier-ruban .metier-nom {
  font-family: 'Cooper Black', 'Bricolage Grotesque', Georgia, serif;
  font-size: 1.52rem;
  line-height: 1.14;
  letter-spacing: -0.005em;
  margin-bottom: 10px;
  padding-top: 12px;
  border-top: 2px solid rgba(255,148,102,.35);
}
/* La promesse recule d'un cran : c'est la hiérarchie qui rend le nom lisible,
   pas seulement sa taille. */
body.v2 .aw-colle-corps .metier-ruban .metier-promesse {
  font-size: .9rem;
  opacity: .78;
}

/* ── LA JAUGE SOUS LE TITRE COLLÉ ────────────────────────────────────────
   Elle occupe le vide laissé par le titre collé (défaut relevé dans
   A-FAIRE-agence-web-v2.md) et dit où l'on en est dans les 14 métiers. */
.metier-jauge { margin-top: 26px; max-width: 260px; }
.metier-jauge-piste {
  height: 3px;
  border-radius: 999px;
  background: rgba(255,255,255,.14);
  overflow: hidden;
}
.metier-jauge-remplie {
  display: block;
  height: 100%;
  width: var(--jauge, 7%);
  border-radius: 999px;
  background: linear-gradient(90deg, var(--aw-orange-vif, #ff8a52), var(--aw-teal-vif, #6ff5dd));
  box-shadow: 0 0 12px rgba(255,138,82,.6);
  transition: width .12s linear;   /* suit le doigt sans traîner */
}
.metier-jauge-txt {
  margin: 10px 0 0;
  font-family: 'JetBrains Mono', monospace;
  font-size: .7rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  opacity: .66;
}
.metier-jauge-rang { color: var(--aw-orange-vif, #ff8a52); font-weight: 700; }
/* Le titre disparaît quand un métier est ouvert : sa jauge n'aurait plus rien
   à mesurer, et elle est de toute façon emportée avec lui. */
@media (max-width: 900px) { .metier-jauge { display: none; } }

/* ── QUAND UN MÉTIER EST OUVERT, LE RUBAN N'EXISTE PLUS ───────────────────
   Le parcours en trois temps reprend la main : la carte choisie s'étale, les
   autres sont aspirées. Un ruban encore collé et décalé afficherait la carte
   ouverte de travers, et la piste garderait 1 500 px de vide sous elle. */
.aw-parcours.parcours-ouvert .metier-cadre {
  position: static;
  overflow: visible;
  margin-right: 0;
}
.aw-parcours.parcours-ouvert .metier-ruban { transform: none; }
body.v2 .aw-colle-corps .aw-parcours.parcours-ouvert .metier-ruban .metier-grid,
body.v2 .aw-parcours.parcours-ouvert .aw-colle-corps .metier-ruban .metier-grid {
  grid-auto-flow: row;
  grid-template-columns: minmax(0, 1fr);
  grid-auto-columns: auto;
  width: auto;
  padding-right: 0;   /* la course n'a plus lieu d'être : elle volerait la place */
}

/* Sous 900 px il n'y a plus de place pour un ruban : les cartes reprennent
   leur empilement d'origine, comme le titre collé qui redevient statique. */
@media (max-width: 900px) {
  .metier-cadre {
    position: static; overflow: visible; margin-right: 0;
    -webkit-mask-image: none;
            mask-image: none;   /* plus de ruban, donc plus rien à estomper */
  }
  .metier-ruban { transform: none; }
  body.v2 .aw-colle-corps .metier-ruban .metier-grid {
    grid-auto-flow: row;
    grid-template-columns: minmax(0, 1fr);
    grid-auto-columns: auto;
    width: auto;
    /* ⚠️ SANS CES DEUX LIGNES, LE RUBAN DÉPLIÉ RESTE INUTILISABLE — mesuré sur
       iPhone 15 le 2026-08-12. Les deux rembourrages ci-dessus (20 px de
       réserve d'aimant, 210 px de course) n'ont de sens QUE tant que la grille
       glisse horizontalement. Une fois le ruban déplié, ils restent et mangent
       230 px des 353 disponibles : il ne reste que 123 px pour la carte, et
       « Restaurant · café » se lit un mot par ligne.
       Le calcul est exact et rejouable : 353 − 20 − 210 = 123.
       Aucun contrôle ne l'avait vu, parce que rien ne débordait : le défaut
       était une carte étranglée, pas une page trop large. */
    padding-left: 0;
    padding-right: 0;
  }
}
/* Mouvement réduit : le ruban ne glisse pas, on le laisse défiler au doigt.
   Le masque saute aussi — un bord estompé sur une zone qu'on fait défiler soi-
   même cache une carte qu'on cherche à atteindre. */
@media (prefers-reduced-motion: reduce) {
  .metier-cadre {
    overflow-x: auto;
    -webkit-mask-image: none;
            mask-image: none;
  }
  .metier-ruban { transform: none; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   13. LE NOM QUI S'ALLUME — survol et sélection
   ───────────────────────────────────────────────────────────────────────────
   Matt le 2026-08-11 : « que le titre du métier s'allume quand tu cliques et
   quand tu passes dessus, et ça pour toutes les cartes métier, y compris les
   cartes tarif ».
   ⚠️ On allume le TEXTE (couleur + halo), jamais la boîte : une carte-bouton
   n'anime rien de géométrique — c'est la règle du fichier.
   ═══════════════════════════════════════════════════════════════════════════ */
body.v2 .metier-card .metier-nom,
body.v2 .price-card h3 {
  transition: color .25s, text-shadow .25s;
}
body.v2 .metier-card:hover .metier-nom,
body.v2 .metier-card:focus-visible .metier-nom,
body.v2 .metier-card.actif .metier-nom,
body.v2 .price-card:hover h3,
body.v2 .price-card:focus-visible h3,
body.v2 .price-card.selected h3 {
  color: var(--aw-orange-vif, #ff8a52);
  text-shadow: 0 0 10px rgba(255,148,102,.8), 0 0 32px rgba(255,106,61,.5);
}
/* Chaque palier garde SA couleur : allumer le nom d'une carte turquoise en
   orange casserait la lecture des trois formules d'un coup d'œil. */
body.v2 .price-card.tier-teal:hover h3,
body.v2 .price-card.tier-teal.selected h3 {
  color: var(--aw-teal-vif, #6ff5dd);
  text-shadow: 0 0 10px rgba(111,245,221,.8), 0 0 32px rgba(47,224,194,.5);
}
body.v2 .price-card.tier-violet:hover h3,
body.v2 .price-card.tier-violet.selected h3 {
  color: var(--aw-violet-vif, #d7aeff);
  text-shadow: 0 0 10px rgba(215,174,255,.8), 0 0 32px rgba(184,120,255,.5);
}


/* ═══════════════════════════════════════════════════════════════════════════
   14. LA CARTE MÉTIER QUI S'OUVRE EN GRAND
   ───────────────────────────────────────────────────────────────────────────
   Matt le 2026-08-11 : « dans la section métier, quand tu cliques sur une
   carte, je veux qu'elle s'agrandisse le triple pour voir les infos, que le
   reste des cartes soit comme aspiré vers le milieu, et que ça ne dirige pas
   vers les tarifs ».

   ⚠️ ON PEUT REFERMER, et ce n'est pas un détail : une fois les treize autres
   cartes aspirées, il n'existerait plus aucun moyen de changer de métier. Un
   deuxième clic sur la carte ouverte la referme, et la consigne est écrite
   dans la carte elle-même (.metier-refermer). Sans ça, le sélecteur devient
   un cul-de-sac au premier clic.

   ⚠️ `grid-column: 1 / -1` et pas une largeur en pixels : la carte occupe
   toute la grille quelle que soit sa largeur, sans jamais la faire déborder.
   ═══════════════════════════════════════════════════════════════════════════ */
body.v2 .metier-grid .metier-card {
  transition: opacity .45s cubic-bezier(.16,1,.3,1),
              scale .45s cubic-bezier(.16,1,.3,1),
              min-height .45s cubic-bezier(.16,1,.3,1),
              padding .45s;
}

/* Les autres cartes : aspirées vers le centre, puis effacées.
   `scale` et `opacity` seulement — elles gardent leur place dans la grille le
   temps de l'animation, sinon les treize cases se réorganisent d'un coup et
   l'œil perd la carte qu'on vient de choisir. */
body.v2 .metier-grid.grille-ouverte .metier-card:not(.actif) {
  opacity: 0;
  scale: .35;
  pointer-events: none;
}
/* Une fois effacées, elles cessent d'occuper la grille : c'est ce qui laisse
   la carte ouverte prendre toute la place. Le retard vaut la durée de
   l'aspiration — enlever la case AVANT la fin ferait sauter la mise en page. */
body.v2 .metier-grid.grille-vide .metier-card:not(.actif) { display: none; }

body.v2 .metier-grid.grille-ouverte .metier-card.actif {
  grid-column: 1 / -1;
  min-height: 420px;          /* le triple des 140 px d'une carte fermée */
  padding: 40px 44px;
  scale: 1;
}
body.v2 .metier-grid.grille-ouverte .metier-card.actif .metier-nom {
  font-size: clamp(1.6rem, 2.6vw, 2.3rem);
}
body.v2 .metier-grid.grille-ouverte .metier-card.actif .metier-promesse {
  font-size: 1.08rem;
  max-width: 60ch;
}
/* Les puces d'options passent en grille : à 44 puces sur une ligne, la carte
   ouverte serait un mur de texte. */
body.v2 .metier-grid.grille-ouverte .metier-card.actif .metier-puces {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

/* La sortie. Écrite ici et pas ajoutée en HTML : l'éditeur de pages efface les
   éléments neufs qu'il ne reconnaît pas, un bouton ajouté au fichier
   disparaîtrait au premier enregistrement. */
body.v2 .metier-card .metier-refermer { display: none; }
body.v2 .metier-grid.grille-ouverte .metier-card.actif .metier-refermer {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 22px;
  padding: 11px 20px;
  border: 1px solid rgba(255,148,102,.5);
  border-radius: 999px;
  font-family: 'JetBrains Mono', monospace;
  font-size: .7rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--aw-orange-vif, #ff8a52);
  width: fit-content;
}
body.v2 .metier-grid.grille-ouverte .metier-card.actif .metier-refermer:hover {
  background: rgba(255,106,61,.16);
}

@media (prefers-reduced-motion: reduce) {
  body.v2 .metier-grid .metier-card { transition: none; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   15. LA SECTION QUI S'EFFACE AU DERNIER MOMENT
   ───────────────────────────────────────────────────────────────────────────
   Matt le 2026-08-11 : « je voudrais que les sections disparaissent au dernier
   moment pour laisser place à l'autre section ».
   La valeur est écrite par le JS dans --aw-sortie (1 = pleine, 0 = effacée) ;
   ici on ne fait que la consommer. Repli à 1 : si le script ne tourne pas,
   tout reste visible. Une animation qui échoue ne doit jamais effacer le
   contenu.
   ⚠️ `opacity` seule, sans `display` ni `visibility` : la section doit garder
   sa hauteur, sinon la page se réorganise pendant qu'on la parcourt.
   ═══════════════════════════════════════════════════════════════════════════ */
body.v2 main > section {
  opacity: var(--aw-sortie, 1);
  will-change: opacity;
}
@media (prefers-reduced-motion: reduce) {
  body.v2 main > section { opacity: 1 !important; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   16. LA BARRE DU HAUT — plus grosse, et elle répond au survol
   ───────────────────────────────────────────────────────────────────────────
   Matt le 2026-08-11 : « la barre du haut où il y a l'offre etc., je veux plus
   gros, et que la touche réagisse quand je passe la souris ».
   ⚠️ La barre est `position: fixed` : sa hauteur est aussi la marge que
   `scroll-margin-top` doit compenser sur chaque ancre. Les deux valeurs se
   suivent — 118 px de barre, 138 px de marge (voir plus bas). Les désaccorder
   remet les titres derrière la barre.
   ═══════════════════════════════════════════════════════════════════════════ */
body.v2 header { padding: 26px 44px; }
body.v2 nav { gap: 38px; }
body.v2 nav a {
  position: relative;
  font-size: 1.02rem;
  letter-spacing: .04em;
  padding: 10px 2px;      /* la cible fait 44 px de haut, au doigt comme à la souris */
  opacity: .92;
}
/* Le trait qui se déploie sous le lien visé. `scaleX` sur un pseudo-élément :
   aucune réorganisation de la mise en page, donc aucun tremblement du menu. */
body.v2 nav a::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 2px;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--aw-orange, #ff6a3d), var(--aw-violet-vif, #d7aeff));
  box-shadow: 0 0 12px rgba(255,106,61,.8);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .32s cubic-bezier(.16,1,.3,1);
}
body.v2 nav a:hover::after,
body.v2 nav a:focus-visible::after { transform: scaleX(1); }
body.v2 nav a:hover {
  opacity: 1;
  color: var(--aw-orange-vif, #ff8a52);
  text-shadow: 0 0 14px rgba(255,106,61,.6);
}
/* La barre grandit, la marge des ancres suit — sinon les titres repassent
   dessous, ce qui était déjà arrivé le 2026-08-10. */
body.v2 section[id] { scroll-margin-top: 138px; }
body.v2 .recit-ecran, body.v2 .aw-colle-titre { top: 150px; }
@media (max-width: 900px) {
  body.v2 header { padding: 14px 20px; }
  body.v2 section[id] { scroll-margin-top: 84px; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   17. TOUTES LES CARTES RÉAGISSENT — au survol, puis au clic
   ───────────────────────────────────────────────────────────────────────────
   Matt le 2026-08-11 : « toutes les cartes, je veux qu'elles bougent quand tu
   passes la souris dessus, et qu'elles s'agrandissent avec du mouvement quand
   tu cliques dessus ».

   ⚠️ COMMENT ON TIENT ÇA SANS RECRÉER LA CIBLE FUYANTE.
   La règle du fichier reste : une carte-bouton n'anime rien de géométrique
   TANT QU'ON NE L'A PAS VISÉE. Ici le mouvement ne démarre qu'AU SURVOL, donc
   le curseur est déjà dessus quand elle bouge — et il s'agit d'un
   agrandissement CENTRÉ : les bords s'écartent du centre, le curseur ne peut
   pas se retrouver dehors. Aucune translation, jamais : c'est elle qui faisait
   fuir la carte sous le pointeur (note du 2026-08-06).

   ⚠️ `scale` ET PAS `transform: scale()` : `transform` est déjà pris par le
   survol d'origine et par `.wow-incline`. Voir l'inventaire en tête de fichier.
   ═══════════════════════════════════════════════════════════════════════════ */
body.v2 .metier-card,
body.v2 .price-card,
body.v2 .option-card,
body.v2 .aw-carte,
body.v2 .hero-carte,
body.v2 .contact-info a {
  transition: scale .34s cubic-bezier(.16,1,.3,1),
              border-color .3s, box-shadow .35s, background .35s;
}
/* ⚠️ `.hero-carte` A ÉTÉ RETIRÉE DE CETTE LISTE — mesuré le 2026-08-11.
   Les trois cartes du hero ont leur propre survol à `scale: 2` (bloc 9).
   Or `body.v2 .hero-carte:hover` ici et `body.v2 .hero-carte:hover` là-bas ont
   EXACTEMENT la même spécificité (0,3,0) : à égalité, c'est la dernière
   déclarée qui l'emporte, et ce bloc-ci vient après. Le zoom restait donc à
   1,035 au lieu de 2, sans qu'aucune règle ne soit fautive.
   Surenchérir en spécificité aurait marché mais aurait caché le problème :
   deux règles qui se disputent la même propriété sur le même élément. On
   sépare franchement les cas au lieu de les empiler. */
body.v2 .metier-card:hover,
body.v2 .price-card:hover,
body.v2 .option-card:hover,
body.v2 .aw-carte:hover,
body.v2 .contact-info a:hover,
body.v2 .metier-card:focus-visible,
body.v2 .price-card:focus-visible,
body.v2 .option-card:focus-visible {
  scale: 1.035;
  z-index: 3;             /* elle passe au-dessus de ses voisines en grandissant */
}

/* LE CLIC : une pression, puis un rebond. La carte s'enfonce d'abord (0.98),
   puis dépasse sa taille (1.06) avant de se poser. C'est ce dépassement qui
   fait « vivant » — une simple montée linéaire se lit comme un changement
   d'état, pas comme un geste. La classe est posée puis retirée par le JS.
   ⚠️ 420 ms : au-delà, on attend la fin de l'animation avant de voir le
   résultat du clic, et l'interface paraît lente au lieu de vive. */
@keyframes awClic {
  0%   { scale: 1;     }
  22%  { scale: .975;  }
  55%  { scale: 1.06;  }
  100% { scale: 1.035; }
}
body.v2 .aw-clic { animation: awClic .42s cubic-bezier(.16,1,.3,1); }

@media (prefers-reduced-motion: reduce) {
  body.v2 .metier-card, body.v2 .price-card, body.v2 .option-card,
  body.v2 .aw-carte, body.v2 .hero-carte, body.v2 .contact-info a { transition: none; }
  body.v2 .metier-card:hover, body.v2 .price-card:hover, body.v2 .option-card:hover,
  body.v2 .aw-carte:hover, body.v2 .hero-carte:hover { scale: 1; }
  body.v2 .aw-clic { animation: none; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   18. LE PIED DE PAGE — la banderole rétrécie
   ───────────────────────────────────────────────────────────────────────────
   Matt le 2026-08-11 : « la banderole avec des informations, mets-la plus
   petite ». Elle prenait la largeur d'un paragraphe de lecture et deux lignes
   de 0,92 rem, alors qu'elle ne porte qu'une mention légale.
   ═══════════════════════════════════════════════════════════════════════════ */
body.v2 footer { padding: 22px 32px; }
body.v2 .footer-note {
  font-size: .74rem;
  font-weight: 500;
  opacity: .72;
  max-width: 720px;
  margin-bottom: 10px;
  line-height: 1.45;
}

/* Les coordonnées, ajoutées le 2026-08-11 (« il manque à la fin de page mes
   infos »). Elles ne remplacent pas la section contact : elles évitent d'y
   remonter quand on est arrivé tout en bas. */
.pied-infos {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 10px 26px;
  margin: 0 auto 16px;
  max-width: 860px;
  text-transform: none;
  letter-spacing: .01em;
}
.pied-infos a,
.pied-infos span {
  font-family: 'Space Grotesk', sans-serif;
  font-size: .88rem;
  font-weight: 600;
  text-decoration: none;
  padding: 9px 2px;          /* cible de 44 px au doigt */
  opacity: .9;
  transition: color .2s, opacity .2s;
}
.pied-infos a:hover { color: var(--aw-orange-vif, #ff8a52); opacity: 1; }
.pied-infos span { opacity: .62; font-weight: 500; }


/* ═══ LA BANDE NOIRE DU BAS, PLUS FINE ═══════════════════════════════════════
   Matt le 2026-08-11, photo à l'appui : « que tout rentre sur une page, que la
   bande noire en bas soit plus fine ». Mesuré : le pied faisait 304 px — trois
   rangées empilées, chacune avec sa marge, plus 68 px de remplissage.

   ⚠️ ON NE RÉTRÉCIT QU'À PARTIR DE 900 px, ET C'EST DÉLIBÉRÉ. Ces liens sont
   la SEULE navigation disponible sur téléphone (le menu du haut y est masqué),
   et leurs 44 px de hauteur de cible sont ce qui les rend cliquables au doigt.
   Les rogner partout gagnerait 60 px sur un écran d'ordinateur en rendant le
   pied inutilisable là où il sert le plus. À la souris, 34 px suffisent.
   ═══════════════════════════════════════════════════════════════════════════ */
/* ═══ LE PIED SE RÉDUIT À SA SIGNATURE ══════════════════════════════════════
   Matt le 2026-08-11, photo à l'appui : « enlève la bande noire en bas, on
   répète les infos. Mets juste droits réservés et fait par DroneWeb, un truc
   classe et pro ».
   Tout ce qu'il contenait existe déjà à dix centimètres au-dessus : les
   coordonnées sont dans la section contact, la mention « prix en CHF, hors
   TVA, engagement 6 mois » est sous les formules, et les liens répètent le
   menu du haut. Un pied qui redit tout n'inspire pas le sérieux, il donne
   l'impression qu'on remplit.

   ⚠️ LA NAVIGATION RESTE SUR TÉLÉPHONE, ET CE N'EST PAS NÉGOCIABLE.
   Sous 900 px, le menu du haut est masqué (`nav{display:none}`) : ces liens
   sont alors la SEULE façon de circuler dans une page de 7 000 px. Les retirer
   partout aurait rendu le site injouable au doigt pour gagner une ligne sur un
   écran d'ordinateur. Ils disparaissent donc au-dessus de 900 px seulement.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 901px) {
  body.v2 footer .pied-nav,
  body.v2 footer .pied-infos,
  body.v2 footer .footer-note { display: none; }
  /* La signature : la marque à gauche, le reste en gris fin. Une ligne, du
     souffle entre les blocs, et rien qui crie. */
  body.v2 footer .pied-signature {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: center;
    gap: 6px 26px;
  }
  body.v2 footer .pied-marque {
    font-family: 'Space Grotesk', sans-serif;
    font-size: .82rem;
    font-weight: 800;
    letter-spacing: .14em;
    color: var(--aw-orange-vif, #ff8a52);
    opacity: .9;
  }
  body.v2 footer .pied-droits,
  body.v2 footer .pied-fait { text-transform: none; letter-spacing: .06em; }
}
/* Au téléphone, la signature ne doit pas s'ajouter aux trois rangées : elle
   ferait une quatrième ligne dans un pied déjà chargé. */
@media (max-width: 900px) {
  body.v2 footer .pied-signature { display: none; }
  /* Une seule ligne, discrète, en petites capitales espacées : la signature
     d'un studio, pas un annuaire. */
  body.v2 footer {
    padding: 22px 32px;
    font-size: .7rem;
    letter-spacing: .22em;
    opacity: .55;
    border-top: 1px solid rgba(243,238,247,.10);
    background: transparent;    /* plus de « bande noire » : elle se fond */
  }
}

/* ⚠️ L'AFFINAGE DU 11/08 (304 → 176 px) A ÉTÉ RETIRÉ D'ICI, PAS PERDU.
   Il réduisait les marges des trois rangées du pied ; ces rangées ne sont plus
   affichées au-dessus de 900 px. Deux blocs `@media (min-width: 901px)` posant
   tous deux un `padding` sur `footer` auraient eu la MÊME spécificité, et le
   dernier écrit aurait gagné en silence — le piège le plus fréquent de ce
   fichier. Un seul bloc, une seule règle. */


/* ═══════════════════════════════════════════════════════════════════════════
   19. LA POLICE — Bricolage Grotesque sur les titres
   ───────────────────────────────────────────────────────────────────────────
   Choisie par Matt le 2026-08-11 après recherche.

   ⚠️ POURQUOI PAS SATOSHI, la police la plus utilisée du moment : Fontshare
   interdit de l'auto-héberger sans accord écrit d'ITF et impose de passer par
   leur API. C'est un CDN — l'IP de chaque visiteur partirait chez un tiers
   (LPD suisse, RGPD). Écarté pour cette seule raison, pas pour son dessin.

   Bricolage Grotesque est sous SIL OFL 1.1, variable sur trois axes (graisse,
   largeur, taille optique), et ELLE EST DÉJÀ DANS LE DÉPÔT :
   `vendor/polices/bricolage-grotesque-200-800-*.woff2`, rapatriée le
   2026-07-31 par outils/editeur-de-pages/rapatrier-cdn.mjs. Zéro téléchargement,
   zéro requête sortante, 185 Ko déjà payés.

   Les titres seulement : sur un paragraphe de six lignes, une grotesque à
   caractère fatigue. Le texte courant reste en Inter.
   ═══════════════════════════════════════════════════════════════════════════ */
/* ⚠️ CHOIX DE MATT LE 2026-08-11, après avoir vu les deux planches de
   comparaison : « 2 pour le titre et 1 pour tout le reste » — Bungee Inline
   sur LE grand titre, Cooper Black sur tous les autres titres.
   Il a choisi en connaissance de cause : je lui avais montré que Bungee Inline
   n'a que des majuscules et que son accent « É » se dédouble à cause du
   contour. C'est sa décision, elle est appliquée telle quelle.

   ⚠️ LE TEXTE COURANT RESTE EN INTER, et ce n'est pas négociable : Cooper
   Black est une grasse d'affiche. Sur un paragraphe de six lignes, elle
   devient un mur noir illisible. « Tout le reste » veut donc dire tous les
   autres TITRES, pas le corps de texte — c'était précisé dans la question. */
body.v2 h2, body.v2 h3,
body.v2 .porte-titre,
body.v2 .hero-carte strong,
body.v2 .aw-carte h3,
body.v2 .metier-nom,
body.v2 .price-card h3,
body.v2 .option-card h3,
body.v2 #vers-dronetech strong {
  font-family: 'Cooper Black', 'Bricolage Grotesque', serif;
  font-weight: 400;             /* elle n'a qu'une graisse : la demander plus
                                   grasse ferait fabriquer un faux gras au
                                   navigateur, et les contreformes se bouchent */
  letter-spacing: -0.005em;
}

/* ⚠️ BUNGEE INLINE EST TRÈS LARGE — environ 35 % de plus que Bricolage à
   taille égale, et uniquement en capitales. Mesuré : « VOTRE SITE
   PROFESSIONNEL, OFFERT. » débordait de deux lignes au plafond précédent
   (6 rem). On abaisse donc le plafond des grands titres et on resserre
   l'interlettrage, sinon le premier écran ne contient plus que le titre. */
body.v2 .hero h1.display,
body.v2 .offre-hero h1.display {
  font-family: 'Bungee Inline', 'Anton', sans-serif;
  font-weight: 400;
  font-size: clamp(1.9rem, 4vw, 3.5rem);
  line-height: 1.22;
  letter-spacing: -0.01em;
}
/* ⚠️ L'EXCEPTION « LE HERO GARDE ANTON » A ÉTÉ RETIRÉE LE 2026-08-11.
   Elle datait du choix précédent, où seuls les titres de section changeaient
   de police. Matt a depuis demandé Bungee Inline « pour le titre » — et cette
   règle, écrite PLUS BAS dans le fichier, l'aurait annulée en silence : le
   hero serait resté en Anton pendant que tout le reste changeait, et personne
   n'aurait compris pourquoi.
   Le mot « OFFERT » garde sa pulsation (`offertPulse`) et sa taille agrandie :
   c'est son argument de vente, seule sa police change. */


/* ═══════════════════════════════════════════════════════════════════════════
   21. LE PARCOURS EN TROIS TEMPS — métier → forfait → options
   ───────────────────────────────────────────────────────────────────────────
   Matt le 2026-08-11 : « quand tu cliques sur une des cartes, elle s'agrandit,
   le titre s'efface pour laisser la place, et juste à sa droite apparaissent
   les forfaits qui correspondent ; puis après le clic sur le forfait, ça
   montre en bas les différentes options en néon. »

   Une seule section porte les trois temps. Chacun n'apparaît qu'une fois le
   précédent franchi : on ne montre jamais un prix avant de savoir de quel
   métier il s'agit, ni une option avant de savoir quelle formule la comprend.

   ⚠️⚠️ RÈGLE ABSOLUE DE CE BLOC : NE JAMAIS POSER UN `display` SUR
   `.price-grid` OU `.option-grid` SANS `.actif`.
   La page contient QUATORZE grilles de chaque, toutes présentes en
   permanence ; seule celle du métier choisi est affichée, par
   `[data-metier]{display:none}` + `.actif{display:grid}` (agence-web.html:725).
   Une règle sans `.actif` les rend TOUTES visibles d'un coup, empilées, prix
   et métiers mélangés. C'est arrivé le 2026-08-11 : Matt l'a vu avant moi et
   l'a résumé par « c'est le bordel ». Aucun test ne l'avait attrapé — ils
   visaient tous `.actif`, donc la bonne grille.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── TEMPS 1 : la grille des métiers, seule ─────────────────────────────── */
.metier-forfaits,
.metier-options-zone { display: none; }

/* ── TEMPS 2 : un métier est choisi ─────────────────────────────────────── */
/* Le titre s'efface pour laisser la place — demande explicite. Il garde sa
   place dans le flux (opacity + visibility, jamais display) : le retirer
   ferait sauter toute la colonne au moment précis du clic. */
.aw-parcours .aw-colle-titre { transition: opacity .4s, visibility .4s; }
.aw-parcours.parcours-ouvert .aw-colle-titre {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  /* ⚠️ IL GARDE SA PLACE, MAIS PLUS SA HAUTEUR — corrigé le 2026-08-11.
     Tant que le titre était dans une COLONNE à côté du contenu, son espace
     réservé ne coûtait rien. Depuis que la section passe à une seule colonne à
     l'ouverture, il se retrouve AU-DESSUS : vu sur `21-metier-ouvert.png`,
     600 px de vide poussaient la carte choisie hors de l'écran, juste après
     un clic censé la montrer.
     `height: 0` et pas `display: none` : l'élément reste dans le flux et dans
     la grille, donc rien ne saute d'un cran au moment du clic — c'était la
     raison d'être de `visibility` ici, elle tient toujours. */
  height: 0;
  min-height: 0;
  margin: 0;
  overflow: hidden;
}
/* La jauge n'a plus rien à mesurer quand le ruban dort. */
.aw-parcours.parcours-ouvert .metier-jauge { display: none; }
/* Libérée du titre, la section prend toute la largeur. */
.aw-parcours.parcours-ouvert .aw-colle-duo { grid-template-columns: 1fr; }

/* La carte ouverte à gauche, ses forfaits à droite. */
/* ⚠️ LA COLONNE DES FORFAITS EST LA PLUS LARGE, ET C'EST CALCULÉ.
   Elle doit loger TROIS cartes de prix sur une ligne (demande du matin même).
   À .82fr / 1.18fr, elles tombaient à 214 px : le montant à 2,2 rem et les
   libellés se cassaient mot par mot. À .6fr / 1.4fr, la colonne fait 796 px et
   chaque carte 256 px — au-dessus du seuil de lisibilité mesuré (240 px).
   Réduire cette proportion, c'est reperdre les trois cartes en ligne. */
.aw-parcours.parcours-ouvert .aw-colle-corps {
  display: grid;
  grid-template-columns: minmax(0, .6fr) minmax(0, 1.4fr);
  gap: clamp(22px, 3vw, 44px);
  align-items: start;
}
/* ⚠️ C'EST LA PISTE QUI EST DANS LA COLONNE, PLUS LA GRILLE. Depuis le ruban
   (2026-08-11), la grille est enveloppée de trois divs ; `grid-column` posé
   sur elle ne dit plus rien à la mise en page du mode ouvert, et le placement
   automatique ne marchait que par chance de l'ordre du HTML. La hauteur est
   remise à `auto` ici ET par le script : sans cette ligne, la piste garderait
   sa course de 1 050 px sous une carte ouverte si le script venait à ne pas
   tourner. */
.aw-parcours.parcours-ouvert .metier-piste { grid-column: 1; height: auto; }
.aw-parcours.parcours-ouvert .metier-grid { grid-column: 1; }
.aw-parcours.parcours-ouvert .metier-forfaits {
  display: block;
  grid-column: 2;
  animation: awEntreeDroite .5s cubic-bezier(.16,1,.3,1);
}
.aw-parcours.parcours-ouvert .metier-recherche,
.aw-parcours.parcours-ouvert .metier-rien { grid-column: 1 / -1; }

@keyframes awEntreeDroite {
  from { opacity: 0; translate: 46px 0; }
  to   { opacity: 1; translate: 0 0;    }
}

/* Les formules, dans leur colonne : trois sur une ligne, comme demandé le
   matin. `.actif` est obligatoire — voir l'avertissement en tête de bloc. */
.aw-parcours .metier-forfaits .price-grid.actif {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
}
.aw-parcours .metier-forfaits .price-grid.deux.actif { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.aw-parcours .metier-forfaits .price-card         { padding: 26px 20px; }
.aw-parcours .metier-forfaits .price-card h3      { font-size: 1.12rem; }
.aw-parcours .metier-forfaits .price-card .amount { font-size: 2.2rem; }
.aw-parcours .metier-forfaits .price-card li      { font-size: .84rem; padding: 8px 0; }
.aw-parcours .metier-forfaits .metier-intro.actif {
  display: block;
  margin-bottom: 18px;
  font-size: 1rem;
}
@media (max-width: 1100px) {
  .aw-parcours.parcours-ouvert .aw-colle-corps { grid-template-columns: 1fr; }
  .aw-parcours.parcours-ouvert .metier-grid,
  .aw-parcours.parcours-ouvert .metier-forfaits { grid-column: 1; }
}

/* ═══ TEMPS 3 : TOUT SE RESSERRE SUR UNE SEULE LIGNE ══════════════════════
   Matt le 2026-08-11, après avoir validé les deux premiers temps : « quand tu
   cliques sur le forfait, la carte métier se met plus petite, la barre de
   recherche disparaît, la carte sélectionnée est mise en avant, les autres
   deviennent légèrement transparentes — toujours visibles et cliquables — et
   les options se voient à droite du forfait. Bref, que tout tienne sur une
   ligne et sur la même page. »

   Trois colonnes : le métier (réduit) · les formules · les options.
   ⚠️ « SUR LA MÊME PAGE » veut dire SANS DÉFILER : la ligne doit tenir dans un
   écran, barre de navigation déduite (~780 px sur un 900). C'est cette
   contrainte qui commande tout ce qui suit — et c'est elle qui oblige à
   réduire les options à leur prix et leur nom.
   ═══════════════════════════════════════════════════════════════════════════ */
.aw-parcours.parcours-forfait .aw-colle-corps {
  grid-template-columns: minmax(0, .46fr) minmax(0, 1.15fr) minmax(0, .92fr);
  gap: clamp(16px, 1.8vw, 28px);
}

/* La barre de recherche disparaît : le métier est choisi, elle n'a plus de
   rôle, et elle coûtait 68 px de hauteur dont on a besoin. */
.aw-parcours.parcours-forfait .metier-recherche,
.aw-parcours.parcours-forfait .metier-rien { display: none; }

/* La carte métier se fait petite : elle rappelle le choix, elle ne le porte
   plus. Ses puces d'options disparaissent — elles sont redites juste à côté,
   en vraies cartes cliquables. */
.aw-parcours.parcours-forfait .metier-grid { grid-column: 1; }
.aw-parcours.parcours-forfait .metier-grid.grille-ouverte .metier-card.actif {
  min-height: 0;
  padding: 20px 22px;
}
.aw-parcours.parcours-forfait .metier-grid.grille-ouverte .metier-card.actif .metier-nom {
  font-size: 1.16rem;
}
.aw-parcours.parcours-forfait .metier-card.actif .metier-promesse,
.aw-parcours.parcours-forfait .metier-card.actif .metier-options,
.aw-parcours.parcours-forfait .metier-card.actif .metier-meta { display: none; }

/* ── LES FORFAITS : le choisi devant, les autres atténués mais VIVANTS ──── */
.aw-parcours.parcours-forfait .metier-forfaits { grid-column: 2; }
.aw-parcours.parcours-forfait .metier-forfaits .metier-intro.actif { display: none; }
.aw-parcours.parcours-forfait .metier-forfaits .price-grid.actif {
  grid-template-columns: 1fr;   /* en colonne : la ligne est déjà prise en largeur */
  gap: 10px;
}
/* ⚠️ ATTÉNUÉES, JAMAIS DÉSACTIVÉES. Matt : « toujours visibles et cliquables ».
   On ne baisse donc que l'opacité — pas de `pointer-events: none`, pas de
   `display`. À 0,45 on lit encore le nom et le prix, et un clic change de
   formule sans avoir à revenir en arrière. */
.aw-parcours.parcours-forfait .price-card:not(.selected) {
  opacity: .45;
  scale: .97;
}
.aw-parcours.parcours-forfait .price-card:not(.selected):hover { opacity: .85; }
.aw-parcours.parcours-forfait .price-card.selected { opacity: 1; }
/* Repliées, les formules ne montrent que l'essentiel : la liste complète du
   forfait choisi tiendrait, celle des trois non. */
.aw-parcours.parcours-forfait .price-card:not(.selected) ul { display: none; }
.aw-parcours.parcours-forfait .price-card { padding: 16px 18px; }
.aw-parcours.parcours-forfait .price-card .amount { font-size: 1.8rem; margin-bottom: 10px; }
.aw-parcours.parcours-forfait .price-card .duration { margin-bottom: 10px; }
.aw-parcours.parcours-forfait .price-card.selected li { font-size: .8rem; padding: 6px 0; }

/* ── LES OPTIONS, À DROITE DU FORFAIT ──────────────────────────────────── */
.aw-parcours.parcours-forfait .metier-options-zone {
  display: block;
  grid-column: 3;
  grid-row: span 2;
  margin-top: 0;
  padding-top: 0;
  padding-left: clamp(16px, 1.6vw, 26px);
  border-top: 0;
  border-left: 1px solid rgba(255,148,102,.28);
  animation: awEntreeDroite .5s cubic-bezier(.16,1,.3,1);
}
/* ⚠️ LES DESCRIPTIONS SONT MASQUÉES, ET C'EST LE PRIX DE « TOUT SUR UNE LIGNE ».
   Huit options avec leurs deux lignes de texte font 1 400 px de haut : la
   demande « tout tient sur la même page » devient alors impossible. Réduites à
   leur prix et à leur nom, elles tiennent en 640 px. La description revient au
   survol, là où on la cherche vraiment — au moment de choisir. */
.aw-parcours.parcours-forfait .metier-options-zone .option-grid.actif {
  grid-template-columns: 1fr;
  gap: 8px;
}
.aw-parcours.parcours-forfait .metier-options-zone .option-card {
  padding: 12px 14px;
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: baseline;
  gap: 4px 12px;
}
.aw-parcours.parcours-forfait .metier-options-zone .option-card .option-price {
  font-size: .92rem !important;
  grid-column: 1;
}
.aw-parcours.parcours-forfait .metier-options-zone .option-card h3 {
  font-size: .92rem;
  margin: 0;
  grid-column: 2;
}
/* ⚠️ LA DESCRIPTION SE POSE PAR-DESSUS — ELLE NE DÉPLIE PLUS LA CARTE.
   CORRIGÉ LE 2026-08-14. Défaut mesuré : au survol, la carte passait de 58 à
   104 px et poussait les 7 suivantes de 47 px. Le curseur arrive par le bas,
   la carte grandit, celles du dessous fuient — et le clic tombe sur l'option
   voisine. Sur une grille tarifaire, cliquer la mauvaise option est un DEVIS
   FAUX envoyé à un client. C'est trait pour trait la note de Matt du 06/08
   (« la sélection se fait mal »), revenue avec la refonte.

   Trois parades ont été chiffrées (diag-cartes-option-variantes.mjs). Garder la
   description ouverte en permanence supprime aussi le mouvement, mais fait
   passer la zone de 570 à 919 px : DEUX options sortent de l'écran, ce que Matt
   a refusé le 11/08 (« toutes les options visibles en même temps »). La
   superposition ne coûte rien : 0 px de décalage, 0 option hors écran.

   ⚠️ LES TROIS DÉCLARATIONS CI-DESSOUS SONT SOLIDAIRES, n'en retirer aucune :
   · `overflow: visible` sur la carte au survol — sans lui le panneau est coupé
     net (la carte porte `overflow:hidden`, agence-web.html:415) et RIEN ne
     s'affiche. Mesuré : « 0 px de décalage » restait vrai, parce que plus rien
     ne bougeait ni n'apparaissait. Un chiffre de géométrie ne sait pas faire la
     différence entre « corrigé » et « la fonction a disparu » — il a fallu
     REGARDER la capture. Sans risque : les deux pseudo-éléments décoratifs de
     wow.css sont en `inset: 0` et `inset: -1px`, ils ne débordent pas.
   · `z-index` sur la carte au survol — la carte porte `isolation: isolate`,
     donc sans lui le panneau passe SOUS la carte suivante, qui vient après
     dans le DOM.
   · PAS de `pointer-events: none` sur le panneau — et c'est le point le moins
     évident. Le panneau recouvre la carte voisine ; en le rendant transparent
     aux clics, cliquer dessus aurait sélectionné CETTE VOISINE, c'est-à-dire
     exactement le défaut qu'on corrige. Le panneau étant un enfant de sa carte,
     le clic remonte à la bonne option. Un contrôle le vérifie dans
     test-cartes-cliquables.mjs.

   ⚠️ ET PAS SUR `.selected` : une carte choisie garderait un panneau affiché en
   permanence par-dessus sa voisine. Le détail se lit au moment de choisir, pas
   après. `:focus-visible` est là pour le clavier, qui n'a pas de survol. */
.aw-parcours.parcours-forfait .metier-options-zone .option-card:hover,
.aw-parcours.parcours-forfait .metier-options-zone .option-card:focus-visible {
  overflow: visible;
  z-index: 10;
}
.aw-parcours.parcours-forfait .metier-options-zone .option-card p {
  position: absolute;
  left: 14px;
  right: 14px;
  top: calc(100% + 4px);
  grid-column: 1 / -1;
  font-size: .82rem;
  line-height: 1.4;
  margin: 0;
  padding: 10px 12px;
  opacity: 0;
  visibility: hidden;
  background: rgba(10,12,20,.97);
  border: 1px solid rgba(255,148,102,.45);
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(0,0,0,.55);
  z-index: 20;
  transition: opacity .25s, visibility .25s;
}
.aw-parcours.parcours-forfait .metier-options-zone .option-card:hover p,
.aw-parcours.parcours-forfait .metier-options-zone .option-card:focus-visible p {
  opacity: 1;
  visibility: visible;
}
/* Sur un écran tactile il n'y a pas de survol : la description ne serait JAMAIS
   lisible. On la remet dans le flux, où elle ne peut plus rien pousser. */
@media (hover: none) {
  .aw-parcours.parcours-forfait .metier-options-zone .option-card p {
    position: static;
    opacity: .86;
    visibility: visible;
    margin-top: 6px;
    padding: 0;
    background: none;
    border: 0;
    box-shadow: none;
  }
}
/* ⚠️ LE TICKET PASSE À GAUCHE PENDANT LE TROISIÈME TEMPS — 2026-08-11.
   Défaut relevé sur capture le matin (« le ticket d'estimation recouvre les
   deux dernières options »), devenu bloquant l'après-midi quand Matt a
   demandé que TOUTES les options soient visibles en même temps : flottant en
   bas à droite, le ticket masquait précisément les deux dernières de la
   colonne de droite. Aucune place à gagner en hauteur — la colonne tient déjà
   tout juste dans l'écran.
   La colonne de GAUCHE, elle, est presque vide au troisième temps : la carte
   métier y est réduite à un rappel de 190 px. Le ticket va donc s'y poser.
   ⚠️ On ne touche QUE `left`/`right` : le ticket reste `fixed`, comme il l'a
   toujours été. Lui imposer une `position` depuis ici l'avait déjà fait
   disparaître une fois — une règle utilitaire ne sait pas ce que l'élément
   était avant. */
body.parcours-en-cours #price-ticket {
  right: auto;
  left: 24px;
}
@media (max-width: 1250px) {
  /* À deux colonnes, les options repassent en dessous : le ticket retrouve sa
     place habituelle, il ne recouvre plus rien. */
  body.parcours-en-cours #price-ticket { right: 24px; left: auto; }
}

/* La pastille de sélection reprend sa place dans une carte aussi compacte. */
.aw-parcours.parcours-forfait .metier-options-zone .pick-check { top: 10px; right: 10px; width: 17px; height: 17px; }
.aw-parcours.parcours-forfait .metier-options-zone .option-badge { display: none; }
/* ⛔ CE BLOC ÉTAIT `display:none` ICI, ET IL DISPARAISSAIT DONC TOUT LE TEMPS.
   Mesuré le 2026-08-16 : `.metier-options-zone` n'est rendue visible QUE par
   `.aw-parcours.parcours-forfait` (l. 2393) — et cette règle-ci la masquait
   sous exactement la même classe. Les deux conditions s'excluant, le bloc
   était INATTEIGNABLE : aucun parcours de visiteur ne pouvait l'afficher.
   Ce qui partait avec lui n'était pas décoratif — c'est l'argument de vente
   « Essayez avant de payer : 30 jours, sans carte, sans engagement », et les
   deux seules occurrences de « 30 jours » de la page vivent dedans.
   Décision de Matt le 2026-08-16 : le faire revenir.
   Il est donc réhabillé pour la colonne étroite (~295 px) au lieu d'être
   masqué : empilé, avec les tailles réduites du reste de cette colonne. */
.aw-parcours.parcours-forfait .metier-options-zone .trial-callout {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  margin-top: 14px;
  padding: 14px;
}
.aw-parcours.parcours-forfait .metier-options-zone .trial-badge {
  font-size: .8rem;
  padding: 8px 14px;
  box-shadow: 0 0 18px rgba(255, 106, 61, .34);
}
.aw-parcours.parcours-forfait .metier-options-zone .trial-text {
  font-size: .82rem;
  line-height: 1.45;
}

@keyframes awEntreeBas {
  from { opacity: 0; translate: 0 40px; }
  to   { opacity: 1; translate: 0 0;    }
}

/* Sous 1250 px, trois colonnes deviennent trois couloirs : on repasse à deux,
   puis à une. « Tout sur une ligne » n'a de sens que s'il reste de la place. */
@media (max-width: 1250px) {
  .aw-parcours.parcours-forfait .aw-colle-corps { grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); }
  .aw-parcours.parcours-forfait .metier-options-zone {
    grid-column: 1 / -1;
    grid-row: auto;
    border-left: 0;
    padding-left: 0;
    border-top: 1px solid rgba(255,148,102,.28);
    padding-top: 24px;
    margin-top: 24px;
  }
  .aw-parcours.parcours-forfait .metier-options-zone .option-grid.actif {
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  }
}

/* ⚠️ LES OPTIONS SONT RANGÉES EN GRILLE, PAS EMPILÉES EN ÉVENTAIL.
   L'éventail essayé le matin faisait se chevaucher sept cartes translucides :
   illisible, et c'est l'autre moitié de ce que Matt a appelé « le bordel ».
   Une grille régulière se parcourt d'un coup d'œil — pour choisir des options,
   c'est tout ce qu'on lui demande. */
.aw-parcours .metier-options-zone .option-grid.actif {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
  gap: 16px;
}
.aw-parcours .metier-options-zone .option-card    { padding: 24px 22px; }
.aw-parcours .metier-options-zone .option-card h3 { font-size: 1.06rem; }
.aw-parcours .metier-options-zone .option-card p  { font-size: .92rem; line-height: 1.5; }

/* Le titre de l'étape, écrit par le CSS et pas par le HTML : l'éditeur de
   pages efface les éléments neufs qu'il ne reconnaît pas. */
.aw-parcours.parcours-forfait .metier-options-zone::before {
  content: 'Vos options — celles comprises dans la formule s’allument en turquoise';
  display: block;
  font-family: 'JetBrains Mono', monospace;
  font-size: .72rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--aw-orange-vif, #ff8a52);
  margin-bottom: 18px;
}


/* ═══════════════════════════════════════════════════════════════════════════
   22. L'OPTION COMPRISE DANS LA FORMULE — la lumière qui tourne
   ───────────────────────────────────────────────────────────────────────────
   Matt le 2026-08-11 : « celles qui sont incluses dans la formule, contour
   néon avec lumière qui tourne autour du contour ».

   ⚠️ ON RÉUTILISE LE LISERÉ DE `css/wow.css`, ON N'EN ÉCRIT PAS UN SECOND.
   Ce fichier occupe déjà `::after` de `.option-card` avec un dégradé conique
   masqué qui tourne (`--wow-angle`, `@keyframes wow-tourne`). En poser un
   second ici écraserait le premier — la faute déjà commise sur cette page le
   2026-08-06, où deux fichiers animaient le même pseudo-élément. On ne fait
   que RENFORCER l'existant : plus épais, plus rapide, et en turquoise, la
   couleur qui dit déjà « compris » dans toute la page.
   ═══════════════════════════════════════════════════════════════════════════ */
body.v2 .option-card.incluse {
  --wow-teinte:  #2fe0c2;
  --wow-teinte2: #6ff5dd;
  box-shadow: 0 0 0 1px rgba(47,224,194,.55),
              0 0 38px -6px rgba(47,224,194,.6),
              0 18px 44px -18px rgba(0,0,0,.8);
}
body.v2 .option-card.incluse::after {
  opacity: 1;
  padding: 2px;
  animation: wow-tourne 2.6s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  body.v2 .option-card.incluse::after { animation: none; }
  .aw-parcours.parcours-ouvert .metier-forfaits,
  .aw-parcours.parcours-forfait .metier-options-zone { animation: none; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   11. LE PARALLAXE
   ───────────────────────────────────────────────────────────────────────────
   Le fond des portes glisse moins vite que la page. La valeur est écrite par
   js/agence-web-v2-effets.js dans --aw-para ; ici on ne fait que la consommer.
   Valeur de repli à 0 : si le script ne tourne pas, rien ne bouge et rien ne
   casse.
   ⚠️ `translate` (propriété autonome) et pas `transform` : `.porte-fond` a
   déjà `transform: scale(1.06)`, que le survol anime. Les écraser ferait
   disparaître le zoom au survol.
   ═══════════════════════════════════════════════════════════════════════════ */
.porte-fond { translate: 0 var(--aw-para, 0px); }


/* ═══════════════════════════════════════════════════════════════════════════
   23. LES DEUX APPELS À L'ACTION — 2026-08-11 (7ᵉ passe)
   ───────────────────────────────────────────────────────────────────────────
   Matt, photo à l'appui : « le bouton demander mon site est pas joli, trop
   classique, améliore-le ainsi que le bouton découvrir DroneWeb ».
   Mesuré avant : deux rectangles pleins, `border-radius: 4px`, une couleur à
   plat, aucun dégradé. Sur une page qui pousse le néon à fond, ce sont les
   deux seuls endroits où le visiteur doit CLIQUER, et c'était le bloc le plus
   plat de la page.

   ⚠️ CE QUI EST DÉJÀ OCCUPÉ SUR `.btn` — vérifié avant d'écrire une ligne :
   · `translate`  → js/magnetique.js (les boutons suivent le curseur) et
     css/metiers-cartes.css:31. On n'y touche pas.
   · `::after`    → la flèche « → » de agence-web.html:285, qui glisse au survol.
   · `transform`  → le `translateY(-2px)` du survol d'origine ; on le remplace,
     il ne se cumule pas avec `translate`, ce sont deux propriétés distinctes.
   Restait `::before` — libre, vérifié dans les quatre fichiers. C'est lui qui
   porte le balayage de lumière.

   ⚠️ AUCUN CHANGEMENT DE GÉOMÉTRIE EN BOUCLE. Ce qui respire en permanence,
   c'est la POSITION DU DÉGRADÉ, jamais la taille du bouton : une cible qui
   grandit et rétrécit sans arrêt est une cible qu'on rate (règle « on n'anime
   pas ce qui se clique »). Et le texte n'étant pas mis à l'échelle, le repaint
   du fond ne peut pas le faire grésiller — c'est l'agrandissement qui faisait
   vibrer les lettres des cartes, pas le repaint.
   ═══════════════════════════════════════════════════════════════════════════ */
body.v2 .btn {
  position: relative;
  overflow: hidden;              /* le balayage reste dans le bouton */
  isolation: isolate;            /* ⚠️ sans lui, le `z-index: -1` du balayage
                                    sortirait du bouton et passerait sous le
                                    fond de la section : invisible. */
  border-radius: 999px;          /* 4 px → pilule : le changement le plus visible */
  padding: 21px 42px;
  letter-spacing: 1.6px;
  transition: transform .28s cubic-bezier(.22,1,.36,1),
              box-shadow .28s ease-out, filter .28s ease-out;
}
/* Le balayage de lumière : au repos il attend hors du bouton, au survol il le
   traverse une fois. Un reflet qui passe, pas un clignotant. */
body.v2 .btn::before {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: 45%;
  z-index: -1;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.55), transparent);
  transform: translateX(-160%) skewX(-18deg);
  transition: transform .75s cubic-bezier(.3,.9,.3,1);
}
body.v2 .btn:hover::before,
body.v2 .btn:focus-visible::before { transform: translateX(320%) skewX(-18deg); }

/* ── L'APPEL PRINCIPAL — « Demander mon site offert » ───────────────────────
   ⚠️ LE SÉLECTEUR REPREND CELUI QU'IL SURCHARGE. `.btn-primary` d'origine vaut
   (0,1,0) ; `body.v2 .btn-primary` vaut (0,2,1) et passe devant. Quatre fois le
   11/08 une surcharge écrite trop court n'a rien fait, sans la moindre erreur. */
body.v2 .btn-primary {
  /* Trois teintes au lieu d'une : le bouton a un volume, il n'est plus un
     aplat. 240 % de large, pour que la lueur ait de la place où glisser. */
  background: linear-gradient(100deg, #ff9a5e 0%, #ff6a3d 32%, #e8481f 62%, #ff8347 100%);
  background-size: 240% 100%;
  background-position: 0% 50%;
  /* ⚠️ TEXTE FONCÉ, ET C'EST MESURÉ, PAS UN GOÛT : blanc sur cet orange donne
     2,85:1, sous la barre des 4,5:1. L'encre de la page donne 6,3:1. */
  color: #140b06;
  border-color: rgba(255,196,150,.55);
  box-shadow: 0 0 0 1px rgba(255,154,94,.35),
              0 14px 34px -14px rgba(255,106,61,.85),
              0 0 44px rgba(255,106,61,.30);
  animation: awBtnLueur 9s ease-in-out infinite;
}
body.v2 .btn-primary:hover,
body.v2 .btn-primary:focus-visible {
  /* ⚠️ `animation: none` et pas une mise en pause : une animation en pause
     impose encore sa valeur et écraserait la position de dégradé du survol.
     Mesuré le 11/08 sur les cartes, faux vert à l'appui. */
  animation: none;
  background-position: 100% 50%;
  transform: translateY(-3px);
  box-shadow: 0 0 0 1px rgba(255,186,140,.75),
              0 20px 44px -14px rgba(255,106,61,.95),
              0 0 74px rgba(255,106,61,.55);
}
body.v2 .btn-primary:active { transform: translateY(-1px); }

/* ── LA PASSERELLE — « Découvrir DroneWeb », l'autre moitié de la maison ────
   ⚠️ IL FAUT UN ID DANS LE SÉLECTEUR : l'original est `#vers-dronetech
   .btn-cross` (1,1,0). Sans `#vers-dronetech`, rien de ce qui suit ne
   s'appliquerait — et aucune erreur ne le dirait. */
body.v2 #vers-dronetech .btn-cross {
  background: linear-gradient(100deg, #7ef0ff 0%, #00d4ff 34%, #009fd6 64%, #4de3ff 100%);
  background-size: 240% 100%;
  background-position: 0% 50%;
  color: #04121a;                /* 11,9:1 sur ce cyan */
  border-color: rgba(160,240,255,.6);
  box-shadow: 0 0 0 1px rgba(0,212,255,.35),
              0 14px 34px -14px rgba(0,180,255,.85),
              0 0 44px rgba(0,212,255,.30);
  animation: awBtnLueur 9s ease-in-out infinite;
}
body.v2 #vers-dronetech .btn-cross:hover,
body.v2 #vers-dronetech .btn-cross:focus-visible {
  animation: none;
  background-position: 100% 50%;
  transform: translateY(-3px);
  box-shadow: 0 0 0 1px rgba(190,246,255,.8),
              0 20px 44px -14px rgba(0,200,255,.95),
              0 0 74px rgba(0,212,255,.55);
}

/* La lueur qui traverse lentement au repos : SEULE la position du dégradé
   bouge. Rien ne change de taille, rien ne se déplace, la cible est fixe. */
@keyframes awBtnLueur {
  0%, 100% { background-position: 0% 50%; }
  50%      { background-position: 62% 50%; }
}

/* Le clavier doit voir où il est — l'anneau est DEHORS, il ne rogne pas le
   texte et ne se confond pas avec la bordure lumineuse du survol. */
body.v2 .btn:focus-visible {
  outline: 2px solid #6ff5dd;
  outline-offset: 4px;
}


/* ═══════════════════════════════════════════════════════════════════════════
   24. LISIBILITÉ — tout est plus gros et plus gras (2026-08-11, 8ᵉ passe)
   ───────────────────────────────────────────────────────────────────────────
   Matt, photo à l'appui : « que tous les textes soient légèrement plus gros et
   gras, on a du mal à lire, ainsi que le texte des titres, et ça sur toute la
   page ». Relevé avant : corps 16 px, texte des blocs 16,32 px, options
   14,72 px, lignes de forfait 13,44 px, sur-titres 11,52 px.

   ⚠️⚠️ LE « PLUS GRAS » ÉTAIT IMPOSSIBLE, ET PERSONNE NE POUVAIT LE VOIR.
   Dans `vendor/polices/polices.css`, les faces Inter déclarées en 500 et en
   600 pointent toutes vers le MÊME fichier — `inter-400`. Le navigateur
   choisit la face demandée et affiche donc les glyphes maigres, sans
   synthétiser quoi que ce soit et sans la moindre erreur : écrire
   `font-weight: 600` ne changeait pas un pixel. La page était déjà en 500
   depuis toujours, pour rien.
   Trois polices VARIABLES sont déjà dans le dépôt (aucun téléchargement,
   aucun CDN) : Geist 300-800, Instrument Sans 400-700, Archivo 400-800.
   Geist est le plus proche d'Inter — même famille de grotesques neutres — et
   il donne enfin une vraie graisse intermédiaire.

   ⚠️ LA TAILLE SE MONTE À LA RACINE, PAS RÈGLE PAR RÈGLE. Toute la page est
   écrite en `rem` (1,02 · 1,16 · 0,84 · 0,72…) : une seule valeur ici les
   déplace toutes ensemble, et les proportions voulues par la maquette sont
   conservées. Les corriger une par une aurait garanti d'en oublier, et de
   casser la hiérarchie là où on aurait tapé à côté.
   ⚠️ Les `clamp(…vw…)` des grands titres ne suivent que par leur part en rem :
   c'est voulu, un titre déjà à 46 px n'a pas besoin des mêmes 9 %.
   ═══════════════════════════════════════════════════════════════════════════ */
html { font-size: 17.5px; }        /* 16 → 17,5 px, soit +9,4 % sur toute la page */

/* ⚠️ LE TICKET S'EFFACE TANT QU'ON N'A RIEN À ESTIMER — voir `ticketHorsDuHero`
   dans js/agence-web-v2-effets.js. Trouvé parce qu'il recouvrait la troisième
   carte du hero. Ni `display`, ni `position` : ce ticket a déjà disparu une
   fois pour avoir reçu un `position` d'une règle utilitaire. */
body.v2.ticket-en-attente #price-ticket {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .35s ease-out, visibility 0s linear .35s;
}

/* ⚠️ LE TICKET REPLIÉ EN PASTILLE — 2026-08-12, sur photo d'écran de Matt :
   « arrange ça ». Le panneau était posé en plein milieu du paragraphe de la
   carte « Un métier qui n'est pas dans la liste » et coupait la phrase.
   Mesuré à 1440×900 : 56 % d'un bloc de texte recouvert.

   `ticketHorsDuHero` ne fait que RETARDER son apparition ; une fois affiché il
   ne s'efface plus, et comme il est `fixed` il reste posé sur tout ce qui
   défile dessous. Le déport à gauche du 11/08 déplaçait le problème.

   Ici on ne cache rien : on REPLIE. Le total reste lu, le clic rouvre, et la
   surface couverte tombe de ~320×300 à ~180×44.
   ⚠️ Ni `position`, ni `display` sur le ticket lui-même : ce sont les deux
   propriétés qui l'ont déjà fait disparaître du document.
   Piloté par `ticketNeCouvrePasLeTexte` dans js/agence-web-v2-effets.js. */
body.v2 #price-ticket.replie {
  width: auto;
  padding: 9px 18px;
  border-radius: 999px;
  cursor: pointer;
}
body.v2 #price-ticket.replie #ticket-lines,
body.v2 #price-ticket.replie .ticket-note,
body.v2 #price-ticket.replie .ticket-cta,
body.v2 #price-ticket.replie .ticket-title { display: none; }
/* ⚠️ LA PASTILLE DOIT ÊTRE PETITE POUR SERVIR À QUELQUE CHOSE. Premier essai :
   elle sortait à 223×70 et recouvrait ENCORE 22,9 % d'un paragraphe — le
   défaut était réduit, pas supprimé. Le coupable était le total, qui garde par
   défaut son chiffre en 1,9 rem, sa marge et son filet de séparation. */
body.v2 #price-ticket.replie .ticket-total {
  margin: 0; padding: 0; border: 0; gap: 12px;
  display: flex; align-items: center; white-space: nowrap;
}
body.v2 #price-ticket.replie .ticket-total b { font-size: 0.68rem; letter-spacing: .4px; }
body.v2 #price-ticket.replie .ticket-total .ticket-amount { font-size: 1.05rem; text-shadow: none; }

/* L'EMPLACEMENT de la pastille n'est PAS dans cette feuille : il est calculé à
   chaque défilement par `ticketNeCouvrePasLeTexte`, qui balaie les deux bords
   et pose `left`/`top` en ligne sur le premier emplacement réellement libre.
   Une position écrite ici serait fatalement fausse une fois sur deux — la
   place libre dépend du texte à l'écran, pas d'une règle.
   ⚠️ Quatre classes de coin ont été essayées avant : il restait un frôlement
   de 3,4 % qui mangeait la fin d'une ligne. */
/* L'affordance : on doit VOIR que ça se rouvre. */
body.v2 #price-ticket.replie .ticket-total::after {
  content: '⌃';
  font-size: 0.9rem; line-height: 1; opacity: .75;
  transform: translateY(3px);
}
body.v2 #price-ticket.replie:hover { box-shadow: 0 18px 50px -18px rgba(0,0,0,.7), 0 0 0 2px rgba(255,106,61,.35); }
/* Le repli ne s'anime pas en mouvement réduit : demandé explicitement. */
@media (prefers-reduced-motion: reduce) {
  body.v2 #price-ticket { transition: none; }
}

body.v2 {
  font-family: 'Geist', 'Inter', system-ui, sans-serif;
  font-weight: 560;                /* une vraie graisse : la police est variable */
}
/* Les blocs de lecture, là où la fatigue se joue vraiment. Les règles qui
   posent explicitement 500 ailleurs ne suivraient pas l'héritage du corps. */
body.v2 p,
body.v2 li,
body.v2 .section-intro,
body.v2 .aw-carte p,
body.v2 .metier-card p,
body.v2 .option-card p,
body.v2 .price-card li { font-weight: 560; }
/* ⚠️ ON NE TOUCHE PAS AUX TITRES EN COOPER BLACK : cette police n'a qu'une
   seule graisse, et lui en demander une autre ferait synthétiser un faux gras
   baveux. Ils grossissent par la racine, comme le reste. */


/* ═══════════════════════════════════════════════════════════════════════════
   25. LA CARTE DE CONTACT — 2026-08-11
   ───────────────────────────────────────────────────────────────────────────
   Matt : « je voudrais qu'elle ouvre une nouvelle carte qui cette fois est un
   formulaire simple de contact, avec en haut mes infos mail tél ».

   ⚠️ `<dialog>` NATIF : le focus enfermé dedans, la touche Échap, le retour du
   focus au bouton d'origine et le reste de la page rendu inerte sont donnés
   par le navigateur. Une fausse fenêtre en `<div>` oblige à réécrire tout ça,
   et on en oublie toujours un morceau.
   ⚠️ `dialog` est en `display: none` tant qu'il n'est pas ouvert : on ne pose
   donc JAMAIS `display` ici, sinon la carte serait visible en permanence — la
   même famille d'erreur que la règle utilitaire qui impose `position`.
   ═══════════════════════════════════════════════════════════════════════════ */
.carte-contact {
  /* ⚠️ `margin: auto` EXPLICITE — vu en capture le 2026-08-11 : la carte
     s'ouvrait collée en haut à gauche de l'écran. Le navigateur centre bien
     un `dialog` modal tout seul… par une règle de sa feuille par défaut, que
     le `* { margin: 0 }` du site écrase. Aucune erreur, aucun avertissement :
     juste une fenêtre dans le coin. */
  margin: auto;
  width: min(640px, calc(100vw - 32px));
  max-height: min(92vh, 860px);
  overflow-y: auto;
  padding: clamp(26px, 3.4vw, 40px);
  border: 1px solid rgba(255,138,82,.34);
  border-radius: 22px;
  background:
    radial-gradient(120% 90% at 12% 0%, rgba(255,106,61,.16), transparent 60%),
    linear-gradient(165deg, rgba(20,18,28,.98), rgba(10,10,16,.99));
  color: var(--paper);
  box-shadow: 0 0 0 1px rgba(255,138,82,.18),
              0 40px 90px -30px rgba(0,0,0,.9),
              0 0 90px rgba(255,106,61,.16);
}
.carte-contact::backdrop {
  background: rgba(6,6,12,.72);
  backdrop-filter: blur(4px);
}
.carte-contact-fermer-zone { display: flex; justify-content: flex-end; margin: -8px -6px 0 0; }
.carte-contact-fermer {
  width: 44px; height: 44px;          /* 44 px : la taille d'une cible au doigt */
  border: 0; border-radius: 50%;
  background: rgba(255,255,255,.07);
  color: var(--paper); font-size: 1.1rem; cursor: pointer;
  transition: background .2s;
}
.carte-contact-fermer:hover { background: rgba(255,106,61,.3); }
.carte-contact-sur {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: .74rem; letter-spacing: 2.4px; text-transform: uppercase;
  color: #ff8a52; margin: 0 0 8px;
}
body.v2 .carte-contact h2 {
  font-family: 'Cooper Black', Georgia, serif;
  font-size: clamp(1.5rem, 3vw, 2rem);
  margin: 0 0 22px;
  text-align: left;
}
/* Les coordonnées EN HAUT, comme demandé : elles doivent être joignables sans
   remplir quoi que ce soit. Quelqu'un qui préfère téléphoner ne remplira
   jamais un formulaire — lui cacher le numéro ne le convertit pas, il part. */
.carte-contact-coord {
  display: grid;
  /* ⚠️ 186 px ET PAS 170 : « contact@droneweb.ch » fait 19 caractères et
     débordait de sa carte, par-dessus le numéro de téléphone d'à côté (vu en
     capture). Une adresse e-mail ne se coupe pas n'importe où — d'où aussi
     `overflow-wrap` plus bas, qui garantit qu'aucune adresse plus longue ne
     puisse ressortir de sa boîte. */
  grid-template-columns: repeat(auto-fit, minmax(186px, 1fr));
  gap: 10px;
  margin-bottom: 22px;
}
.carte-contact-lien {
  display: block;
  padding: 12px 14px;
  border: 1px solid rgba(255,255,255,.12);
  border-radius: 12px;
  background: rgba(255,255,255,.04);
  text-decoration: none;
  color: var(--paper);
  transition: border-color .2s, background .2s;
}
.carte-contact-lien:hover { border-color: rgba(255,138,82,.6); background: rgba(255,106,61,.1); }
.carte-contact-etiquette {
  display: block;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: .64rem; letter-spacing: 1.6px; text-transform: uppercase;
  color: #ff8a52; margin-bottom: 3px;
}
.carte-contact-lien strong {
  display: block;
  font-size: .94rem;
  font-weight: 620;
  overflow-wrap: anywhere;   /* aucune adresse ne peut sortir de sa carte */
}
/* Le récapitulatif repris du ticket : on le montre au lieu de le faire
   retaper. `pre-line` parce qu'il arrive avec ses retours à la ligne. */
.carte-contact-contexte {
  white-space: pre-line;
  margin: 0 0 18px;
  padding: 13px 15px;
  border-left: 3px solid #6ff5dd;
  border-radius: 0 10px 10px 0;
  background: rgba(111,245,221,.08);
  font-size: .9rem;
  line-height: 1.5;
}
.carte-contact-form { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.carte-contact-champ { display: flex; flex-direction: column; gap: 6px; }
.carte-contact-champ--large { grid-column: 1 / -1; }
.carte-contact-champ > span {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: .68rem; letter-spacing: 1.4px; text-transform: uppercase;
  color: rgba(255,255,255,.72);
}
.carte-contact-champ input,
.carte-contact-champ textarea {
  width: 100%;
  padding: 13px 14px;
  border: 1px solid rgba(255,255,255,.16);
  border-radius: 11px;
  background: rgba(0,0,0,.34);
  color: var(--paper);
  font-family: inherit;
  font-size: 1rem;            /* ⚠️ jamais moins : sous 16 px, un iPhone zoome
                                 tout seul sur le champ et la page saute. */
  font-weight: 520;
  resize: vertical;
  transition: border-color .2s, box-shadow .2s;
}
.carte-contact-champ input:focus,
.carte-contact-champ textarea:focus {
  outline: none;
  border-color: #6ff5dd;
  box-shadow: 0 0 0 3px rgba(111,245,221,.2);
}
.carte-contact-erreur {
  grid-column: 1 / -1;
  margin: 0;
  padding: 11px 14px;
  border-radius: 10px;
  background: rgba(255,106,61,.14);
  border: 1px solid rgba(255,138,82,.4);
  color: #ffc9ae;
  font-size: .9rem;
}
.carte-contact-actions {
  grid-column: 1 / -1;
  display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
  margin-top: 4px;
}
/* Les deux voies de secours ne doivent pas rivaliser avec l'envoi : même
   hauteur de cible, moins de poids visuel. */
.carte-contact-autre {
  padding: 15px 22px;
  border: 1px solid rgba(111,245,221,.45);
  border-radius: 999px;
  background: transparent;
  color: #6ff5dd;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: .76rem; letter-spacing: 1.4px; text-transform: uppercase;
  cursor: pointer;
  transition: background .2s, border-color .2s;
}
.carte-contact-autre:hover { background: rgba(111,245,221,.12); border-color: #6ff5dd; }
.carte-contact-note {
  margin: 18px 0 0;
  font-size: .8rem;
  color: rgba(255,255,255,.55);
}
@media (max-width: 620px) {
  .carte-contact-form { grid-template-columns: 1fr; }
  .carte-contact-actions > * { width: 100%; justify-content: center; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   26. LES RESPIRATIONS ET LA VIDÉO DE FOND — 2026-08-11
   ───────────────────────────────────────────────────────────────────────────
   Matt : « plus d'espace entre les sections pour faire défiler la vidéo », et
   il a choisi 70 % de la hauteur d'écran. Le temps de la vidéo n'avance que
   dans ces intervalles — voir `fondVideo` dans js/agence-web-v2-effets.js.

   ⚠️ L'EMPILEMENT EST LA MOITIÉ DU TRAVAIL ICI. La page compte déjà quatre
   couches de fond, documentées plus haut :
     body::before (noir, z-index -1) · #gold-bg et le canvas #fond-vivant
     (z-index 0) · main (z-index 1).
   La vidéo se pose au même niveau que le fond vivant mais APRÈS lui dans le
   document : elle passe donc devant lui et reste derrière tout le contenu.
   Un `z-index: -1` l'aurait envoyée sous le noir de `body::before`, c'est-à-
   dire invisible — sans la moindre erreur.
   ═══════════════════════════════════════════════════════════════════════════ */
.aw-respiration {
  height: 70vh;
  /* ⚠️ Une hauteur sans contenu suffit : ce bloc ne doit RIEN peindre, la
     vidéo est derrière lui. Lui donner un fond la masquerait. */
  pointer-events: none;
}
/* Sur téléphone, 70 % d'un écran de 844 px font 590 px de vide entre chaque
   section, sur une page déjà longue à parcourir au pouce. La vidéo garde sa
   place, mais mesurée. */
@media (max-width: 900px) {
  .aw-respiration { height: 42vh; }
}

.aw-fond-video {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: var(--fond-video-opacite, 0);
  /* ⚠️ PAS DE TRANSITION SUR L'OPACITÉ. Elle est déjà pilotée image par image
     par le défilement : une transition ajouterait un retard sur une valeur qui
     change à chaque image, et la vidéo « traînerait » derrière le scroll. */
}
.aw-fond-video video {
  width: 100%;
  height: 100%;
  /* ⚠️ `cover` JUSQU'AU 2026-08-19, ET IL COUPAIT LA TÊTE. `cover` zoome la
     vidéo pour remplir l'écran : dès que la fenêtre est plus haute qu'un 16:9,
     le haut et le bas sont rognés. Sur un décor de chantier ça ne se voyait
     pas — on peut rogner du métal n'importe où. Sur une silhouette humaine, la
     tête part, et c'est la première chose que Matt a vue.
     `contain` fait tenir l'image entière quel que soit l'écran. Les bandes qui
     apparaissent sur les côtés sont NOIRES SUR FOND NOIR, donc invisibles :
     le décor hologramme est un vide noir, et html porte déjà #0a0912.
     ⛔ Si un jour un fond au décor CLAIR revient (chantier, extérieur), il
     faudra repasser à `cover` pour lui : la règle dépend du sujet, pas du
     réglage. Le fond de secours `fond-chantier-spatial-v4-allintra.mp4` est
     dans ce cas. */
  object-fit: contain;
  display: block;
}

/* ══ LE FOND GAUSSIEN — 2026-08-17 ═══════════════════════════════════════════
   Le canvas prend exactement la place de la vidéo. Il n'a PAS besoin de
   `object-fit` : le module cadre lui-même la scène selon le rapport de la
   vidéo et celui du canvas.
   ⛔ La vidéo n'est masquée qu'une fois le nuage RÉELLEMENT dessiné
   (`data-fond-etat="pret"`), jamais avant : dans l'ordre inverse, un appareil
   sans WebGL2 se retrouverait avec un fond vide au lieu du fond vidéo. */
.aw-fond-video canvas {
  /* ⚠️ `position: absolute` AJOUTÉ LE 2026-08-19, ET CE N'EST PAS COSMÉTIQUE.
     Sans lui le canvas reste DANS LE FLUX, occupe les 100 % de hauteur du
     cadre, et POUSSE LA VIDÉO EXACTEMENT UNE HAUTEUR D'ÉCRAN PLUS BAS.
     Mesuré : canvas top 0 / hauteur 900, vidéo top 900 dans un écran de 900 —
     donc hors champ, quoi qu'on fasse à son opacité.
     Le défaut était INVISIBLE tant que l'effet gaussien tournait : dans ce cas
     la vidéo passe volontairement à `opacity: 0` et on regarde le canvas. Il
     n'apparaît qu'en éteignant l'effet — ce qui a coûté une heure de recherche
     dans le CSS, la luminosité et le serveur avant qu'on pense à mesurer la
     POSITION de la vidéo.
     Le commentaire du bloc voisin disait déjà l'intention : « le canvas prend
     exactement la place de la vidéo ». Il la prenait au sens propre.
     Contrôle qui refuse la récidive : « le fond vidéo est bien DANS l'écran »
     dans outils/test-agence-web-v2.mjs. */
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
.aw-fond-video[data-fond-etat="pret"] video { opacity: 0; }
/* Secours : le module cache le canvas, la vidéo reprend sa place d'origine. */
.aw-fond-video[data-fond-etat="secours"] video { opacity: 1; }
/* ⚠️ UN VOILE SOMBRE, ET IL N'EST PAS DÉCORATIF. Une vidéo plein écran passe
   sous les bords du contenu qui arrive ; sans voile, le texte de la section
   suivante commence sa révélation sur une image claire et devient illisible
   une fraction de seconde. Le voile garde le contraste dans tous les cas. */
/* ⚠️ ET LE VOILE SUIT LA ZONE — travaux 100, 2026-08-11. Depuis que la vidéo
   reste visible DERRIÈRE le contenu, un voile constant ne peut plus convenir :
   il doit s'épaissir quand une section est à l'écran, pour que le texte garde
   son contraste, et s'alléger dans les vides, pour qu'on voie enfin l'image
   qu'on a payée. `--fond-video-vide` vaut 1 dans une respiration pleine et 0
   sur une section ; le script l'écrit à chaque image de défilement. */
.aw-fond-video::after {
  content: '';
  position: absolute;
  inset: 0;
  /* 0.60 → 0.20 jusqu'au 2026-08-18. ⚠️ CE VOILE SE MULTIPLIE AVEC L'OPACITÉ
     DU CALQUE (js/agence-web-v2-effets.js, OPACITE_SECTION) — personne ne
     l'avait écrit nulle part, et c'est ce qui a rendu le fond hologramme
     invisible : 0.26 × (1 − 0.60) ≈ 10 % de l'image seulement. Un décor plein
     cadre y survit, une silhouette fine sur du noir non. Descendu sur demande
     de Matt (« je veux l'hologramme en fond »).
     ⚠️ C'est LUI qui protège le contraste du texte par-dessus : on ne le baisse
     jamais sans relancer `node outils/test-agence-web-v2.mjs`. */
  --voile: calc(0.30 - 0.15 * var(--fond-video-vide, 0));
  background: linear-gradient(180deg,
    rgba(10,9,18, calc(var(--voile) + .10)),
    rgba(10,9,18, var(--voile)) 42%,
    rgba(10,9,18, calc(var(--voile) + .16)));
}

/* Mouvement réduit : la vidéo ne défile plus. On garde les respirations —
   elles font partie du rythme de lecture — mais l'image reste immobile. */
@media (prefers-reduced-motion: reduce) {
  .aw-fond-video { opacity: .18; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   8. MOUVEMENT RÉDUIT
   ───────────────────────────────────────────────────────────────────────────
   Un visiteur qui a demandé moins d'animation reçoit la page ENTIÈRE, lisible
   et complète — simplement immobile. Ce n'est pas une option de confort :
   c'est une exigence d'accessibilité, et une page qui l'ignore peut déclencher
   des malaises chez les personnes sensibles au mouvement.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  #fond-vivant { display: none; }
  body.v2 .metier-card,
  body.v2 .price-card,
  body.v2 .option-card,
  body.v2 .contact-info a,
  body.v2 .contact-info .card,
  .aw-carte,
  .hero-carte,
  body.v2 section h2 .accent,
  body.v2 .hero h1.display .accent { animation: none; }
  .porte, .porte-fond, .porte-texte, .porte-lien { transition: none; }
  .porte-texte { opacity: .9; transform: none; }
  /* Le parallaxe est écrit en variable par le JS : on la neutralise ici, sinon
     le fond des portes resterait décalé à sa dernière valeur. */
  .porte-fond { translate: 0 0; }
  /* Un titre collé qui suit le regard est un mouvement, lui aussi. */
  .aw-colle-titre { position: static; }
  /* Les boutons gardent leur dégradé et leur halo — c'est de la couleur, pas
     du mouvement — mais la lueur cesse de traverser et le reflet ne balaie
     plus. Le bouton reste exactement aussi lisible et aussi cliquable. */
  body.v2 .btn-primary,
  body.v2 #vers-dronetech .btn-cross { animation: none; }
  body.v2 .btn, body.v2 .btn::before { transition: none; }
  body.v2 .btn::before { display: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   TÉLÉPHONE UNIQUEMENT — demandé par Matt le 2026-08-12, photos à l'appui :
   « arrange la mise en page et la dynamique de la page. Je veux absolument pas
   que ça touche à la page PC, ce réglage est seulement pour téléphone. »

   ⚠️ TOUT CE BLOC EST SOUS 700 px, ET C'EST LA SEULE CHOSE QUI GARANTIT LA
   PROMESSE. 700 px passe au-dessus des grands téléphones (412, 430) et reste
   SOUS l'iPad portrait (768) : les tablettes gardent la mise en page du
   bureau, qui allait déjà bien. Aucune règle d'ici ne peut donc atteindre le
   PC — c'est vérifié par mesure, empreinte du bureau avant/après.

   ⚠️ ET CHAQUE SÉLECTEUR REPART DU SÉLECTEUR COMPLET qu'il annule. C'est le
   piège le plus cher de ce fichier, tombé quatre fois le 11/08 et une
   cinquième le 12/08 : une surcharge écrite plus court perd la cascade et ne
   fait RIEN, sans la moindre erreur. `.price-grid` (0,1,0) ne peut pas battre
   `.aw-parcours .metier-forfaits .price-grid.actif` (0,4,0).

   POURQUOI CE BLOC EXISTE — les quatre défauts mesurés sur les photos de Matt,
   tous invisibles à l'outil de mesure mobile parce qu'il n'ouvrait jamais un
   métier : les forfaits n'apparaissent qu'APRÈS ce clic.
     1. trois colonnes de 114 px sur un écran de 412 (« 209 CHF/M » tronqué) ;
     2. le ticket d'estimation déplié occupait 297 px, soit 32 % de l'écran,
        et recouvrait 100 % de la formule « Social » ;
     3. la note de prix s'étalait sur huit lignes, centrée et espacée ;
     4. la carte « Complet » paraissait décalée — conséquence de 1.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 700px) {

  /* 1. UNE FORMULE PAR LIGNE. Sur 412 px, trois colonnes donnent 114 px
     chacune : le prix se coupe et chaque mot prend une ligne. Le parcours de
     vente doit rester lisible avant d'être compact. */
  .aw-parcours .metier-forfaits .price-grid.actif,
  .aw-parcours .metier-forfaits .price-grid.deux.actif { grid-template-columns: 1fr; }
  .aw-parcours .metier-forfaits .price-card { padding: 22px 18px; }
  /* Le prix retrouve sa taille : il n'a plus à tenir dans un tiers d'écran. */
  .aw-parcours .metier-forfaits .price-card .amount { font-size: 2.6rem; }
  .aw-parcours .metier-forfaits .price-card li      { font-size: .95rem; }

  /* Les options aussi : une par ligne, sinon deux colonnes rendent le libellé
     et son prix illisibles côte à côte. */
  .aw-parcours .metier-options .option-grid.actif { grid-template-columns: 1fr; }

  /* 3. LA NOTE DE PRIX SE LIT, ELLE NE SE DÉCHIFFRE PAS. En capitales espacées
     et centrées, elle occupait huit lignes sur la photo de Matt. */
  .aw-parcours .metier-forfaits .price-note,
  .aw-parcours .price-note {
    letter-spacing: .02em;
    text-transform: none;
    text-align: left;
    font-size: .82rem;
    line-height: 1.5;
  }

  /* 2. LE TICKET DEVIENT UNE BARRE BASSE, ET IL NE RECOUVRE PLUS RIEN.
     Sur un écran de poche il n'existe aucune « place libre » à trouver : le
     balayage des bords, qui marche sur un écran large, n'a nulle part où
     aller. On change donc de forme plutôt que de place — pleine largeur, collé
     en bas, hauteur bornée — et on rend au contenu la hauteur qu'il occupe.
     ⚠️ On ne touche NI à `position` NI à `display` : ce sont les deux
     propriétés qui ont déjà fait disparaître ce ticket deux fois. */
  body.v2 #price-ticket {
    left: 12px !important;
    right: 12px !important;
    top: auto !important;
    bottom: 12px !important;
    width: auto !important;
    border-radius: 18px;
    padding: 12px 16px;
  }
  /* ⚠️ COMPACTE PAR DÉFAUT — 297 px mesurés, soit un TIERS de l'écran, pour
     une information qui tient en une ligne. Le détail des options n'a pas à
     être affiché en permanence : on garde le total et le bouton, c'est-à-dire
     ce qui fait avancer la vente, et le reste se déplie au toucher. */
  body.v2 #price-ticket #ticket-lines,
  body.v2 #price-ticket .ticket-note,
  body.v2 #price-ticket .ticket-title { display: none; }
  body.v2 #price-ticket.detail-ouvert #ticket-lines,
  body.v2 #price-ticket.detail-ouvert .ticket-note,
  body.v2 #price-ticket.detail-ouvert .ticket-title { display: block; }
  body.v2 #price-ticket.detail-ouvert { max-height: 60vh; overflow-y: auto; }
  body.v2 #price-ticket .ticket-total { margin: 0 0 10px; padding: 0; border: 0; }
  /* La barre mange le bas de l'écran : le parcours lui rend exactement sa
     hauteur, sinon elle masquerait la dernière option — le défaut qu'on vient
     de corriger, simplement déplacé d'un cran. */
  body.parcours-en-cours .aw-parcours { padding-bottom: 190px; }

  /* Replié, il reste une pastille — mais collée à droite, pas posée au milieu
     du texte : sur mobile le JS ne cherche plus d'emplacement (voir la garde
     dans js/agence-web-v2-effets.js). */
  body.v2 #price-ticket.replie {
    left: auto !important;
    right: 12px !important;
    width: auto !important;
    max-height: none;
  }

  /* 4. LE TITRE DU MÉTIER OUVERT NE DÉBORDE PLUS. Mesuré : « Restaurant ·
     café » dépassait de 16 px sur iPhone SE — un mot coupé net au bord. */
  .aw-parcours .metier-nom,
  .aw-parcours .metier-card h3 { overflow-wrap: anywhere; hyphens: auto; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LE CHARGEMENT SOIGNÉ — la jauge de l'écran d'attente          (2026-08-14)
   ───────────────────────────────────────────────────────────────────────────
   Demandé par Matt le 11/08. Le loader affichait un logo fixe 500 ms puis
   partait, sans jamais regarder le fond vidéo — qui se DÉROULE au scroll et
   gèle s'il n'est pas chargé. Mesuré par outils/mesurer-chargement-v2.mjs :
   en 3G on entrait avec 3,4 s de fond sur 10 ; en connexion lente, 23 secondes
   de logo immobile sans la moindre indication de progrès.
   Le JS qui la pilote est composé par outils/composer-agence-web-v2.mjs
   (bloc CHARGEMENT_AVANT/APRES) — la page est GÉNÉRÉE, ne pas la retoucher.

   Matt le 2026-08-14 : « met une page de chargement avec une belle animation ».
   D'où les orbites : le décor de la page est un CHANTIER SPATIAL, l'écran
   d'attente en reprend le vocabulaire au lieu d'être une barre anonyme.

   ⚠️ TOUT EST EMPILÉ EN GRILLE, PAS EN COLONNE FLEX.
   Première version : `flex-direction: column`. Les orbites se retrouvaient
   AU-DESSUS du symbole au lieu de tourner AUTOUR. Une grille à une seule
   cellule où tous les enfants prennent `grid-area: 1/1` les rend concentriques
   par construction — aucun calcul de décalage à maintenir.
   ═══════════════════════════════════════════════════════════════════════════ */
body.v2 #loader { display: grid; place-items: center; }
body.v2 #loader > .loader-scene,
body.v2 #loader > .loader-video,
body.v2 #loader > .loader-txt-bloc { grid-area: 1 / 1; }

/* ⚠️ LE SYMBOLE FAISAIT 88vmin — soit presque tout l'écran.
   C'était cohérent quand il ÉTAIT l'écran d'attente à lui seul. Avec des
   orbites autour, il faut qu'il leur laisse la place : sinon les orbites
   sortent du cadre et on ne voit qu'un logo géant traversé de traits. */
body.v2 .loader-video { width: min(26vmin, 168px); height: auto; max-width: none; max-height: none; }

body.v2 .loader-scene { width: min(60vmin, 380px); aspect-ratio: 1; pointer-events: none; }
body.v2 .loader-svg   { width: 100%; height: 100%; overflow: visible; }

/* ── LES TROIS ORBITES ─────────────────────────────────────────────────────
   Trois ellipses inclinées différemment, à des vitesses différentes : c'est
   ce décalage qui donne l'impression de volume. Trois tournant ensemble se
   liraient comme un seul objet plat.
   ⚠️ `transform-box: fill-box` + `transform-origin: center` : sans eux, un
   SVG tourne autour de l'origine du VIEWBOX (le coin haut-gauche) et l'ellipse
   part en vrille hors du cadre. C'est le piège classique de l'animation SVG. */
body.v2 .loader-orbite {
  fill: none; stroke: var(--line-strong); stroke-width: 1;
  transform-box: fill-box; transform-origin: center;
}
body.v2 .loader-orbite.o1 { animation: awOrbite 7s linear infinite; }
body.v2 .loader-orbite.o2 { animation: awOrbite 11s linear infinite reverse; opacity: .7; }
body.v2 .loader-orbite.o3 { animation: awOrbite 15s linear infinite; opacity: .45; }
@keyframes awOrbite { to { transform: rotate(360deg); } }

/* ── L'ARC DE PROGRESSION ──────────────────────────────────────────────────
   Il commence EN HAUT et tourne dans le sens des aiguilles : un cercle SVG
   démarre à 3 h, d'où la rotation de -90°. Le pointillé est posé par le JS
   (strokeDasharray / strokeDashoffset), pas ici — la longueur vient de
   getTotalLength(), pour ne pas écrire deux fois le même rayon. */
body.v2 .loader-arc-piste,
body.v2 .loader-arc {
  fill: none; stroke-width: 2.5; stroke-linecap: round;
  transform: rotate(-90deg); transform-box: fill-box; transform-origin: center;
}
body.v2 .loader-arc-piste { stroke: var(--line-strong); opacity: .35; }
body.v2 .loader-arc {
  stroke: url(#loaderDegrade);
  transition: stroke-dashoffset .22s linear;
  filter: drop-shadow(0 0 6px rgba(255,106,61,.55));
}

/* Deux satellites qui tournent : ils donnent le signe de vie que l'arc ne
   peut pas donner quand la progression stagne — et elle stagne, c'est le
   propre d'un téléchargement sur une ligne saturée. */
/* ⚠️ `view-box` ET SURTOUT PAS `fill-box` — défaut VU sur la capture
   `bureau-3-068.png` du 2026-08-14 : avec `fill-box`, l'origine est relative à
   la boîte du POINT (7 px de côté), donc `130px 130px` l'envoyait à 130 px de
   lui-même — un satellite perdu en haut à droite, hors de toute orbite.
   `view-box` place l'origine dans le repère du viewBox, où 130/130 EST le
   centre. La règle générale : dans un SVG, `transform-origin` en pixels n'a de
   sens qu'avec `transform-box: view-box`. */
body.v2 .loader-satellites { transform-box: view-box; transform-origin: 130px 130px; }
body.v2 .loader-sat { fill: var(--gold-bright); transform-box: view-box; transform-origin: 130px 130px; }
body.v2 .loader-sat.s1 { animation: awSat 3.4s linear infinite; }
body.v2 .loader-sat.s2 { animation: awSat 5.2s linear infinite reverse; opacity: .6; }
@keyframes awSat { to { transform: rotate(360deg); } }

/* Le texte descend sous les orbites sans sortir de la cellule de grille :
   `align-self: end` + une marge qui suit la taille de la scène. */
body.v2 .loader-txt-bloc { align-self: end; margin-bottom: max(10vh, 90px); }
body.v2 .loader-txt {
  margin: 0;
  font-family: 'JetBrains Mono', monospace;
  /* ⚠️ 0,72 D'OPACITÉ ÉTAIT TROP DISCRET, ET JE M'Y SUIS LAISSÉ PRENDRE :
     sur la capture `bureau-3-068.png` du 14/08 j'ai cru le texte DISPARU. La
     mesure a dit l'inverse — présent, visible, à y=796 sur 900. C'était donc un
     défaut de LISIBILITÉ, pas d'affichage : 11,9 px à 72 % sur un fond quasi
     noir, on ne le voit pas. Un indicateur de chargement qu'on ne lit pas ne
     sert à rien. Leçon générale : « je ne le vois pas » et « il n'est pas là »
     sont deux constats différents, et seul le second se code. */
  font-size: 0.74rem; letter-spacing: 2px; text-transform: uppercase;
  color: var(--paper); opacity: 0.92;
  text-align: center;
}
body.v2 #loader-pct { color: var(--gold-bright); opacity: 1; font-weight: 700; }

/* Mouvement réduit : le loader est déjà sauté par le JS dans ce cas
   (skipVideoIntro). La règle reste, pour que rien n'anime si l'écran apparaît
   quand même — une animation oubliée est un défaut d'accessibilité qui ne se
   voit sur aucune capture. */
@media (prefers-reduced-motion: reduce) {
  body.v2 .loader-orbite, body.v2 .loader-sat { animation: none; }
  body.v2 .loader-arc { transition: none; }
}
