/* =====================================================================
   Only Way Garden — styles.css (V3)
   Feuille de style unique du site (mobile-first, aucune dépendance).
   Toutes les couleurs viennent de tokens.css (var(--…)) — JAMAIS de hex ici.
   Sommaire :
     1. Reset & base            7. Étapes & calendrier saisonnier
     2. Utilitaires             8. Réalisations / avis
     3. Boutons                 9. Espace pro / Zone / FAQ
     4. Header & navigation    10. CTA final / Contact / Footer
     5. Hero + formulaire      11. Barre mobile / bulle WA / divers
     6. Services / tarifs / estimateur
   ===================================================================== */

/* ---------- 1. Reset léger & base ----------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--background);
  color: var(--foreground);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin: 0 0 0.5em;
  text-wrap: balance;
  color: var(--primary);
}
h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-1); }

p { margin: 0 0 1em; }
ul, ol { margin: 0 0 1em; padding-left: 1.25em; }
img { max-width: 100%; height: auto; display: block; }
a { color: var(--primary-mid); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { text-decoration-thickness: 2px; }

section[id], div[id] { scroll-margin-top: calc(var(--header-h) + 1rem); }

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

::selection { background: var(--primary); color: var(--primary-foreground); }

/* ---------- 2. Utilitaires ------------------------------------------ */
.container {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--section-y); }
.section--muted { background: var(--muted); }

/* Ecart entre le chapeau et le contenu de la section, resserre le 28/07 en
   meme temps que `--section-y` : un titre ne doit pas flotter loin de ce
   qu'il annonce. */
.section-head { max-width: 46rem; margin-bottom: clamp(var(--space-5), 3vw, var(--space-9)); }
.section-head .kicker {
  display: inline-block;
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--primary-mid);
  margin-bottom: var(--space-2);
}
.section-head p { color: var(--muted-foreground); font-size: var(--step-0); }
.section-head--center { margin-inline: auto; text-align: center; }

/* --- Utilitaires ------------------------------------------------------
   Jeu volontairement restreint, pour qu'aucun style= ne traîne dans le
   HTML. Règle : si un besoin revient plus de deux fois, il devient une
   classe ici plutôt qu'un attribut style. */
.u-muted    { color: var(--muted-foreground); }
.u-center   { text-align: center; }
.u-small    { font-size: var(--step--1); }
.u-optional { font-weight: 400; color: var(--muted-foreground); }
.u-emoji    { font-size: 3rem; margin: 0 0 var(--space-4); }
.u-h2-sm    { font-size: var(--step-2); }
.u-mt       { margin-top: var(--space-8); }
.u-mt-lg    { margin-top: clamp(var(--space-10), 6vw, var(--space-16)); }
.u-actions  {
  margin-top: var(--space-8);
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
  justify-content: center;
}
/* Le sprite SVG : présent dans le DOM, jamais affiché. */
.sprite { display: none; }

/* Pages de texte : mentions légales, confidentialité, merci, 404 */
.prose { max-width: 46rem; }
.prose h2 { font-size: var(--step-1); }
.page-centree { max-width: 40rem; margin-inline: auto; text-align: center; }

.icon {
  width: 1.35em; height: 1.35em;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: -0.25em;
}

.card {
  background: var(--card);
  color: var(--card-foreground);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
}

/* ---------- 3. Boutons ---------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  /* Plancher de zone tactile confortable : voir --tap-target-comfort dans
     tokens.css. Pour alléger un bouton, jouer sur le padding et la taille
     de texte, jamais sur cette hauteur. */
  min-height: var(--tap-target-comfort);
  padding: var(--space-2) var(--space-5);
  border: 2px solid transparent;
  border-radius: var(--radius-pill);
  font: inherit;
  font-weight: 700;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  touch-action: manipulation;
  transition: transform var(--duration-fast) var(--ease), box-shadow var(--duration-base) var(--ease), background-color var(--duration-base) var(--ease), color var(--duration-base) var(--ease), border-color var(--duration-base) var(--ease);
}
.btn:active { transform: scale(0.98); }
.btn[disabled] { opacity: 0.6; cursor: progress; }

.btn-cta {
  background: var(--accent);
  color: var(--accent-foreground);
  box-shadow: var(--shadow-sm);
}
.btn-cta:hover { background: var(--primary); box-shadow: var(--shadow-md); }

/* ⚠️ LE BOUTON D'ENVOI DU HERO EST BLEU DEPUIS LE 01/08, et l'ambre est
   passée au bouton « Calculer mon prix » posé juste dessous (demande de
   Quentin). L'ancienne règle forçait ici `--highlight` ; elle a été retirée,
   et le bouton retrouve simplement son `.btn-cta`, donc `--accent`.
   ⚠️ Le sélecteur portait aussi sur `.contact-form`, qui n'existe plus dans
   aucune page depuis la suppression du formulaire du bas le 28/07.
   ⚠️ Le calculateur n'est PAS concerné : son formulaire porte la classe
   `.calc`, et son bouton « Envoyer ma demande » reste en ambre.

   Le bouton du calculateur dans le hero prend l'ambre et son ombre : c'est
   désormais lui qui doit sauter aux yeux. */
.hero-form .btn-highlight {
  box-shadow: var(--shadow-highlight);
}
.hero-form .btn-highlight:hover {
  box-shadow: var(--shadow-highlight-hover);
}
/* Il suit le bouton d'envoi, séparé par le message de retour. */
.hero-form .btn-highlight { margin-top: var(--space-3); }

.btn-outline {
  background: transparent;
  color: var(--primary);
  border-color: var(--primary);
}
.btn-outline:hover { background: var(--primary); color: var(--primary-foreground); }

.btn-outline--light {
  background: transparent;
  color: var(--primary-foreground);
  border-color: color-mix(in oklab, var(--primary-foreground) 60%, transparent);
}
.btn-outline--light:hover { background: var(--primary-foreground); color: var(--primary); }

.btn-wa {
  background: var(--whatsapp);
  color: var(--whatsapp-foreground);
  box-shadow: var(--shadow-sm);
}
.btn-wa:hover { filter: brightness(0.92); box-shadow: var(--shadow-md); }

/* Grands CTA. Le texte suivait `--step-1` (jusqu'à 1,45 rem), ce qui rendait
   ces boutons trop imposants : échelle propre, entre le corps de texte et
   --step-1. */
.btn-lg {
  padding: var(--space-3) var(--space-7);
  font-size: var(--text-btn-lg);
}
/* 40 px : bouton compact. Sous le plancher tactile, donc releve au doigt
   dans le bloc ZONES TACTILES en fin de feuille. */
.btn-sm { min-height: 40px; padding: var(--space-1) var(--space-4); font-size: var(--text-sm); }
.btn-block { width: 100%; }

/* ---------- 4. Header & navigation ---------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: color-mix(in oklab, var(--background) 92%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}
.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: var(--header-h);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--primary);
  text-decoration: none;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-md);
  line-height: 1.1;
}
.brand svg { width: 2.2rem; height: 2.2rem; }
.brand .brand-txt small {
  display: block;
  font-family: var(--font-sans);
  font-size: var(--text-3xs);
  font-weight: 600;
  letter-spacing: 0.28em;
  color: var(--muted-foreground);
}

.main-nav { display: none; }

.header-actions { display: flex; align-items: center; gap: var(--space-2); }

.header-phone {
  display: none;
  align-items: center;
  gap: var(--space-2);
  font-weight: 700;
  font-size: var(--text-md);
  color: var(--primary);
  text-decoration: none;
  white-space: nowrap;
}
/* L'icone prend la couleur du numero, et la garde au survol : deux bleus
   differents cote a cote sur un meme lien se lisaient comme deux elements
   distincts. `currentColor` suffit, et suit automatiquement le survol. */
.header-phone .icon { color: currentColor; }
.header-phone:hover { color: var(--primary-mid); }
@media (min-width: 30rem) { .header-phone { display: inline-flex; } }

/* Le numero de telephone est de retour dans l'entete a TOUTES les largeurs
   (28/07). Il avait du en sortir tant que le menu etait centre : la grille
   centree donne aux deux colonnes laterales la largeur de la plus lourde,
   et `.container` etant plafonne a 1200 px, les actions finissaient par
   deborder sur le menu. Le menu etant desormais aligne a gauche, cette
   contrainte n'existe plus : la place manquante se prend simplement sur
   l'espace libre du milieu. */

/* Deux boutons dans l'entete au lieu d'un : ils s'allegent par le padding et
   la taille de texte, JAMAIS par la hauteur. Le plancher de 48 px est celui
   des zones tactiles exige par Lighthouse, le SEO 100 en depend. */
.header-actions .btn { padding-inline: var(--space-4); font-size: var(--text-sm); }

/* Bouton AMBRE, la couleur de l'action la plus engageante de la page :
   « Calculer mon prix » dans l'entete et le menu mobile, « Demander un devis
   international » dans sa section. Les autres boutons pleins sont bleus :
   deux pleins de la meme famille cote a cote, et aucun des deux ne ressort.
   ⚠️ Nom de ROLE et non d'usage : la classe s'appelait « btn-calc » tant
   qu'elle ne servait qu'au calculateur, ce qui est devenu faux des le
   deuxieme emploi. `.btn-highlight` porte la couleur, `.header-calc` porte
   l'affichage selon la largeur : les separer permet de reprendre le meme
   bouton dans le menu mobile, ou il doit rester visible. */
.btn-highlight {
  background: var(--highlight);
  color: var(--primary);
  box-shadow: var(--shadow-sm);
}
/* Bouton BLANC plein : l'action principale sur un fond bleu nuit, quand
   l'ambre est déjà pris par une action voisine.

   ⚠️ Il existe parce que `.btn-cta` ne peut PAS servir sur ces fonds-là.
   Mesuré au canvas le 01/08 : le bleu moyen sur le bleu nuit donne un
   contraste de **2,0**, sous le minimum de 3,0 exigé pour un élément
   d'interface. Le blanc y mesure **13,11**, l'ambre 7,68. Le pied de page
   portait ce bouton bleu invisible depuis l'origine, alors que le §8 du
   HANDOFF affirmait qu'il était en ambre. */
.btn-light {
  background: var(--primary-foreground);
  color: var(--primary);
  box-shadow: var(--shadow-sm);
}
.btn-light:hover {
  background: color-mix(in oklab, var(--primary-foreground) 86%, var(--primary));
  color: var(--primary);
}

.btn-highlight:hover {
  background: var(--primary);
  color: var(--primary-foreground);
  box-shadow: var(--shadow-md);
}

.nav-burger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap-target-min); height: var(--tap-target-min);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--primary);
  cursor: pointer;
}
.nav-burger:hover { border-color: var(--primary-mid); }

/* Les deux boutons d'en-tete n'existent qu'au-dessus du seuil du menu de
   bureau : sous ce seuil c'est le menu burger qui les porte. Declares ICI,
   avant la media query qui les affiche : une media query n'ajoute aucune
   specificite, une regle ecrite plus bas l'emporterait sur elle. */
.header-cta, .header-calc { display: none; }
.header-calc { white-space: nowrap; }

/* Menu mobile : placé HORS du <header> dans le HTML — un parent avec
   backdrop-filter piège les enfants position:fixed dans sa propre boîte. */
.mobile-menu {
  position: fixed;
  top: var(--header-h);
  left: 0;
  right: 0;
  height: calc(100vh - var(--header-h));
  height: calc(100dvh - var(--header-h));
  z-index: var(--z-overlay);
  background: var(--background);
  padding: var(--space-5) var(--gutter) var(--space-20);   /* marge basse : dernier lien atteignable */
  display: none;
  flex-direction: column;
  gap: var(--space-1);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.mobile-menu.is-open { display: flex; }
/* Scroll de la page bloqué + barre d'actions masquée quand le menu est ouvert */
html.nav-open, body.nav-open { overflow: hidden; }
.mobile-menu a {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  color: var(--primary);
  text-decoration: none;
  padding: var(--space-3) var(--space-1);
  border-bottom: 1px solid var(--border);
}
.mobile-menu a:hover { color: var(--primary-mid); }
.mobile-menu .btn { margin-top: var(--space-5); }

/* Seuil du menu de bureau : 74 rem. REMESURE quatre fois le 28/07 : 69 rem
   a l'origine, 75 rem avec le menu centre et deux boutons, 70 rem une fois le
   menu aligne a gauche, 74 rem depuis que « International » est la sixieme
   entree de menu.
   La formule n'est pas « marque + nav + actions » : la grille centre le nav
   en donnant aux DEUX colonnes laterales la largeur de la plus lourde, celle
   des actions. Il faut donc 2 x actions + nav + ecarts + gouttieres :
     marque   163
     nav      398 (Services, Tarifs, Entreprises, Zones, International, FAQ)
     actions  482 (telephone 113, langue 44, calcul 156, devis 120)
     + les ecarts et les deux gouttieres
     = 1139 px, soit 71,2 rem sur le papier. Mesure a l'ecran : a 1152 px
     il ne restait que 2 px entre le nav et les actions, la barre de
     defilement mangeant 15 px que le calcul ignorait. Seuil pose a 74 rem.
   ⚠️ L'ENTETE EST A SA LIMITE, et la marge se MESURE, elle ne se calcule
     pas : la barre de defilement retire une quinzaine de pixels. Une entree de menu
     de plus, ou un libelle nettement plus long, impose de choisir : sortir le
     telephone (il vaut 125 px), raccourcir un libelle, ou remonter le seuil
     et laisser le burger prendre plus de largeurs.
   ⚠️ Le telephone dispararait EXACTEMENT au meme seuil, et ce n'est pas un
   hasard : s'il restait visible ne serait-ce que sur une bande de 1 rem, il
   ajouterait 125 px aux actions, soit 250 px au besoin total, et le nav
   toucherait les actions sur toute cette bande.
   C'est aussi pourquoi le numero de telephone reste en icone seule sous
   91 rem : chaque pixel des actions se paie deux fois.
   En dessous du seuil, le burger prend le relais : un entete a l'etroit est
   pire qu'un burger. REMESURER si un libelle s'allonge. */
@media (min-width: 74rem) {
  .nav-burger, .mobile-menu { display: none !important; }
  .main-nav { display: flex; gap: var(--space-0-5); }
  .main-nav a {
    color: var(--foreground);
    text-decoration: none;
    font-weight: 600;
    font-size: var(--text-sm);
    /* Padding horizontal reduit a `--space-1` le 28/07, quand « International »
       est devenu la sixieme entree : six entrees x 8 px de padding en trop,
       c'etaient 48 px qui manquaient a l'autre bout de la barre. Le padding
       VERTICAL reste a `--space-2` : c'est lui qui donne la hauteur de la
       zone cliquable. */
    padding: var(--space-2) var(--space-1);
    border-radius: var(--radius);
  }
  .main-nav a:hover { color: var(--primary-mid); background: var(--muted); }
  .header-cta, .header-calc { display: inline-flex; }
}

/* --- Menu Zones -------------------------------------------------------
   Repris du menu Villes du jardin, avec ses DEUX filets de survol : le pont
   invisible qui comble l'espace sous le bouton, et le delai de grace a la
   fermeture. Sans eux, le panneau se referme sous le curseur pendant la
   descente. Ne jamais fermer par `display: none`, ni ajouter
   `pointer-events: none` : `visibility` coupe deja les evenements, et le
   rattrapage en depend. */
.nav-zones { position: relative; display: none; }
@media (min-width: 74rem) { .nav-zones { display: block; } }
.nav-zones > button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font: inherit;
  font-weight: 600;
  font-size: var(--text-sm);
  color: var(--foreground);
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: var(--space-2);
  border-radius: var(--radius);
}
.nav-zones > button:hover { color: var(--primary-mid); background: var(--muted); }
.nav-zones > button .icon {
  width: 0.9em;
  height: 0.9em;
  transition: transform var(--duration-base) var(--ease);
}
.nav-zones-menu {
  position: absolute;
  top: calc(100% + var(--space-1));
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: min(22rem, calc(100vw - 2rem));
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-lg);
  padding: var(--space-3);
  display: grid;
  gap: var(--space-0-5);
  z-index: calc(var(--z-header) + 1);
  visibility: hidden;
  opacity: 0;
  transition: opacity var(--duration-fast) var(--ease) var(--delay-hover-grace),
              visibility 0s linear calc(var(--duration-fast) + var(--delay-hover-grace));
}
/* Le pont : il comble le vide entre le bouton et le panneau, pour que la
   souris ne traverse jamais une zone morte. */
.nav-zones-menu::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--space-4) * -1);
  height: var(--space-4);
}
.nav-zones:hover .nav-zones-menu,
.nav-zones.is-open .nav-zones-menu,
.nav-zones:has(button:focus-visible) .nav-zones-menu,
.nav-zones-menu:focus-within {
  visibility: visible;
  opacity: 1;
  transition: opacity var(--duration-fast) var(--ease) 0s, visibility 0s linear 0s;
}
.nav-zones:hover > button .icon,
.nav-zones.is-open > button .icon,
.nav-zones:has(button:focus-visible) > button .icon { transform: rotate(180deg); }
/* Le panneau ne porte plus que trois noms, sans sous-titre : `block`
   suffit, la grille servait a empiler le libelle et son `small`. */
.nav-zones-menu a {
  display: block;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius);
  color: var(--foreground);
  text-decoration: none;
  font-size: var(--text-sm);
  font-weight: 600;
}
.nav-zones-menu a:hover { background: var(--muted); color: var(--primary-mid); }

/* Intertitre du menu mobile : il annonce le groupe des zones, sans etre un
   lien. Sans lui les trois regions se lisent comme trois rubriques de plus. */
.mobile-menu-titre {
  margin: var(--space-4) 0 0;
  font-size: var(--text-2xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}

/* NAV ALIGNE A GAUCHE, contre la marque (choix de Quentin, 28/07).
   ⚠️ Ecart assume avec le skill site_vitrine_onlyway, qui centre le menu sur
   les autres sites onlyway. La raison est mesuree, pas esthetique : un menu
   centre impose que les DEUX colonnes laterales fassent la largeur de la
   plus lourde, celle des actions. Avec un telephone, un selecteur de langue
   et deux boutons, cela reclamait 1165 px quand `.container` est plafonne a
   1200 px : l'entete n'avait plus aucune marge, et les actions finissaient
   par recouvrir le menu sur les grands ecrans.
   Aligne a gauche, le besoin retombe a marque + nav + actions, et tout
   l'espace excedentaire va au milieu, la ou il ne gene personne.
   Trois colonnes `auto auto 1fr` : la troisieme absorbe le reste et pousse
   les actions a droite. */
@media (min-width: 74rem) {
  .site-header .container {
    display: grid;
    grid-template-columns: auto auto minmax(0, 1fr);
  }
  .main-nav { margin-left: var(--space-2); }
  .header-actions { justify-self: end; }
}
/* ---------- 5. Hero avec formulaire de devis -------------------------- */
.hero {
  position: relative;
  background: var(--primary);
  color: var(--primary-foreground);
  overflow: hidden;
}
/* PHOTO DU HERO — posée le 01/08 : un camion de déménagement dans une rue
   bruxelloise. Générée par tools/build_images.py.
   L'emplacement est un DIV avec `background-image` et non une balise <img> :
   sans fichier, il ne montre rien et le bleu nuit reste seul, là où une
   <img> sans source afficherait une icône de fichier cassé.

   ⚠️ LA PETITE VERSION EST SERVIE PAR DÉFAUT, la grande seulement au-dessus
   de 48 rem. Mobile-first jusque dans les images : un téléphone n'a aucune
   raison de télécharger 65 ko quand 27 suffisent, et le hero est au-dessus
   de la ligne de flottaison.

   ⚠️ L'OPACITÉ SE RÈGLE ICI, et elle n'est pas décorative : le hero porte
   du texte BLANC. Elle est passée de 0,38 à 0,30 le 01/08, en même temps
   que la photo, et c'est une MESURE qui l'a décidé, pas un jugement.

   La note précédente annonçait « sous AA au-delà de ~0,45 ». C'était vrai
   d'une photo sombre. Celle-ci porte un camion blanc et un ciel clair : le
   composite a été recalculé pixel par pixel sur la moitié gauche, celle qui
   porte le texte.

     opacité   contraste moyen   0,5 % les plus clairs
       0,38         8,55               4,12   <- SOUS le seuil AA de 4,5
       0,34         8,98               4,62      juste au-dessus
       0,30         9,40               5,25   <- retenu, marge confortable

   ⚠️ La moyenne ne suffit pas à juger : à 0,38 elle donnait 8,55, très
   confortable, alors que le ciel et la carrosserie faisaient tomber le
   texte sous AA à ces endroits-là. C'est le PIRE cas qu'il faut mesurer.
   Changer de photo impose donc de refaire ce calcul. */
.hero .hero-bg {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  background: center / cover no-repeat url("../assets/img/hero-800.webp");
  opacity: 0.30;
}
@media (min-width: 48rem) {
  .hero .hero-bg { background-image: url("../assets/img/hero-1672.webp"); }
}
.hero .container {
  position: relative;
  z-index: 1;
  padding-block: clamp(var(--space-10), 6vw, var(--space-18));
  display: grid;
  gap: clamp(var(--space-8), 4vw, var(--space-12));
  align-items: center;
}
@media (min-width: 62rem) {
  .hero .container { grid-template-columns: 1.05fr 0.95fr; }
}

.hero h1 { color: inherit; margin-bottom: var(--space-2); }
.hero h1 .h1-accent { color: var(--highlight); }
.hero .hero-kicker {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--highlight);
  margin-bottom: var(--space-4);
}
.hero .lede {
  font-size: var(--step-1);
  max-width: 34rem;
  color: color-mix(in oklab, var(--primary-foreground) 92%, transparent);
}
.hero-points {
  list-style: none;
  padding: 0;
  margin: var(--space-6) 0 0;
  display: grid;
  gap: var(--space-2);
  font-weight: 600;
}
.hero-points li { display: flex; align-items: center; gap: var(--space-2); }
.hero-points .icon { color: var(--highlight); }
.hero-phone-line {
  margin: var(--space-6) 0 0;
  font-size: var(--step-0);
  color: color-mix(in oklab, var(--primary-foreground) 85%, transparent);
}
/* L'icone est DANS le lien, alignee sur la ligne de base du numero plutot
   que posee devant : `inline-flex` sur le lien garde les deux solidaires si
   la phrase passe a la ligne. */
.hero-phone-line a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--highlight);
  font-weight: 700;
  white-space: nowrap;
  vertical-align: bottom;
}
.hero-phone-line a .icon { width: 1.05em; height: 1.05em; flex: none; }

/* Carte formulaire dans le hero */
.hero-form {
  background: var(--card);
  color: var(--card-foreground);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-lg);
  padding: clamp(var(--space-5), 3vw, var(--space-7));
}
.hero-form h2 {
  font-size: var(--step-2);
  margin-bottom: var(--space-1);
}
.hero-form .form-intro {
  font-size: var(--step--1);
  color: var(--muted-foreground);
  margin-bottom: var(--space-4);
}
.hero-form .form-grid { display: grid; gap: var(--space-3); }
@media (min-width: 30rem) {
  .hero-form .form-grid { grid-template-columns: 1fr 1fr; }
  .hero-form .form-grid .full { grid-column: 1 / -1; }
}
.hero-form .btn { margin-top: var(--space-4); }
.hero-form .form-trust {
  list-style: none;
  padding: 0;
  margin: var(--space-4) 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
  font-size: var(--step--1);
  color: var(--muted-foreground);
}
.hero-form .form-trust li { display: inline-flex; align-items: center; gap: var(--space-1); }
.hero-form .form-trust .icon { color: var(--success); width: 1.05em; height: 1.05em; }

/* Champs de formulaire (communs hero + contact) */
.field { display: grid; gap: var(--space-1); }
.field > label { font-weight: 600; font-size: var(--text-sm); }
.field .hint { font-size: var(--step--1); color: var(--muted-foreground); margin: 0; }

/* Champs de saisie.
   Selecteur par EXCLUSION, et non par liste de types autorises. Le site
   jardin listait text, email, tel et number ; le champ `date` ajoute ici
   n'y figurait pas et heritait donc du rendu natif du navigateur :
   monospace 13 px, 21 px de haut, sans bordure ni arrondi, au milieu de
   champs de 46 px. Une liste d'autorisation oublie toujours le type
   suivant (date, url, search, time...). Une liste d'exclusion, elle, ne
   laisse passer que ce qui doit VRAIMENT garder son apparence native. */
input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="image"]):not([type="range"]):not([type="color"]):not([data-spin]),
select, textarea {
  font: inherit;
  color: var(--foreground);
  background: var(--card);
  border: 1.5px solid var(--input);
  border-radius: var(--radius);
  padding: var(--space-2) var(--space-3);
  min-height: 46px;
  width: 100%;
  /* Interligne serre et EXPLICITE, pose apres `font: inherit` qui le
     reinitialise. Sans lui, la hauteur d'un champ depend de son type :
     un `input` texte se dimensionne sur l'interligne du corps de texte
     (47 px sur grand ecran) tandis qu'un `select` retombe sur `normal`
     et s'arrete au `min-height` (46 px). Un pixel d'ecart entre deux
     champs de la meme ligne, c'est peu, mais ca se voit. Ici le contenu
     reste toujours sous 46 px : c'est le `min-height` qui decide, pour
     tous les types et a toutes les largeurs. */
  line-height: 1.25;
}
/* La zone de message, elle, est multiligne : elle a besoin d'air. */
textarea { min-height: 11rem; resize: vertical; line-height: 1.5; }

/* Le champ date resiste au style commun : les navigateurs lui imposent une
   hauteur interne et une icone de calendrier. On neutralise le rendu natif,
   on centre le contenu sur la hauteur du champ, et on teinte l'icone comme
   le reste de l'interface plutot que de la laisser en noir plein. */
input[type="date"] {
  appearance: none;
  -webkit-appearance: none;
}
/* `line-height: normal` sur le bloc interne, et pas sur le champ lui-meme :
   sans cela le champ mesure 46,4 px la ou tous les autres font 46 px. La
   fraction vient de l'ascendante et de la descendante de la police, que le
   bloc de date reporte sur sa propre hauteur au lieu de suivre le
   `min-height` commun. Quatre dixiemes de pixel ne se voient pas seuls,
   mais se voient tres bien a cote d'un champ voisin sur la meme ligne. */
input[type="date"]::-webkit-datetime-edit { padding: 0; line-height: normal; }
input[type="date"]::-webkit-calendar-picker-indicator {
  cursor: pointer;
  opacity: 0.5;
  transition: opacity var(--duration-fast) var(--ease);
}
input[type="date"]::-webkit-calendar-picker-indicator:hover,
input[type="date"]:focus::-webkit-calendar-picker-indicator { opacity: 1; }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--ring);
  box-shadow: var(--shadow-focus);
}

/* Zone d'ajout de photos */
/* CHAMP PIEGE ANTI-ROBOTS. Il doit rester dans le flux du formulaire pour
   qu'un robot le remplisse, mais invisible pour un humain. D'ou le
   deplacement hors ecran plutot qu'un `display: none`, que certains robots
   savent detecter et ignorer.
   ⚠️ Sans cette regle, le libelle « Ne remplissez pas ce champ » s'affiche
   en clair sous le formulaire. C'est arrive le 28/07. */
.hp {
  position: absolute;
  left: -9999px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-feedback { margin: var(--space-2) 0 0; font-size: var(--text-sm); font-weight: 600; color: var(--success); }

/* Bandeau fin de réassurance */
.reassure {
  background: var(--primary-mid);
  color: var(--primary-foreground);
}
.reassure ul {
  list-style: none;
  margin: 0;
  padding: var(--space-3) var(--gutter);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-1) var(--space-7);
  font-size: var(--step--1);
  font-weight: 600;
  max-width: var(--container);
  margin-inline: auto;
}
.reassure li { display: inline-flex; align-items: center; gap: var(--space-2); white-space: nowrap; }
.reassure .icon { width: 1.1em; height: 1.1em; }

/* ---------- 6. Services / tarifs / estimateur ------------------------ */
.services-grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1fr;
}
@media (min-width: 34rem) { .services-grid { grid-template-columns: 1fr 1fr; } }
/* Six services, donc DEUX LIGNES DE TROIS sur grand ecran (le jardin en a
   huit et tient sur deux lignes de quatre). Ne pas repasser a 4 colonnes
   sans ajouter deux services, sinon la seconde ligne reste boiteuse. */
@media (min-width: 62rem) { .services-grid { grid-template-columns: repeat(3, 1fr); } }

.card-service {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  /* Pas de `cursor: pointer` ici, contrairement au jardin : la-bas la carte
     ouvre une fenetre de detail, ici elle ne fait rien. Une main qui invite
     a cliquer sur un element inerte est un mensonge d'interface. */
  transition: box-shadow var(--duration-base) var(--ease), transform var(--duration-base) var(--ease), border-color var(--duration-base) var(--ease);
}
.card-service:hover,
.card-service:focus-visible {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
  border-color: var(--primary-mid);
}
/* ⚠️ `16 / 6` et non `4 / 3` : une bande, pas un carre. Six cartes en 4/3
   auraient double la hauteur de la section services. Meme cadrage pour la
   photo definitive et pour l'emplacement d'attente, afin que rien ne bouge
   le jour ou les images arrivent. */
.card-service > img,
.card-service .service-photo {
  aspect-ratio: 16 / 6;
  object-fit: cover;
  width: 100%;
}
/* Emplacement d'attente : une bande vide sur le gris de fond, et non une
   <img> sans source, qui afficherait une icone de fichier casse dans la
   moitie des navigateurs. L'icone du service, elle, est revenue devant son
   titre : la meme icone deux fois dans une carte de 300 px se lirait comme
   une erreur. */
.card-service .service-photo {
  background: var(--muted);
  border-bottom: 1px solid var(--border);
}
/* L'icone devant le titre : `inline-flex` sur le h3 pour qu'un titre sur
   deux lignes ne passe pas SOUS l'icone, et `flex: none` pour qu'elle ne
   s'ecrase pas quand le titre est long. */
.card-service h3 {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.card-service h3 .icon {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  color: var(--primary-mid);
}
.card-service .service-body {
  padding: var(--space-4) var(--space-5) var(--space-5);
  display: flex;
  flex-direction: column;
  flex: 1;
}
.card-service h3 { font-size: var(--text-lg); margin-bottom: var(--space-2); }
.card-service .service-teaser {
  font-size: var(--text-sm);
  color: var(--muted-foreground);
  margin: 0 0 var(--space-4);
}

/* --- Cartes tarifaires ---------------------------------------------- */
.price-grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1fr;
  align-items: start;
}
@media (min-width: 56rem) { .price-grid { grid-template-columns: repeat(3, 1fr); } }

.price-card {
  padding: var(--space-7) var(--space-6);
  display: flex;
  flex-direction: column;
  height: 100%;
}
.price-card .price-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-lg);
  color: var(--primary);
  margin-bottom: var(--space-0-5);
}
.price-card .price-sub { font-size: var(--step--1); color: var(--muted-foreground); margin-bottom: var(--space-4); }
.price-card .price-amount {
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: 700;
  color: var(--primary);
  line-height: 1;
  margin: 0;
}
.price-card .price-amount small {
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: 600;
  color: var(--muted-foreground);
}
.price-card ul {
  list-style: none;
  padding: 0;
  margin: var(--space-5) 0 var(--space-6);
  display: grid;
  gap: var(--space-2);
  font-size: var(--text-sm);
}
.price-card ul li { display: flex; gap: var(--space-2); align-items: baseline; }
.price-card ul .icon { width: 0.95em; height: 0.95em; color: var(--success); }
.price-card .btn { margin-top: auto; }

/* Carte mise en avant */
.price-card--featured {
  position: relative;
  background: var(--primary);
  border-color: var(--primary);
  box-shadow: var(--shadow-lg);
}
.price-card--featured .price-label,
.price-card--featured .price-amount { color: var(--primary-foreground); }
.price-card--featured .price-sub,
.price-card--featured .price-amount small { color: color-mix(in oklab, var(--primary-foreground) 78%, transparent); }
.price-card--featured ul { color: var(--primary-foreground); }
.price-card--featured ul .icon { color: var(--highlight); }
.price-card--featured .btn-cta { background: var(--highlight); color: var(--primary); }
.price-card--featured .btn-cta:hover { background: var(--primary-foreground); }

/* Case à cocher en ligne (évacuation des déchets, TVA réduite) */
.check-inline {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  min-height: var(--tap-target-min);
}
.check-inline input {
  accent-color: var(--primary-mid);
  width: 1.1rem;
  height: 1.1rem;
  flex: none;
}

/* ---------- 7. Étapes & calendrier saisonnier ------------------------ */
.steps-grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1fr;
  counter-reset: step;
}
@media (min-width: 48rem) { .steps-grid { grid-template-columns: repeat(3, 1fr); } }
/* Les trois étapes sont des CARTES depuis le 01/08, et non plus trois blocs
   de texte posés côte à côte : sur la page du calculateur elles annoncent un
   parcours, et un parcours se lit mieux en jalons qu'en paragraphes.
   Le numéro est sorti du <h3> pour devenir un badge — voir le commentaire
   dans estimation.html. */
/* ⚠️ Le numero est DANS le <h3>, sur la meme ligne que le titre depuis le
   01/08 : au-dessus et centre, il imposait une carte haute pour trois mots.
   La carte s'aligne donc a gauche et se resserre. */
.step-item {
  padding: var(--space-5);
  text-align: left;
}
.step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* `flex: none` : sans lui le badge s'ecrase quand le titre passe a deux
     lignes, et le rond devient un ovale. */
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: var(--radius-pill);
  background: var(--primary);
  color: var(--primary-foreground);
  font-family: var(--font-display);
  font-size: var(--text-sm);
  line-height: 1;
}
.step-item h3 {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-md);
  line-height: 1.25;
  margin-bottom: var(--space-2);
}
.step-item p { color: var(--muted-foreground); font-size: var(--text-sm); margin: 0; }
@media (min-width: 40rem) { }
@media (min-width: 68rem) { }

/* ---------- 8. Réalisations / pourquoi nous / avis ------------------- */
.why-grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1fr;
}
@media (min-width: 48rem) { .why-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 62rem) { .why-grid { grid-template-columns: repeat(3, 1fr); } }
/* Quatre points sur UNE ligne (section international). Le palier a 48 rem
   les met deux par deux, celui-ci les aligne des que la place existe :
   quatre colonnes de moins de 15 rem donneraient des titres sur trois
   lignes. */
/* ⚠️ `.why-grid--4` EXISTAIT DEJA, hérité du gabarit jardin (voir plus bas,
   « Pages communes ») : il y pose quatre colonnes et l'icone au-dessus du
   texte. On s'y branche au lieu de le redeclarer, sinon deux regles
   gouvernent la meme grille et la seconde gagne en silence.
   Des 48 rem la grille passe a deux colonnes : l'alignement des titres
   compte donc des ce palier, pas seulement a quatre colonnes. */
@media (min-width: 48rem) {
  /* Les titres tombent sur une ou deux lignes selon leur longueur, si bien
     que les paragraphes ne demarraient pas a la meme hauteur d'une colonne
     a l'autre. On reserve donc DEUX lignes, la valeur la plus haute
     observee : les paragraphes s'alignent quelle que soit la largeur.
     `min-height` et non `height` : un titre plus long doit encore pouvoir
     passer a trois lignes sans etre coupe. La premiere declaration sert de
     repli pour un navigateur qui ignorerait l'unite `lh`. */
  .why-grid--4 .why-item h3 { min-height: calc(2 * 1.35em); }
  .why-grid--4 .why-item h3 { min-height: 2lh; }
}

/* Quatre colonnes : les 8 services des pages communes du jardin, et depuis
   le 28/07 la section international et les atouts des pages de zone. */
@media (min-width: 62rem) {
  .why-grid--4 { grid-template-columns: repeat(4, 1fr); gap: var(--space-5); }
  /* colonnes etroites : icone au-dessus du texte, plus lisible */
  .why-grid--4 .why-item { flex-direction: column; gap: var(--space-3); }
}
.why-item { display: flex; gap: var(--space-4); align-items: flex-start; }
.why-item .icon {
  width: 2rem; height: 2rem;
  color: var(--primary-mid);
  background: var(--muted);
  border-radius: var(--radius);
  padding: var(--space-2);
  box-sizing: content-box;
}
.why-item h3 { font-size: var(--text-md); margin-bottom: var(--space-1); }
.why-item p { font-size: var(--text-sm); color: var(--muted-foreground); margin: 0; }
@media (min-width: 48rem) { }
@media (min-width: 62rem) { }
@media (min-width: 48rem) { }

/* ---------- 9. Espace pro / Zone / FAQ ------------------------------- */
/* PHOTO DE FOND — un bureau en cours de déménagement, posée le 01/08.
   Générée par tools/build_images.py depuis assets/img/in/12.png.

   ⚠️ ELLE PASSE PAR UN PSEUDO-ÉLÉMENT, pas par un <div> comme le hero.
   Raison : cette section vit dans les TROIS accueils écrits à la main. Un
   div de fond aurait dû y être ajouté trois fois, et aurait divergé au
   premier changement. Ici le HTML ne bouge pas d'une ligne.

   ⚠️ LE CONTRASTE AA NE SUFFIT PAS À JUGER LA LISIBILITÉ, et cette section
   l'a prouvé. Posée d'abord à 0,26, elle passait le seuil partout (5,78 au
   pire) et restait pourtant pénible à lire : Quentin l'a signalé le 01/08.

   Ce que la mesure de luminance ne voit pas, c'est le BRUIT : des cartons,
   des arêtes et des lignes qui défilent derrière un paragraphe gênent l'œil
   même quand le contraste est bon. Le seuil AA est un minimum
   réglementaire, pas un critère de confort de lecture.

   L'écart entre le contraste moyen et le pire cas mesure ce désordre :

     opacité   moyen   pire 0,5 %   écart
       0,26     9,30      5,78       3,52   <- passait AA, illisible en vrai
       0,16    10,79      7,97       2,82
       0,12    11,30      9,11       2,19   <- retenu, image à peine visible
       0,08    11,96     10,30       1,66

   À 0,12 la photo ne donne plus qu'une texture au bleu nuit, ce qui était
   la demande. Changer de photo impose de refaire ce calcul, ET de regarder
   le résultat. */
.pro-band {
  position: relative;
  overflow: hidden;
  background: var(--primary);
  color: var(--primary-foreground);
}
.pro-band::before {
  content: "";
  position: absolute;
  inset: 0;
  background: center / cover no-repeat url("../assets/img/entreprises-800.webp");
  opacity: 0.12;
  pointer-events: none;
}
@media (min-width: 48rem) {
  .pro-band::before { background-image: url("../assets/img/entreprises-1672.webp"); }
}
/* Le contenu repasse au-dessus du fond. Sans cela, le pseudo-élément se
   pose PAR-DESSUS le texte : il vient après dans l'ordre de peinture. */
.pro-band > * { position: relative; z-index: 1; }
.pro-band .section-head .kicker { color: var(--highlight); }
.pro-band h2, .pro-band h3 { color: inherit; }
.pro-band .section-head p { color: color-mix(in oklab, var(--primary-foreground) 88%, transparent); }

.pro-targets {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-7);
  padding: 0;
  list-style: none;
}
.pro-targets li {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--overlay-soft);
  border: 1px solid color-mix(in oklab, var(--primary-foreground) 22%, transparent);
  border-radius: var(--radius-pill);
  padding: var(--space-2) var(--space-4);
  font-weight: 600;
  font-size: var(--text-sm);
}
.pro-targets .icon { color: var(--highlight); }

.pro-layout { display: grid; gap: var(--space-7); }
@media (min-width: 62rem) { .pro-layout { grid-template-columns: 1.15fr 0.85fr; align-items: start; } }

.pro-checks { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--space-4); }
.pro-checks li { display: flex; gap: var(--space-3); align-items: flex-start; }
.pro-checks .icon {
  color: var(--primary);
  background: var(--highlight);
  border-radius: var(--radius-pill);
  padding: var(--space-1);
  width: 1.05rem; height: 1.05rem;
  box-sizing: content-box;
  flex: none;
  margin-top: var(--space-0-5);
}
.pro-checks strong { display: block; font-size: var(--text-md); }
.pro-checks span { font-size: var(--text-sm); color: color-mix(in oklab, var(--primary-foreground) 85%, transparent); }

.pro-card {
  background: var(--card);
  color: var(--card-foreground);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-lg);
  /* ⚠️ DEGRAISSE le 29/07 : la carte depassait de 88 px le bas de la liste
     d'a cote, et Quentin l'a vu. Ce n'etait pas du contenu en trop, c'etait
     du vide : « Sur visite » trainait la marge par defaut d'un <p>, soit
     1 em a 49 px de corps, et la liste gardait 20 px sous elle alors que le
     padding de la carte en fournit deja. Quatre reglages, aucun mot retire.
     Toute retouche ici se REMESURE : la carte doit finir au niveau de
     `.pro-checks`, pas plus bas. */
  padding: var(--space-5);
  text-align: center;
}
.pro-card .pro-card-title { font-family: var(--font-display); font-weight: 700; font-size: var(--text-md); color: var(--primary); margin-bottom: var(--space-1); }
.pro-card .pro-price {
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: 700;
  color: var(--primary);
  line-height: 1;
  /* ⚠️ Sans cette ligne, le <p> reprend la marge par defaut du navigateur,
     soit 1 em : a ce corps-la, 49 px de vide sous deux mots. */
  margin: 0;
}
.pro-card .pro-price small { font-family: var(--font-sans); font-size: var(--step-0); font-weight: 600; color: var(--muted-foreground); }
.pro-card .pro-note { font-size: var(--step--1); color: var(--muted-foreground); margin-block: var(--space-1) var(--space-3); }
/* Pas de marge basse : le padding de la carte fournit deja cet espace, et
   l'ajouter faisait descendre la carte sous la liste voisine. */
.pro-card ul { list-style: none; padding: 0; margin: 0; font-size: var(--text-sm); display: grid; gap: var(--space-2); text-align: left; }
.pro-card ul li { display: flex; gap: var(--space-2); align-items: baseline; }
.pro-card ul .icon { width: 0.95em; height: 0.95em; color: var(--success); }

/* Zone d'intervention */
.zone-grid { display: grid; gap: var(--space-5); }
@media (min-width: 48rem) { .zone-grid { grid-template-columns: 1fr 1fr; } }
/* Rembourrage resserre le 01/08 : les trois cartes de zone restaient tres
   hautes meme une fois la liste repliee. */
.zone-card { padding: var(--space-4) var(--space-5); }
.zone-card h3 { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-lg); }
.zone-card h3 .icon { color: var(--primary-mid); }
.zone-card .zone-communes { color: var(--foreground); font-weight: 500; line-height: 1.95; margin: 0; }
/* Les trois zones n'ont pas le meme nombre de communes : 19, 27 et 65. La
   carte du Brabant flamand faisait donc le double des autres.
   Solution : la liste est repliee a HAUTEUR FIXE, la meme pour les trois,
   et un bouton la deroule. Les cartes redeviennent des rectangles de meme
   hauteur, et la liste complete reste a un clic.
   ⚠️ Le repli est pose par le JS (classe `is-clamped`) et le bouton est
   `hidden` dans le HTML : sans JavaScript, la liste s'affiche en entier
   plutot que d'etre coupee sans moyen de l'ouvrir. */
.zone-card .zone-communes { font-size: var(--step--1); }
/* La phrase qui couvre l'arrondissement de Louvain, sous la liste de la
   couronne : elle se distingue des noms de communes sans les alourdir. */
.zone-communes-suite { display: inline-block; margin-top: var(--space-2); color: var(--muted-foreground); }
/* Hauteur du repli ramenee de 9 a 6,5 rem le 01/08 : les cartes restaient
   massives. La liste complete est toujours a un clic, et les 111 noms de
   communes restent dans le HTML dans tous les cas. */
.zone-card.is-clamped .zone-communes {
  height: 6.5rem;
  overflow: hidden;
  /* Le degrade de fin de bloc dit « ca continue » sans ajouter d'element.
     Un simple `overflow: hidden` couperait net, au milieu d'un nom. */
  -webkit-mask-image: linear-gradient(to bottom, #000 65%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 65%, transparent 100%);
}
/* ⚠️ `is-clamped` RESTE posee quand la carte s'ouvre : c'est elle qui porte
   le bouton et l'etat initial. Sans annuler le masque ici, le degrade
   continuait donc de s'appliquer a la liste DEPLIEE, et les dernieres
   communes etaient illisibles. Signale par Quentin le 29/07 sur les 27
   communes du Brabant wallon. Le degrade n'a de sens que replie. */
.zone-card.is-open .zone-communes {
  height: auto;
  -webkit-mask-image: none;
  mask-image: none;
}
/* La carte est une colonne : le bouton et le lien sont pousses en bas, donc
   alignes d'une carte a l'autre meme si les intitules different. */
.zone-card { display: flex; flex-direction: column; }
.zone-plus-ligne { margin: var(--space-3) 0 0; }
/* ⚠️ Le bouton s'est reduit a un CHEVRON le 01/08 : « Afficher les 65
   communes » ajoutait une ligne de texte a une carte qu'on cherchait
   justement a raccourcir. Le libelle n'est pas perdu, il est passe en
   `aria-label` et bascule au clic (voir main.js).
   Plus de soulignement ni de gras : il n'y a plus de texte a souligner. */
.zone-plus {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  /* Le bouton ne coute que 1.5rem de hauteur de carte — c'etait tout
     l'interet de l'operation. La zone tactile de 44 px (le plancher
     Lighthouse) est rendue par le pseudo-element ci-dessous, qui deborde
     sans peser sur la mise en page. */
  width: 2.75rem;
  height: 1.5rem;
  color: var(--primary-mid);
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease);
}
/* ⚠️ Le debordement va vers le HAUT uniquement. Vers le bas se trouve
   « Demenager a Bruxelles → » : une zone tactile qui deborderait par en
   dessous lui volerait ses clics. Au-dessus il n'y a que la liste des
   communes, qui ne clique pas. */
.zone-plus::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  top: -1.25rem;                       /* 1.5rem + 1.25rem = 44 px */
}
.zone-plus:hover { color: var(--primary); }
.zone-plus .icon {
  width: 1.25rem;
  height: 1.25rem;
  transition: transform var(--duration-fast) var(--ease);
}
/* Ouvert = le chevron se retourne. C'est le SEUL retour visuel qui reste
   maintenant que le libelle a quitte le bouton : il doit etre net. */
.zone-card.is-open .zone-plus .icon { transform: rotate(180deg); }
/* --- Pages de zone (zones/*.html, generees) --------------------------
   Elles reutilisent tout ce qui existe deja : .why-grid pour les atouts,
   .faq-list pour les questions, .cta-band pour l'appel final. Seuls le
   bandeau de prix et la liste d'acces sont propres a ces pages. */
.zone-intro { max-width: 62rem; }
.zone-prix { margin-top: var(--space-6); padding: clamp(var(--space-5), 4vw, var(--space-8)); }
.zone-prix h2 {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-5);
  font-size: var(--text-lg);
  color: var(--primary);
}
.zone-prix h2 .icon { color: var(--primary-mid); flex: none; }
.zone-prix-liste { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-5); }
@media (min-width: 48rem) { .zone-prix-liste { grid-template-columns: 1fr 1fr; } }
/* Le filet ambre a gauche remplace la puce : dans une grille a deux
   colonnes, une puce de liste se perd, un filet tient la colonne. */
.zone-prix-liste li {
  display: grid;
  gap: var(--space-1);
  padding-left: var(--space-4);
  border-left: var(--space-1) solid var(--highlight);
}
.zone-prix-etiquette {
  font-size: var(--text-2xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.zone-prix-liste strong {
  font-family: var(--font-display);
  font-size: var(--step-2);
  line-height: 1.1;
  color: var(--primary);
}
.zone-prix-suite { font-size: var(--step--1); color: var(--muted-foreground); }
.zone-prix-note { margin: var(--space-5) 0 0; font-size: var(--step--1); }
.zone-prix-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-5); }
/* Le titre de la section est centre, la liste doit donc l'etre aussi comme
   BLOC, sans centrer le texte lui-meme : une liste a puces au texte centre
   ne s'aligne sur rien. */
.zone-acces {
  display: grid;
  gap: var(--space-3);
  margin-inline: auto;
  padding-left: var(--space-5);
  max-width: 62rem;
}

.zone-lien { margin: var(--space-4) 0 0; font-weight: 700; }
/* `auto` et non une marge : le lien se colle au bas de la carte, quelle que
   soit la hauteur de la ligne. C'est ce qui aligne les trois cartes. */
.zone-card .zone-lien { margin-top: auto; padding-top: var(--space-4); }
.zone-lien a { color: var(--primary-mid); text-decoration: none; }
.zone-lien a:hover { text-decoration: underline; }
.zone-note { font-size: var(--step--1); color: var(--muted-foreground); margin-top: var(--space-5); }

/* FAQ accordéon */
.faq-list { display: grid; gap: var(--space-3); max-width: 52rem; margin-inline: auto; }
.faq-list details {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.faq-list summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--primary);
  min-height: var(--tap-target-comfort);
}
.faq-list summary::-webkit-details-marker { display: none; }
.faq-list summary .icon { color: var(--primary-mid); transition: transform var(--duration-base) var(--ease); }
.faq-list details[open] summary .icon { transform: rotate(45deg); }
.faq-list details .faq-answer {
  padding: 0 var(--space-5) var(--space-5);
  color: var(--muted-foreground);
  font-size: var(--text-sm);
}
.faq-list details .faq-answer p:last-child { margin-bottom: 0; }

/* ---------- 10. CTA final / Contact / Footer -------------------------- */

/* Invitation au calculateur, au bas de la grille tarifaire. Meme fond bleu
   nuit que le bloc de cloture : ce sont les deux seuls encarts pleins de la
   page, et ils marquent tous deux un passage a l'action. */
.invite-calc {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  margin-top: var(--space-7);
  padding: clamp(var(--space-6), 4vw, var(--space-9));
  background: var(--primary);
  color: var(--primary-foreground);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-md);
}
/* Titre en ambre : dans un encart bleu nuit, c'est la seule chose qui
   doit accrocher l'oeil avant le bouton.
   ⚠️ MONTE A L'ECHELLE DE SECTION le 29/07 (« on ne comprend pas
   l'encadre, il faudrait mettre en valeur », Quentin). Il etait a
   --text-lg, la taille d'un titre de CARTE : sur une bande pleine largeur
   il se lisait comme un sous-titre, pas comme une invitation. Il passe a
   --step-2, l'echelle des titres de section.
   ⚠️ SANS ICONE, et c'est un choix : l'euro essaye le 29/07 mettait
   l'argent en tete d'une invitation qui vend d'abord du temps gagne. Le
   titre est assez gros pour porter seul.
   ⚠️ LE TEXTE N'A PAS CHANGE, et c'est une decision de Quentin. Deux
   reecritures ont ete essayees le 29/07, « Combien pour votre
   demenagement ? » puis « Ces tarifs appliques a votre demenagement » ;
   il a tranche pour le libelle d'origine. Ce qui manquait a ce bloc
   n'etait donc pas sa formulation mais son poids : c'est la taille qui
   a double, pas les mots. Ne pas le reecrire sans le lui demander. */
.invite-calc h3 {
  color: var(--highlight);
  margin: 0 0 var(--space-2);
  font-size: var(--step-2);
  line-height: 1.15;
}
.invite-calc-txt { flex: 1 1 22rem; }
/* `40rem` : le texte garde une longueur de ligne lisible et laisse le bouton
   sur la meme ligne tant que la place existe. En dessous, `flex-wrap` fait
   passer le bouton dessous, sans point de rupture a maintenir. */
.invite-calc p { margin: 0; max-width: 40rem; color: color-mix(in oklab, var(--primary-foreground) 88%, transparent); font-size: var(--text-sm); }
.invite-calc .btn { flex: none; }

.cta-band { background: var(--background); }
/* Le message final est un bloc encadre, pas une bande pleine largeur */
.cta-band .container {
  /* Bleu nuit et non bleu moyen : c'est le dernier bloc de la page, il doit
     peser plus que les bandeaux qui le precedent. */
  background: var(--primary);
  color: var(--primary-foreground);
  text-align: center;
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-md);
  /* ⚠️ LARGEUR : le bloc doit tomber exactement sur la colonne de contenu du
     reste de la page, ni plus etroit ni plus large.
     Il avait `max-width: 62rem`, soit 992 px la ou tout le reste tient sur
     1136 px : il se lisait comme un element etranger. Mais retirer
     simplement le plafond ne suffit pas : ce bloc EST le `.container`, donc
     sa boite inclut les gouttieres, et il depassait alors de 64 px.
     D'ou le calcul : largeur du conteneur MOINS ses deux gouttieres. */
  width: calc(100% - 2 * var(--gutter));
  max-width: calc(var(--container) - 2 * var(--gutter));
  padding: clamp(var(--space-9), 5vw, var(--space-15)) clamp(var(--space-6), 4vw, var(--space-12));
}
.cta-band h2 { color: inherit; }
.cta-band p { color: color-mix(in oklab, var(--primary-foreground) 90%, transparent); max-width: 40rem; margin-inline: auto; }
.cta-band .cta-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-6);
}
/* Les trois moyens de contact vivent dans le bloc de cloture depuis la
   fusion du 28/07. Cartes blanches sur bleu nuit : elles ressortent, et
   leur texte reste aligne a gauche malgre le centrage du bandeau, sinon
   l'icone et le libelle ne tiennent plus sur la meme ligne de lecture. */
.cta-contacts { margin-top: var(--space-6); text-align: left; }

.cta-band .btn-cta { background: var(--highlight); color: var(--primary); }
.cta-band .btn-cta:hover { background: var(--primary-foreground); }
@media (min-width: 62rem) { }
@media (min-width: 48rem) { }

.contact-aside { display: grid; gap: var(--space-4); align-content: start; }
/* Depuis le retrait du formulaire du bas (28/07), les quatre cartes de
   contact occupent toute la largeur au lieu d'une colonne latérale. Elles
   se répartissent d'elles-memes, sans point de rupture a maintenir : deux
   par ligne sur une tablette, quatre sur un grand ecran. */
.contact-aside--large { grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
/* ⚠️ FOND BLEU CLAIR, et rembourrage resserre depuis le 01/08 : en blanc et
   en 5/6, les trois cartes pesaient plus lourd que le bouton de devis
   qu'elles entourent. Le bleu est DERIVE de `--primary-mid`, il n'est pas
   ecrit en dur : si la palette change, il suit. */
.contact-card {
  padding: var(--space-3) var(--space-4);
  display: flex;
  gap: var(--space-3);
  align-items: center;
  text-decoration: none;
  background: color-mix(in oklab, var(--primary-mid) 14%, var(--card));
  color: var(--card-foreground);
  transition: box-shadow var(--duration-base) var(--ease), transform var(--duration-base) var(--ease);
}
a.contact-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.contact-card .icon { width: 1.4rem; height: 1.4rem; color: var(--primary-mid); }
.contact-card.contact-card--wa .icon { color: var(--whatsapp); }
/* `overflow-wrap: anywhere` : une adresse e-mail n'a aucune coupure
   naturelle, et depuis que les quatre cartes se partagent la largeur,
   « demenagement@onlyway.be » debordait de la sienne. Le mot se coupe donc
   la ou il le faut plutot que de sortir du cadre. */
.contact-card strong {
  display: block;
  font-size: var(--text-md);
  color: var(--primary);
  overflow-wrap: anywhere;
}
.contact-card span { font-size: var(--text-sm); color: var(--muted-foreground); }

.site-footer {
  background: var(--primary);
  color: var(--primary-foreground);
  padding-block: clamp(var(--space-10), 6vw, var(--space-16)) var(--space-6);
  /* ⚠️ La réserve du bas ne dégage plus une barre pleine largeur — celle-ci
     a disparu le 04/08 — mais la PILE FLOTTANTE, qui n'occupe que le coin.
     Elle reste nécessaire : sans elle, la dernière ligne du pied passe sous
     la pastille WhatsApp. Trois pastilles de 52 px et deux écarts font
     176 px ; on ne dégage que la plus basse, les autres flottent sur du
     fond uni. */
  padding-bottom: calc(var(--space-6) + 64px);
}
@media (min-width: 48rem) { .site-footer { padding-bottom: var(--space-6); } }
/* ⚠️ `:not(.btn)` est indispensable, et son absence a coûté un bouton
   illisible en production le 01/08. `.site-footer a` a une spécificité de
   (0,1,1) : elle bat donc `.btn-light` et `.btn-highlight`, qui ne valent
   que (0,1,0). Les deux boutons du pied héritaient alors du blanc du pied,
   ce qui donnait du blanc sur blanc — contraste mesuré à 1,0 — et du blanc
   sur ambre à 1,71.
   Conformément à la charte, on SORT l'exception de la règle générale au
   lieu de surenchérir en spécificité. */
.site-footer a:not(.btn) { color: inherit; }
.footer-grid { display: grid; gap: var(--space-8); margin-bottom: var(--space-10); }
@media (min-width: 48rem) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 62rem) { .footer-grid { grid-template-columns: 1.4fr 1fr 1fr 1fr; } }
.footer-brand svg { width: 3rem; height: 3rem; margin-bottom: var(--space-3); color: var(--highlight); }
.footer-brand p { font-size: var(--text-sm); color: color-mix(in oklab, var(--primary-foreground) 80%, transparent); max-width: 22rem; }
.site-footer h3 {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--highlight);
  margin-bottom: var(--space-4);
}
.site-footer ul { list-style: none; padding: 0; margin: 0; font-size: var(--text-sm); }
.site-footer ul li { padding-block: var(--space-1); }
.footer-legal {
  border-top: 1px solid color-mix(in oklab, var(--primary-foreground) 20%, transparent);
  padding-top: var(--space-5);
  font-size: var(--step--1);
  color: color-mix(in oklab, var(--primary-foreground) 75%, transparent);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-6);
  justify-content: space-between;
}

/* ---------- 11. Boutons flottants / divers ---------------------------- */
/* Pile fixe en bas à droite, sur le modèle de tag-brussels.be. Elle a
   REMPLACÉ la barre du bas le 04/08, à la demande de Quentin.

   ⚠️ Chaque bouton apparaît là où l'en-tête cesse de porter son action, et
   les seuils ne sont donc PAS les mêmes. Les aligner serait plus simple à
   lire et faux à l'usage :
     · l'en-tête est STICKY et porte le numéro dès 30rem, donc un bouton
       d'appel flottant au-dessus de 48rem ferait doublon avec lui ;
     · le CTA, lui, passe derrière le burger sous 74rem — c'est là qu'il
       manque, y compris sur tablette, où la barre du bas ne venait jamais.
   La pile couvre donc PLUS que la barre qu'elle remplace. */
.float-actions {
  position: fixed;
  right: var(--space-5);
  bottom: calc(var(--space-5) + env(safe-area-inset-bottom));
  z-index: var(--z-mobilebar);
  display: flex;
  /* Ordre visuel de haut en bas : Devis, Appeler, WhatsApp. `column` suit
     donc l'ordre du DOM. WhatsApp reste le plus bas, donc le plus proche du
     pouce, comme sur tag-brussels et garden. */
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-3);
}
/* Le menu mobile ouvert prend tout l'écran : la pile flotterait par-dessus. */
body.nav-open .float-actions { display: none; }

.float-btn {
  display: none;
  width: 52px;
  height: 52px;
  border-radius: var(--radius-pill);
  align-items: center;
  justify-content: center;
  color: var(--primary-foreground);
  text-decoration: none;
  box-shadow: var(--shadow-lg);
  transition: transform var(--duration-fast) var(--ease), box-shadow var(--duration-base) var(--ease);
}
.float-btn .icon { width: 1.5rem; height: 1.5rem; flex: none; }
.float-btn:hover { transform: scale(1.06); color: var(--primary-foreground); }
/* ⚠️ L'ambre porte du texte SOMBRE, jamais clair : sur `--highlight` le
   blanc mesure 1,9 de contraste. C'est la règle du skill de charte, et
   c'est le piège le plus fréquent de cette palette.
   ⚠️ L'ambre marque UNE seule action par écran. Elle est passée du devis au
   CALCULATEUR le 12/08, à la demande de Quentin : deux pastilles ambre
   auraient annulé la hiérarchie que l'ambre sert précisément à créer. */
.float-calc { background: var(--highlight); color: var(--primary); }
/* Le survol reprend celui de `.btn-highlight` : ambre -> bleu nuit. */
.float-calc:hover { background: var(--primary); color: var(--primary-foreground); }
/* Le devis descend d'un cran : bleu nuit, puis le bleu moyen pour l'appel.
   Les trois pastilles se lisent donc de haut en bas comme une hiérarchie. */
.float-devis { background: var(--primary); }
.float-call { background: var(--primary-mid); }
/* ⚠️ Les seuils restent ceux de chaque action, pas un seuil commun. Le
   calculateur suit le devis : l'en-tête ne porte aucun bouton de calcul,
   donc aucun doublon à craindre, et c'est sous 74rem que la page se resserre. */
@media (max-width: 73.99rem) { .float-calc, .float-devis { display: inline-flex; } }
@media (max-width: 47.99rem) { .float-call  { display: inline-flex; } }


/* Apparition au defilement. Le contenu est VISIBLE par defaut : il n'est
   masque que si le script a pris la main (classe js-reveal posee sur <html>).
   Ainsi une erreur JS ne peut jamais faire disparaitre du contenu. */
@media (prefers-reduced-motion: no-preference) {
  html.js-reveal .reveal {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity var(--duration-slow) var(--ease), transform var(--duration-slow) var(--ease);
  }
  html.js-reveal .reveal.is-visible { opacity: 1; transform: none; }

  /* ⚠️ La règle ci-dessus remet `transform` à zéro avec une spécificité
     plus forte que les règles de survol écrites plus haut dans ce fichier,
     et elle vient après : le soulèvement au survol des cartes services
     était donc purement et simplement annulé. On le rétablit ici, à la
     même spécificité mais plus bas. Toute nouvelle carte `.reveal` avec un
     effet de survol doit être ajoutée à cette liste. */
  html.js-reveal .reveal.card-service:hover,
  html.js-reveal .reveal.card-service:focus-visible,
  html.js-reveal a.reveal.zone-card:hover,
  html.js-reveal a.reveal.zone-card:focus-visible { transform: translateY(-4px); }
  html.js-reveal a.reveal.contact-card:hover { transform: translateY(-2px); }

  /* Même remarque pour la DURÉE : `.reveal` impose 0,5 s à `transform` et
     ne transitionne ni l'ombre ni la bordure. Une fois la carte apparue,
     on repasse au rythme des cartes « Autour de » des pages communes. */
  html.js-reveal .reveal.is-visible.card-service,
  html.js-reveal a.reveal.is-visible.zone-card,
  html.js-reveal a.reveal.is-visible.contact-card {
    transition: opacity var(--duration-slow) var(--ease), transform var(--duration-base) var(--ease),
                box-shadow var(--duration-base) var(--ease), border-color var(--duration-base) var(--ease);
  }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

@media print {
  .site-header, .float-actions, .mobile-menu, .estimator { display: none !important; }
}

/* ---------- Fil d'Ariane (pages communes) --------------------------- */
.breadcrumb {
  border-bottom: 1px solid var(--border);
  background: var(--card);
  font-size: var(--step--1);
}
.breadcrumb .container {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding-block: var(--space-3);
  color: var(--muted-foreground);
}
.breadcrumb a { color: var(--primary-mid); text-decoration: none; font-weight: 600; }
.breadcrumb a:hover { text-decoration: underline; }

/* ---------- Selecteur de langue --------------------------------------
   Meme composant que tag-brussels.be, memes noms de classes
   (.lang-switch / .ls-btn / .ls-menu) : un developpeur qui passe d'un site
   onlyway a l'autre retrouve le meme selecteur au meme endroit. Seules les
   valeurs viennent d'ici, par les tokens.

   Le menu s'affiche et se masque par l'attribut `hidden`, pilote en JS.
   Pas de survol : sur ecran tactile il n'existe pas, et un menu de langue
   doit s'ouvrir au doigt. C'est aussi ce que fait tag-brussels.

   Le bouton ne montre QUE la langue courante, la liste ne montre QUE les
   autres. Deux caracteres par ligne, aucune traduction du nom de langue :
   c'est plus court a lire et ca ne demande aucune traduction. */
.lang-switch { position: relative; font-size: var(--text-sm); font-weight: 700; }
.ls-btn {
  font: inherit;
  font-weight: 700;
  color: var(--primary);
  background: var(--muted);
  border: 1.5px solid var(--border);
  /* Rond, comme sur garden.onlyway.be, et comme le bouton burger juste a
     cote : ces deux commandes de l'entete doivent avoir la meme forme. */
  border-radius: var(--radius-pill);
  cursor: pointer;
  /* Largeur imposee egale a la hauteur : sans elle le bouton fait 43 x 44
     et le cercle est legerement ovale. */
  width: var(--tap-target-min);
  min-height: var(--tap-target-min);
  justify-content: center;
  padding: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}
.ls-btn:hover { border-color: var(--primary-mid); color: var(--primary-mid); }
.ls-menu {
  position: absolute;
  /* ancre a DROITE : le bouton est au bord droit de l'entete, un panneau
     centre deborderait de l'ecran sur telephone. */
  right: 0;
  top: calc(100% + var(--space-2));
  z-index: var(--z-overlay);
  list-style: none;
  margin: 0;
  padding: var(--space-1);
  background: var(--card);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  min-width: 4.5rem;
}
.ls-menu[hidden] { display: none; }
.ls-menu a {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-target-min);
  padding: var(--space-2) var(--space-4);
  color: var(--primary);
  text-decoration: none;
  border-radius: var(--radius);
}
.ls-menu a:hover { background: var(--muted); color: var(--primary-mid); }

@media (min-width: 62rem) { .zone-grid--3 { grid-template-columns: repeat(3, 1fr); } }

/* Cartes cliquables (communes voisines) : pas de soulignement, leger
   soulevement au survol plutot qu'un effet de lien souligne. */
a.zone-card {
  text-decoration: none;
  color: inherit;
  transition: transform var(--duration-fast) var(--ease), box-shadow var(--duration-fast) var(--ease), border-color var(--duration-fast) var(--ease);
}
a.zone-card:hover,
a.zone-card:focus-visible {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
  border-color: var(--primary-mid);
}
a.zone-card h3, a.zone-card p,
a.zone-card:hover h3, a.zone-card:hover p { text-decoration: none; }

/* Footer : logo et nom cote a cote, liens non soulignes */
.footer-logo {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: inherit;
  text-decoration: none;
  margin-bottom: var(--space-4);
}
.footer-logo svg { width: 2.6rem; height: 2.6rem; flex: none; }
.footer-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-lg);
  line-height: 1.1;
}
.footer-name small {
  display: block;
  font-family: var(--font-sans);
  font-size: var(--text-3xs);
  font-weight: 600;
  letter-spacing: 0.28em;
  opacity: 0.7;
}
.site-footer a { text-decoration: none; }
.site-footer a:hover { text-decoration: none; opacity: 0.75; }
.footer-brand .btn { margin-top: var(--space-4); }

/* ⚠️ RETIRÉ le 01/08 : plus aucun `btn-cta` ne vit dans un pied de page.
   Cette règle mettait le CTA du pied en ambre, et c'est elle qui rendait vrai
   le §8 du HANDOFF quand il annonçait un bouton ambre — la classe HTML disait
   `btn-cta`, le rendu était ambre. Le pied porte désormais `btn-light` pour
   le devis et `btn-highlight` pour le calculateur, qui portent leur couleur
   eux-mêmes. Vérifié par script : aucun pied du site n'a plus de `btn-cta`.
   `.cta-band .btn-cta` reste en place, elle, et sert toujours au bouton
   téléphone de la page du calculateur. */

/* =====================================================================
   12. Calculateur d'estimation
   ---------------------------------------------------------------------
   Cinq cadres. Sur grand ecran : le client sur toute la largeur, puis
   depart et arrivee cote a cote, puis services et commentaire cote a
   cote. Sur telephone, tout s'empile dans l'ordre de lecture.
   ===================================================================== */
.calc { display: grid; gap: var(--space-6); }
/* Les trois etapes precedent desormais le formulaire dans la meme section :
   sans cette respiration, le premier cadre du calculateur collait a la
   derniere etape, et les deux blocs se lisaient comme un seul. */
.steps-grid + .calc { margin-top: var(--space-8); }

.calc-service { max-width: 32rem; margin-inline: auto; width: 100%; }
.calc-service label { text-align: center; display: block; }

.calc-grid { display: grid; gap: var(--space-5); }
@media (min-width: 62rem) {
  /* Pas d'`align-items: start` : les cartes d'une meme ligne s'etirent a la
     hauteur de la plus haute. « Votre commentaire » se cale ainsi sur
     « Services additionnels », et depart sur arrivee. */
  .calc-grid { grid-template-columns: 1fr 1fr; }
  /* Le cadre des coordonnees et le bouton d'ajout tiennent la largeur */
  .calc-bloc--full, .calc-ajout { grid-column: 1 / -1; }
}

/* Un fieldset porte des marges et une bordure par defaut : on les retire,
   l'apparence vient entierement de .card. */
.calc-bloc {
  margin: 0;
  padding: var(--space-5) var(--space-5) var(--space-6);
  border: 1px solid var(--border);
}
/* Les cadres sont des <div> et non des <fieldset> : une <legend> se place
   a cheval sur la bordure, et ni le flex ni la grille ne s'appliquent
   proprement aux enfants d'un fieldset. Or il faut que « Votre commentaire »
   s'etire a la hauteur de « Services additionnels ».
     ⚠️ Le calculateur ne contient plus AUCUN fieldset depuis le 01/08 :
     le seul qui restait portait les boutons radio de civilite, champ
     retire a la demande de Quentin. */
.calc-bloc-titre {
  margin: 0 0 var(--space-4);
  font-size: var(--text-lg);
  color: var(--primary);
}

/* La carte etire son contenu, le contenu etire son dernier champ : c'est
   la chaine qui permet a la zone de commentaire d'occuper toute la hauteur
   disponible au lieu de laisser un blanc sous elle. */
.calc-bloc { display: flex; flex-direction: column; }
.calc-bloc > .calc-champs { flex: 1; }
.calc-champs { display: grid; gap: var(--space-4); align-content: start; }
.calc-champs > .field:only-child,
.calc-champs > .field:only-child textarea { height: 100%; }
@media (min-width: 34rem) {
  .calc-champs { grid-template-columns: 1fr 1fr; }
  .calc-champs .full { grid-column: 1 / -1; }
}
/* Rue et numero, code postal et ville : des largeurs qui suivent le
   contenu reel plutot que deux colonnes egales. */
@media (min-width: 34rem) {
  /* Deux colonnes egales : la rue et le code postal n'ont pas besoin de
     plus de place que le numero et la ville, et l'asymetrie se voyait. */
  .calc-champs--adresse { grid-template-columns: 1fr 1fr; }
  .calc-champs--adresse .calc-cp { grid-column: 1; }
  .calc-champs--adresse .calc-ville { grid-column: 2; }
}

/* Civilite et nom partagent une cellule : ce sont deux moities d'une meme
   information, et les separer creait une ligne bancale. */
.calc-identite { display: grid; gap: var(--space-3); align-content: start; }

/* Deux champs partagent leur ligne avec un voisin plus haut : « Ce dont vous
   avez besoin » face a la civilite et au nom, la case du stationnement face
   au compteur de cartons. Un champ est une grille : etire sur la hauteur de
   la ligne, il etirait aussi sa liste deroulante. Cale en bas, il se pose
   exactement sur le dernier champ d'en face. */
.calc-besoin, .calc-stationnement { align-self: end; }

/* Le texte d'explication du stationnement tient sa propre ligne, en pleine
   largeur : sous la case il etirait sa colonne, a cote il l'aurait retrecie.
   L'ecart de la grille suffit, la marge propre du .hint ferait double. */
.calc-champs > .hint { margin: 0; }

.calc-ajout { margin: 0; }

/* Le bouton de retrait appartient au cadre, pas au formulaire : il se place
   en haut a droite, a hauteur de la legende, la ou l'oeil cherche la croix
   de fermeture. Le cadre devient donc le referentiel de position. */
.calc-bloc { position: relative; }
.calc-retirer-ligne {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  margin: 0;
  z-index: 1;
}
/* Fond bleu nuit : le bouton doit se distinguer nettement du cadre blanc,
   sans pour autant crier comme un bouton d'appel a l'action. */
.calc-retirer {
  background: var(--primary);
  color: var(--primary-foreground);
  border-color: var(--primary);
}
.calc-retirer:hover {
  background: var(--primary-mid);
  border-color: var(--primary-mid);
  color: var(--primary-foreground);
}

/* --- Champ numerique a fleches ---------------------------------------
   Le champ reste un <input type="number"> : il se remplit au clavier,
   les fleches ne sont qu'un raccourci. Les fleches natives sont masquees,
   sinon l'utilisateur en verrait deux jeux. */
/* Le champ du spin porte data-spin et est EXCLU de la regle generale
   des champs, plus haut. Sans cette exclusion il gardait sa bordure et son
   arrondi : la regle generale enchaine neuf :not(), sa specificite est
   telle qu'aucun .spin input ne peut la battre sans surenchere absurde.
   Retirer une exception vaut mieux que gagner une course a la specificite.

/* UN SEUL cadre pour l'ensemble, et non trois boites accolees.
   La version precedente laissait a chaque element sa propre bordure et son
   propre arrondi : les deux boutons apparaissaient comme des pastilles
   posees a l'interieur du champ, avec des angles qui se coupaient. Ici le
   contour appartient au groupe, les enfants n'ont plus ni bordure ni
   arrondi, et `overflow: hidden` fait suivre les coins aux boutons. */
.spin {
  display: flex;
  align-items: stretch;
  min-height: 46px;
  border: 1.5px solid var(--input);
  border-radius: var(--radius);
  background: var(--card);
  overflow: hidden;
}
/* Le cadre du groupe prend le relais de celui du champ au focus, sinon on
   verrait deux anneaux concentriques. */
.spin:focus-within {
  border-color: var(--ring);
  box-shadow: var(--shadow-focus);
}
.spin input {
  width: 100%;
  min-width: 3rem;
  min-height: 0;
  text-align: center;
  border: 0;
  border-radius: 0;
  background: transparent;
}
.spin input:focus { outline: none; box-shadow: none; }
/* Les fleches natives feraient doublon avec nos deux boutons. */
.spin input::-webkit-outer-spin-button,
.spin input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.spin input[type="number"] { -moz-appearance: textfield; }
.spin button {
  flex: none;
  width: 46px;
  border: 0;
  background: var(--muted);
  color: var(--primary);
  font: inherit;
  font-size: var(--text-xl);
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--duration-fast) var(--ease);
}
/* Un simple filet separe les boutons du champ : pas de bordure complete,
   qui redessinerait des angles a l'interieur du groupe. */
.spin button:first-child { border-right: 1.5px solid var(--input); }
.spin button:last-child  { border-left: 1.5px solid var(--input); }
.spin button:hover { background: var(--border); }
.spin button:active { background: var(--input); }

.hint { font-size: var(--text-2xs); color: var(--muted-foreground); margin: var(--space-1) 0 0; }

/* --- L'estimation, meme cadre que la section du site jardin ----------
   Fond bleu nuit, total en ambre, aucun detail poste par poste : le
   client veut un ordre de grandeur, le detail appartient au devis. */
.estimator-result {
  padding: clamp(var(--space-5), 4vw, var(--space-7));
  background: var(--primary);
  color: var(--primary-foreground);
  border: 0;
  display: grid;
  gap: var(--space-2);
}
.estimator-result h3 { color: inherit; margin-bottom: 0; }
.estimator-result .price-range {
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: 700;
  line-height: 1.05;
  margin: 0;
  color: var(--highlight);
}
.estimator-result .price-vat {
  margin: calc(var(--space-1) * -1) 0 0;
  font-weight: 600;
  font-size: var(--text-md);
}
.estimator-result .price-meta {
  font-size: var(--step--1);
  display: grid;
  gap: var(--space-0-5);
}
.estimator-result .disclaimer {
  font-size: var(--step--1);
  border-top: 1px solid color-mix(in oklab, var(--primary-foreground) 28%, transparent);
  padding-top: var(--space-3);
  margin: var(--space-2) 0 0;
}
.estimator-result .calc-alerte {
  margin: var(--space-1) 0 0;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius);
  background: color-mix(in oklab, var(--highlight) 22%, transparent);
  color: var(--primary-foreground);
  font-weight: 700;
  font-size: var(--text-sm);
}

/* --- L'avertissement d'entree ----------------------------------------
   Un encadre clair borde d'ambre a gauche : il doit se lire comme une
   precision de la maison, pas comme une alerte d'erreur, donc ni fond
   colore ni icone rouge. Largeur bornee : une ligne de 1100 px ne se lit
   pas, et ce bloc est le seul texte suivi de la page. */
/* ⚠️ RESSERRE le 28/07 : le bloc faisait 310 px pour quatre items d'une
   ligne chacun, soit plus haut que ce qu'il dit. Aucun mot n'a ete retire,
   c'est la DENSITE qui change : liste sur deux colonnes des qu'il y a la
   place, padding et interlignes reduits d'un cran, titre a --text-md. */
.calc-avis {
  max-width: 62rem;
  /* Marge HAUTE depuis que le bloc est passe sous le cadre d'estimation :
     colle a lui, il se lisait comme une suite du cadre bleu nuit. */
  margin: var(--space-6) auto var(--space-5);
  padding: clamp(var(--space-4), 3vw, var(--space-5)) clamp(var(--space-5), 3vw, var(--space-6));
  border-left: var(--space-1) solid var(--highlight);
}
.calc-avis h3 {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-2);
  font-size: var(--text-md);
  color: var(--primary);
}
.calc-avis h3 .icon { color: var(--highlight); flex: none; }
.calc-avis p { margin: 0 0 var(--space-2); font-size: var(--step--1); }
.calc-avis-liste {
  display: grid;
  gap: var(--space-1) var(--space-6);
  margin: 0 0 var(--space-2);
  padding-left: var(--space-5);
  font-size: var(--step--1);
}
/* Deux colonnes des que la largeur le permet : quatre items courts empiles
   font quatre lignes la ou deux suffisent. Sous ce seuil ils se remettent
   en pile tout seuls. */
@media (min-width: 40rem) {
  .calc-avis-liste { grid-template-columns: 1fr 1fr; }
}
.calc-avis-fin { margin: 0; }

/* Les trois actions vivent dans le cadre bleu nuit de l'estimation. Elles se
   rangent sur une ligne et s'empilent d'elles-memes quand la place manque,
   sans point de rupture a maintenir. Le message de retour prend une ligne
   entiere : coince entre deux boutons, il decalait la rangee a chaque envoi. */
.calc-envoi {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-3);
  padding-top: var(--space-4);
  border-top: 1px solid color-mix(in oklab, var(--primary-foreground) 28%, transparent);
}
.calc-envoi .form-feedback { flex-basis: 100%; margin: 0; text-align: center; }
/* Le bouton d'envoi doit rester le plus lourd des trois : ambre plein, quand
   le telephone est en contour et WhatsApp dans sa couleur de marque. */
.estimator-result .calc-envoi .btn-cta { background: var(--highlight); color: var(--primary); }
.estimator-result .calc-envoi .btn-cta:hover { background: var(--primary-foreground); }
/* Le trait du disclaimer ferait doublon avec celui de la rangee d'actions
   juste au-dessus. */
.estimator-result .calc-envoi + .disclaimer { border-top: 0; padding-top: 0; }

/* --- Listes deroulantes : la fleche dans son propre bloc clair ----------
   La fleche native est minuscule et se confond avec le texte. On la remplace
   par un chevron pose sur un pave clair borde a gauche, exactement le meme
   traitement que les boutons + et - des champs numeriques. Trois couches de
   fond : le chevron, le filet de separation, le pave.

   ⚠️ La couleur du chevron est ecrite en dur (#142d53 = --primary) : une
   image de fond en data:URI ne peut pas lire une variable CSS. C'est la
   seule exception du site, et elle doit suivre si --primary change. */
select {
  appearance: none;
  -webkit-appearance: none;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23142d53' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"),
    linear-gradient(var(--input), var(--input)),
    linear-gradient(var(--muted), var(--muted));
  background-position: right 15px center, right 46px center, right center;
  background-size: 14px 14px, 1.5px 100%, 46px 100%;
  background-repeat: no-repeat;
  padding-right: 60px;
}
select:disabled { opacity: 0.6; }

/* =====================================================================
   AFFICHAGE TÉLÉPHONE UNIQUEMENT — sous 768 px
   =====================================================================
   ⚠️ TOUT ce qui concerne l'affichage téléphone vit dans CE BLOC, et
   nulle part ailleurs. C'est ce qui rend la promesse « l'affichage
   bureau ne bouge pas » vérifiable plutôt que promise : aucune règle
   d'ici ne peut atteindre un écran de 768 px ou plus.

   ⚠️ 47.99rem et NON 48rem : à exactement 768 px, `min-width: 48rem` et
   `max-width: 48rem` s'appliqueraient toutes les deux.

   Piloté par js/mobile.js. Sans JavaScript, rien de tout ceci ne se
   déclenche et la page reste entière.
   ===================================================================== */

/* La réserve garde hors du flux ce que l'affichage téléphone a retiré.
   Déclarée HORS de la media query, volontairement : si un contenu y
   restait coincé au moment d'une rotation, il ne doit s'afficher en vrac
   à AUCUNE largeur. */
.ow-reserve { display: none; }

@media (max-width: 47.99rem) {

  /* --- L'invitation au calculateur ------------------------------------ */

  /* Sur bureau, texte à gauche et bouton à droite : la ligne se lit d'un
     bord à l'autre. Empilés sur une colonne de 375 px, les deux restaient
     calés à gauche et le bouton flottait sous un texte plus large que lui,
     ce qui le faisait passer pour une note de bas de bloc. Centré, il
     redevient la conclusion du bloc. Demandé par Quentin le 04/08. */
  .invite-calc {
    flex-direction: column;
    justify-content: center;
    text-align: center;
  }
  /* `max-width: 40rem` + `margin: 0` gardaient le paragraphe collé à gauche
     dès qu'il était plus étroit que son conteneur. */
  .invite-calc p { margin-inline: auto; }

  /* ⚠️ `flex: 1 1 22rem` porte sur l'axe PRINCIPAL. En `row` c'est une
     largeur de base, en `column` c'est une HAUTEUR : le bloc passait de
     284 à 475 px, soit 191 px de vide sous le texte. Le piège se voit à la
     mesure, pas à la lecture de la règle. */
  .invite-calc-txt { flex: none; }

  /* --- Le pop-up partagé --------------------------------------------- */

  /* ⚠️ OUI, CE BLOC EST DANS UNE MEDIA QUERY, ET C'EST VOULU SUR CE SITE.
     Le skill demande de coller `ow-popup.css` HORS de toute media query,
     parce qu'un pop-up atteignable sur ordinateur perdrait tout style
     au-delà de 768 px — c'est arrivé six semaines sur le site jardin.

     Ici, il n'est atteignable nulle part ailleurs que sur téléphone :
     aucun déclencheur `data-popup` n'est écrit dans les gabarits, et le
     pop-up n'est ouvert que par `js/mobile.js`, lui-même conditionné à
     `matchMedia("(max-width: 47.99rem)")`. Vérifié à 1280 px le 24/09/2026,
     sur l'accueil ET sur une page de zone où la section visée n'existe pas
     dans le flux : aucun `<dialog>` n'est créé.

     L'exception est DÉCLARÉE, pas devinée : `tools/build.py` passe
     `--mobile-seulement` avec sa raison à `verifier_popup.py`, et cette
     raison s'affiche à chaque build.

     ⚠️ LE JOUR OÙ UN GABARIT DE CE SITE PORTERA `data-popup`, il faudra
     sortir ce bloc d'ici ET retirer la déclaration. Les deux vont ensemble. */

  /* ⚠️ Le `display` se pose sur `[open]`, jamais sur `.ow-modal`. La règle
     de navigateur `dialog:not([open]) { display: none }` a une spécificité
     plus faible : un `display: flex` posé sur la classe la battrait, et le
     pop-up resterait affiché en permanence, fermé ou non. */
  .ow-modal[open] { display: flex; }

  /* ⚠️ Mêmes dimensions que le pop-up du site jardin, dont ce motif est
     repris : une boîte CENTRÉE et bornée, pas une feuille plein écran.
     La première version occupait toute la largeur, ancrée en bas — elle se
     lisait comme une page et non comme une fenêtre. Signalé le 01/08.
     On laisse donc le navigateur centrer le <dialog> lui-même : ni
     `position`, ni `inset`, juste `margin: auto`. */
  .ow-modal {
    flex-direction: column;
    width: 100%;
    max-width: min(34rem, calc(100vw - 2rem));
    max-height: 88vh;
    max-height: 88dvh;
    margin: auto;
    padding: 0;
    border: 0;
    border-radius: var(--radius-card);
    background: var(--card);
    color: var(--card-foreground);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
  }

  .ow-modal::backdrop {
    background: var(--overlay);
    backdrop-filter: blur(3px);
  }

  /* Croix POSÉE sur le contenu, sans bandeau. Le rond bleu nuit translucide
     la rend lisible aussi bien sur le blanc des cartes que sur le bleu nuit
     de la section Entreprises.
     ⚠️ Le <dialog> porte `position: absolute` par défaut : il sert donc de
     référence à ce positionnement sans qu'on ait à le déclarer. */
  .ow-modal-close {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    z-index: 2;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Le plancher de zone tactile : voir --tap-target-min. */
    width: var(--tap-target-min);
    height: var(--tap-target-min);
    padding: 0;
    border: 0;
    border-radius: var(--radius-pill);
    background: color-mix(in oklab, var(--primary) 75%, transparent);
    color: var(--primary-foreground);
    cursor: pointer;
    transition: background var(--duration-fast) var(--ease);
  }
  .ow-modal-close:hover { background: var(--primary); }

  /* ⚠️ La croix de ow-popup.js est un SVG INLINE sans classe : le module
     commun ne doit dependre ni du sprite du site ni de sa classe `.icon`.
     Sa taille se pose donc ici. Sans cette regle, le SVG prend sa taille par
     defaut — 300x150 — et fait exploser le bouton. */
  .ow-modal-close svg { width: 20px; height: 20px; }

  /* ⚠️ DEUX règles contre UN seul défaut : le deuxième cercle autour de la
     croix, signalé par Quentin le 04/08 sur tous les pop-up.

     La règle globale `:focus-visible` pose `outline: 3px` à `outline-offset:
     2px`. Sur un bouton rond, cet anneau devient un cercle concentrique de
     54 px autour du cercle de 44. */

  /* 1. Le conteneur prend le focus à l'ouverture (voir mobile.js) : il ne
        doit pas dessiner d'anneau à son tour. Un <dialog> n'est pas un
        contrôle, l'indiquer au clavier n'apprend rien. */
  .ow-modal:focus,
  .ow-modal:focus-visible { outline: none; }

  /* 2. Au CLAVIER, la croix doit rester repérable — c'est une exigence, pas
        un ornement. L'anneau passe donc À L'INTÉRIEUR du bouton : même
        lisibilité, et toujours un seul cercle. */
  .ow-modal-close:focus-visible {
    outline: 3px solid var(--ring);
    outline-offset: -3px;
  }

  /* ⚠️ Aucun rembourrage horizontal ici : le pop-up reçoit des SECTIONS
     entières, qui portent déjà leur `.container` et leur fond. Un padding
     sur le corps laisserait une bande blanche autour du bleu nuit de la
     section Entreprises. Le formulaire, lui, reçoit le sien plus bas. */
  .ow-modal-body {
    overflow-y: auto;
    /* Le défilement ne se propage plus à la page derrière une fois le bas
       du pop-up atteint. */
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    padding: 0;
  }

  /* Les sections gardent leur fond — `.pro-band` est en bleu nuit avec du
     texte clair, le priver de son fond le rendrait illisible. Seul l'air
     vertical se resserre : `--section-y` se paie deux fois, ce qui fait
     120 px de vide dans une fenêtre qui n'en a pas besoin.
     ⚠️ Le haut est plus généreux que le bas : la croix n'a plus de bandeau
     à elle, elle est posée sur le contenu, et occupe les 56 premiers pixels
     du coin droit. Sans cette réserve, elle recouvrirait le titre. */
  .ow-modal-body .section {
    padding-block: calc(var(--space-10) + var(--space-4)) var(--space-5);
  }

  /* Le fond ne défile plus derrière le pop-up. */
  html.ow-modal-ouvert { overflow: hidden; }

  /* --- Le formulaire de devis, devenu un bouton ---------------------- */

  /* Le formulaire pesait l'essentiel des 1801 px du hero. Les deux boutons
     qui le remplacent gardent les mêmes actions à la même place : demander
     un devis, et calculer son prix. */
  .ow-hero-actions {
    display: grid;
    gap: var(--space-3);
    margin-top: var(--space-5);
  }
  /* Le bouton du calculateur arrive du formulaire, où il portait `btn-block`.
     Dans la grille il occupe déjà toute la largeur : sa marge propre ferait
     double emploi avec le `gap`. */
  .ow-hero-actions .btn { margin: 0; }

  /* Dans le pop-up, le formulaire n'a plus besoin de son propre cadre :
     le pop-up EST le cadre. Il reprend en revanche le rembourrage que le
     corps du pop-up ne porte plus. */
  .ow-modal-body .hero-form {
    /* Même réserve en haut que pour les sections : la croix y est posée. */
    padding: calc(var(--space-10) + var(--space-4)) var(--gutter) var(--space-6);
    border: 0;
    background: none;
    box-shadow: none;
  }

  /* --- Les sections de contenu, devenues des cartes ------------------ */

  .ow-cartes {
    display: grid;
    gap: var(--space-3);
    padding: var(--section-y) var(--gutter);
  }

  /* Une carte par section. Pleine largeur et non en grille de deux : les
     titres du site vont de « Questions fréquentes » à « Déménagement
     d'entreprise, de commerce et de cabinet », et sur une demi-largeur de
     téléphone le second passerait à cinq lignes. C'est aussi la
     disposition du site jardin sous 34 rem, dont ce motif est repris. */
  .ow-carte {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    padding: var(--space-4);
    font: inherit;
    color: inherit;
    text-align: left;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-sm);
    cursor: pointer;
    transition: box-shadow var(--duration-fast) var(--ease),
                border-color var(--duration-fast) var(--ease);
  }
  .ow-carte:active,
  .ow-carte:focus-visible {
    border-color: var(--primary-mid);
    box-shadow: var(--shadow-md);
  }

  .ow-carte-icone {
    width: 26px;
    height: 26px;
    color: var(--primary-mid);
  }

  /* Le chevron du sprite pointe vers le BAS : il sert aux listes
     déroulantes et à la FAQ. Ici il annonce une ouverture, donc il pointe
     vers la droite. */
  .ow-carte-chevron {
    color: var(--muted-foreground);
    transform: rotate(-90deg);
  }

  .ow-carte-texte {
    display: grid;
    gap: var(--space-1);
    min-width: 0;
  }

  .ow-carte-titre {
    font-family: var(--font-display);
    font-size: var(--text-md);
    line-height: 1.25;
    color: var(--primary);
  }

  /* Deux lignes au maximum. Au-delà, la carte redevient le paragraphe
     qu'on cherchait justement à replier, et les hauteurs se dérèglent. */
  .ow-carte-accroche {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: var(--text-xs);
    line-height: 1.4;
    color: var(--muted-foreground);
  }
}


/* =====================================================================
   ZONES TACTILES AU DOIGT
   =====================================================================
   Un lien de texte fait la hauteur de sa ligne : 19 px dans le pied de
   page, 27 px pour le téléphone du hero. C'est lisible, et intouchable
   au doigt.

   On élargit la boîte UNIQUEMENT pour un pointeur grossier — un doigt —
   pour ne pas aérer le pied de page d'un visiteur à la souris, qui n'a
   pas ce problème. `pointer: coarse` est le test exact : il vise le
   moyen de pointage, pas la largeur de l'écran, donc il couvre aussi la
   tablette et le portable tactile.

   ⚠️ Lighthouse ne mesure PAS ceci : le site annonçait 100 en
   accessibilité avec des cibles à 19 px. Le contrôle est manuel, il est
   dans docs/HANDOFF.md.

   Mesuré à 375 px : 12 cibles sous 44 px avant, 0 après.
   ===================================================================== */
@media (pointer: coarse) {
  .brand,
  .hero-phone-line a {
    min-height: var(--tap-target-min);
  }

  /* Les liens de liste et les liens légaux sont en flux : il faut leur
     donner une boîte avant de pouvoir leur donner une hauteur. */
  .site-footer ul li a,
  .footer-legal a {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap-target-min);
    vertical-align: middle;
  }

  /* Le fil d'Ariane est une NAVIGATION, pas de la prose : il se touche. */
  .breadcrumb a {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap-target-min);
  }

  /* Un paragraphe qui n'est QU'UN LIEN est un bouton déguisé : il prend
     le plancher. ⚠️ Un lien AU MILIEU D'UNE PHRASE ne le prend pas —
     l'agrandir casserait l'interligne, et WCAG 2.5.8 exempte
     explicitement les cibles en ligne dans un bloc de texte. C'est
     pourquoi le gabarit marque le premier d'une classe : aucun sélecteur
     CSS ne sait distinguer les deux. */
  .lien-seul > a {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap-target-min);
  }

  /* Le bouton compact repasse au-dessus du plancher : a 40 px il etait la
     seule vraie cible trop petite du calculateur. */
  .btn-sm { min-height: var(--tap-target-min); }
}
