/* A2T Couvreur — feuille de style partagée
   Tokens relevés sur a2t-couvreur.fr (voir brand-spec.md). Aucun hex inventé. */
:root{
  --bg:#060914; --band:#08101F; --surface:#0D1220; --inset:#0A1525;
  --fg:#FFFFFF; --muted:#9B9DA1; --fg-3:#82848A;
  --border:#1A1D27; --border-2:#2E303A;
  --accent:#F58A1F; --accent-hi:#F79A38; --accent-lo:#DD7A12;
  --accent-soft:rgba(245,138,31,.10); --accent-line:rgba(245,138,31,.28);
  /* Aplat d'action et etiquette des boutons : l'etiquette est BLANCHE dans les deux themes
     (demande 2026). L'aplat n'est donc pas le meme partout : orange vif ici, etiquette noire
     deplacee en blanc (7,8:1) et aplat descendu d'un cran en theme clair pour que le blanc y
     tienne aussi. Le signal fin (--accent : mots saillants, filets, puces) garde une valeur
     d'encre par theme : 8,1:1 sur le fond nuit ici, 4,6:1 sur le fond blanc en clair. */
  --accent-fill:#F58A1F; --accent-fill-hi:#F79A38; --accent-fill-lo:#DD7A12; --on-accent:#060914;
  /* Aplat du bouton « diagnostic », demande 2026 (couleur demandee en dur, puis etendue a tout
     le site) : #4B3F72, le meme aplat dans les DEUX themes. Ce n'est plus une derivation de la
     primaire : c'est la couleur propre du diagnostic, ce qui le separe nettement du « devis ».
     Elle est assez sombre pour tenir 9,3:1 sous une encre blanche et 7,9:1 de rive sur le fond
     clair le plus pale ; la rive sur les fonds sombres ne tient que 2,0:1, le boitier se lit
     donc par son aplat et non par son contour (meme contrat que l'ambre, qui ne tient que 2,1:1
     de rive en sombre).
     Survol #5A4C86 et appui #3A3059 : le premier s'eclaire sans descendre sous 6,7:1 sous
     l'encre blanche, le second s'assombrit sans toucher au contraste de l'encre.
     --diag-ink : l'encre du dessin ET de l'etiquette, blanche dans LES DEUX themes. Noire, elle
     ne tiendrait que 2,25:1 sur #4B3F72 (il en faut 4,5) : le blanc est la seule encre qui tienne.
     Jamais redite par le theme clair. */
  --diag-fill:#4B3F72; --diag-fill-hi:#5A4C86; --diag-fill-lo:#3A3059; --diag-ink:#FFFFFF;
  /* Touche violette du mot du logo (demande 2026) : la couleur du bouton « diagnostic », #403662,
     entre en couleur complementaire PERMANENTE dans le degrade peint du logo. Elle n'est jamais
     un aplat : c'est une encre posee sur des glyphes, donc elle doit tenir le contraste du texte.
     #403662 sur le bandeau nuit ne tient que 1,8:1 : en sombre la touche reprend la MEME couleur
     montee a L ,64 a teinte et chroma conserves (#8D84B6) — 5,5:1 sur le bandeau, 5,3:1 sur le
     fond le plus pale des quatre, et le melange le plus defavorable (viole sur ambre) 6,9:1.
     En theme clair la touche est #403662 tel quel : 10,5:1 sur le fond clair, 10,0:1 sur le
     bandeau, 9,2:1 sur l'insert, et 6,2:1 melangee a l'ambre clair. Aucun hex invente : c'est
     #403662, retourne en luminance comme le fait deja l'ambre d'un theme a l'autre. */
  --logo-touch:#8D84B6;
  --fg-soft:rgba(255,255,255,.06);
  /* Voile du bandeau. L'en-tête collant et la légende posée sur la photo laissent voir la
     page à travers une voile de la couleur du bandeau. La transparence est une valeur de
     calque, pas une couleur : elle ne change pas de thème, seule la teinte dessous change.
     D'où le canal séparé (--band-rgb) plutôt qu'un rgba() écrit en dur, qui resterait bleu
     nuit sur une page claire. */
  --band-rgb:6,9,20;
  /* Meme canal separe pour le FOND de la page : une photo posée derriere du texte se voile avec
     le fond de la section, pas avec le bandeau de navigation (les deux teintes different). */
  --bg-rgb:6,9,20;
  /* Le texte presque blanc, et le libellé du bouton fantôme (fond transparent, filet). En
     clair ils deviennent le noir du thème, au même pourcentage de voile : le sombre et le
     clair restent le même dessin, retourné. Les aplats ambres n'ont pas besoin de leur
     propre jeton : leur texte est déjà var(--bg), donc il suit le thème tout seul. */
  --fg-hi:rgba(255,255,255,.92);
  --btn-fg:rgba(255,255,255,.85);
  --font-display:'Montserrat','Montserrat Fallback','Helvetica Neue',Arial,sans-serif;
  --font-body:'Inter','Inter Fallback',-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;
  --font-mono:ui-monospace,'IBM Plex Mono','JetBrains Mono',Menlo,monospace;
  --pad:clamp(20px,8vw,120px);
  --radius:10px; --radius-lg:20px;
}

/* ---------- Thème clair ----------
   Le site se lit en sombre (le design capturé sur a2t-couvreur.fr) ou en clair, le clair étant
   demandé par le bouton de l'en-tête. Un seul mécanisme : html[data-theme="light"] redéfinit
   les mêmes jetons. Aucune règle de composant ne porte de couleur, tout passe par :root, donc
   les 19 pages basculent sans qu'aucune d'elles soit retouchée.

   Le clair n'est pas une palette inventée : c'est le miroir du sombre en OKLch. Même teinte
   (H 258-277° pour les bleus nuit, 73° pour l'ambre), chroma des neutres conservé, seul le
   canal L est retourné : les fonds gagnent en L ce que le texte perd, les filets restent à
   mi-chemin.

   Deux jetons ne peuvent pas être un simple miroir, et c'est voulu :
   - l'ambre, orange vif depuis la demande de 2026. En sombre, #F58A1F (L ,74) porte du texte
     sur fond nuit : 8,1:1. Le L retourné donnerait un orange pâle illisible sur fond blanc.
     L'ambre du thème clair est descendu à L ,53 (#A8500A) : 4,6:1 au pire des quatre fonds
     clairs. accent-hi et accent-lo descendent du même cran ; l'aplat des boutons (--accent-fill)
     est propre au thème clair (voir le bloc ci-dessous) et porte une etiquette blanche.
   - les fonds. Le sombre est très contrasté (bg L ,14 / inset L ,19) ; le clair ne peut pas
     descendre aussi bas sans devenir gris, donc l'écart entre les trois niveaux de fond est
     resserré (L ,985 / ,956 / ,940). Le relief se lit toujours, il est juste plus doux.

   Contrastes mesurés en clair : texte 17,1:1 · secondaire 6,9:1 · tertiaire 4,7:1 (4,5:1 sur
   le bandeau) · signal sur fond 4,6:1 au pire des quatre fonds · etiquette noire sur aplat
   d'action 4,8:1 au pire, et rive de cet aplat 3,4:1 (seuil 3:1 des limites non textuelles).
   Le seuil AA (4,5:1) est tenu partout. */
html[data-theme="light"]{
  --bg:#F8FAFE; --band:#F1F5FB; --surface:#EDF0F7; --inset:#E6ECF4;
  --fg:#131723; --muted:#55575A; --fg-3:#6E7076;
  --border:#D3D8E6; --border-2:#B4B7C3;
  --accent:#A8500A; --accent-hi:#92440A; --accent-lo:#7E3A08;
  --accent-soft:rgba(168,80,10,.10); --accent-line:rgba(168,80,10,.28);
  /* L'etiquette des boutons est blanche dans les DEUX themes (demande 2026), donc l'aplat
     d'action du theme clair ne peut plus rester l'orange vif : blanc sur #C2650E ne tient que
     4,07:1, et 3,61:1 au survol. Les aplats clairs sont descendus a L ,55 / ,57 / ,53 du meme
     orange (sature et teinte conservees) pour que le blanc tienne : #B05400 au repos (etiquette
     5,1:1 / rive 4,3:1 sur le fond le plus pale), #B65A00 au survol (4,7 / 4,0), #A94E00 a
     l'appui (5,9 / 4,8). Les deux seuils sont tenus partout : etiquette >=4,5:1 SUR l'aplat et
     rive de l'aplat >=3:1 sur les quatre fonds clairs. Le violet du diagnostic, lui, est deja
     assez sombre dans les deux themes et garde son aplat commun sous la meme encre blanche. */
  --accent-fill:#B05400; --accent-fill-hi:#B65A00; --accent-fill-lo:#A94E00; --on-accent:#FFFFFF;
  /* La touche du logo prend en clair la couleur demandee en dur, #403662 (10,0:1 sur le bandeau
     clair, 6,2:1 melangee a l'ambre du theme clair). */
  --logo-touch:#403662;
  /* Rien ici : le violet du diagnostic est demande identique dans les deux themes (il est
     assez sombre pour le fond clair, assez clair pour porter une encre blanche sur les deux).
     Reduire l'ecart theme/theme sur ce bouton, c'est aussi le rendu a l'impression. */
  --fg-soft:rgba(19,23,35,.06);
  --band-rgb:241,245,251;
  --bg-rgb:248,250,254;
  --fg-hi:rgba(19,23,35,.92);
  --btn-fg:rgba(19,23,35,.85);
  /* Ascenseurs, cases à cocher et champs du devis prennent le jeu clair. */
  color-scheme:light;
}

/* Thème par défaut : le sombre de la marque. Le clair n'arrive que si le visiteur l'a choisi
   (le script de chaque page pose data-theme avant la peinture) ou s'il a posé la bascule sur
   « système » ET que son appareil est en clair. Un visiteur qui n'a rien choisi, une capture, un
   partage et un devis imprimé restent donc ce qui a été validé. Le thème peint se lit sur
   data-theme ; la règle choisie (dark / light / system) se lit sur data-theme-rule. Ascenseurs et
   champs natifs suivent le thème PEINT, jamais la règle : un visiteur en « système » sur un
   appareil sombre est peint en sombre, ses ascenseurs doivent donc rester sombre. */
html{color-scheme:dark}
/* Polices auto-hébergées : plus de requête vers fonts.googleapis.com (ni le délai de la
   réponse, qui ouvrait la fenêtre d'échange et provoquait le saut de mise en page), et le site
   ne transmet plus l'IP du visiteur à Google — point soulevé par la CNIL sur les sites français.
   Fichiers = sous-ensemble latin de Google Fonts (OFL 1.1) : tout le texte du site tient dans
   ce sous-ensemble, vérifié caractère par caractère. 47 Ko + 35 Ko, en variable : une seule
   requête par famille couvre les 3 poids utilisés. */
@font-face{font-family:'Inter';font-style:normal;font-weight:400 600;font-display:swap;src:url('fonts/inter-latin.woff2') format('woff2')}
@font-face{font-family:'Montserrat';font-style:normal;font-weight:800 900;font-display:swap;src:url('fonts/montserrat-latin.woff2') format('woff2')}

/* Polices de substitution mesurées : si le fichier local échoue ou pendant son chargement,
   Inter/Montserrat sont plus larges que le substitut système, ce qui faisait gagner une ligne
   au paragraphe d'accueil et sauter la page (CLS 0,039).
   Deux pièges mesurés ici : (1) un seul size-adjust ne suffit pas, il faut le régler AU POIDS
   (Inter 400 = 1,065 × Arial Regular mais Inter 700 = 1,01 × Arial Bold) ; (2) local('Arial')
   rend les métriques d'Arial Regular même pour une plage 600-900, donc le gras doit appeler
   local('Arial Bold'). Les overrides d'ascent/descent ramènent la hauteur de ligne à 115 % de la
taille de police, ce que donnent Arial, Inter et Montserrat. Substitut : Arial (macOS/Windows) ; sinon sans-serif. */
@font-face{font-family:'Inter Fallback';font-weight:400 500;src:local('Arial');size-adjust:106.5%;ascent-override:90%;descent-override:25%;line-gap-override:0%}
@font-face{font-family:'Inter Fallback';font-weight:600 900;src:local('Arial Bold');size-adjust:101%;ascent-override:90%;descent-override:25%;line-gap-override:0%}
@font-face{font-family:'Montserrat Fallback';font-weight:800;src:local('Arial Bold');size-adjust:112.5%;ascent-override:90%;descent-override:25%;line-gap-override:0%}
@font-face{font-family:'Montserrat Fallback';font-weight:900;src:local('Arial Bold');size-adjust:112.5%;ascent-override:90%;descent-override:25%;line-gap-override:0%}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{margin:0;background:var(--bg);color:var(--fg);font-family:var(--font-body);font-size:15px;line-height:1.8;-webkit-font-smoothing:antialiased}
h1,h2,h3{font-family:var(--font-display);margin:0;text-wrap:balance}
h1 *,h2 *,h3 *{font-family:var(--font-display);margin:0;text-wrap:balance}
p{margin:0;text-wrap:pretty}
a{color:inherit}
img,svg{display:block;max-width:100%}
code{font-family:var(--font-mono);font-size:13px}
section{padding:76px var(--pad);border-top:1px solid var(--border);scroll-margin-top:var(--nav-h,96px)}
section:first-of-type{border-top:0}
.band{background:var(--band)}
.eyebrow{font-family:var(--font-mono);font-size:11px;letter-spacing:.16em;text-transform:uppercase;font-weight:500;color:var(--muted);margin-bottom:10px}
.h2{font-size:clamp(26px,3.2vw,38px);font-weight:800;letter-spacing:-.02em;line-height:1.12;margin-bottom:14px}
/* La couleur principale vit dans un segment de titre, pas dans le titre entier :
   h1 et h2 (y compris le h2 du bloc CTA, qui ne porte pas la classe .h2). */
h1 em,h2 em{font-style:normal;color:var(--accent)}
.lead{font-size:16px;color:var(--muted);line-height:1.85;max-width:64ch}
.meta{font-family:var(--font-mono);font-size:12px;color:var(--fg-3);letter-spacing:.04em}
.tag{display:inline-flex;align-items:center;gap:8px;background:var(--accent-soft);border:1px solid var(--accent-line);border-radius:999px;padding:6px 15px;margin-bottom:18px;text-decoration:none}
.tag span{font-size:11px;color:var(--accent);letter-spacing:.14em;text-transform:uppercase;font-weight:700}
.dot{width:7px;height:7px;border-radius:50%;background:var(--accent);flex:0 0 auto}

/* ── en-tête ── */
/* --nav-gap est declare ici et non laisse au 22 px litteral du nav : le bouton « diagnostic »
   est colle au « devis » par une marge negative de cette valeur, et il doit rester colle aux
   largeurs ou le creux change (16 / 12 / 8 px ailleurs dans la feuille).
   22 → 16 px : ce creux est compte trois fois dans la rangee desktop, le retirer rend 18 px aux
   deux CTA et fait entrer les cinq onglets dans la plus petite largeur (mesure : la rangee tient a
   951 px avec 76 px de jeu, elle plie a 950 px). */
.topnav{--nav-gap:16px;position:sticky;top:0;z-index:20;background:rgba(var(--band-rgb),.9);backdrop-filter:blur(14px);border-bottom:1px solid var(--border);padding:12px var(--pad);display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap}
.logo{font-family:var(--font-display);font-size:17px;font-weight:900;letter-spacing:-.02em;text-decoration:none}
.logo b{font-weight:900}
.logo small{display:block;font-family:var(--font-mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--fg-3);font-weight:400}
/* Mot du logo en dégradé blanc → ambre → blanc, sur les 19 pages. Le motif est large de
   200 % de la boîte et se répète : parcourir 0 → 200 % de position décale exactement d'une
   période, donc la boucle est sans raccord visible. Deux régimes, un seul rendu :
     - sans script (crawler, script bloqué) : la boucle CSS seule, 11 s par tour ;
     - avec assets/a2t.js : la classe .lg-run coupe l'image clé et le script reprend la même
       vitesse de base, augmentée de l'accent du défilement et des impulsions au clic.
   Les arrêts du dégradé sont les couleurs de la marque, donc aucun point ne descend
   sous 9,2:1 sur le fond nuit. background-clip:text est une peinture de glyphe, pas un masque :
   le mot reste du texte réel, lisible et indexable. Le <small> (communes) reste hors de
   l'élément peint : -webkit-text-fill-color s'hérite et le rendrait transparent. */
/* ── Le balayage prend deux couleurs de plus PENDANT le geste ────────────────────────────
   --lgh (0 % au repos, jusqu'à 100 % pendant un défilement ou un clic) mélange les deux rives
   de la bande d'ambre : la rive qui ferme le balayage descend vers --accent-lo (ambre profond),
   celle qui l'ouvre monte vers --accent-hi (ambre clair). Au repos --lgh vaut 0 : les deux
   rives valent --accent, le dégradé est exactement celui d'avant, une seule bande ambre. Un
   #F6B13D et un #DC9520 restent dans la palette de la marque (aucun hex inventé) et tiennent
   10,7:1 et 7,9:1 sur le fond nuit comme sur le fond clair. La règle du dessous est redite avec
   les couleurs mélangées : un navigateur sans color-mix() garde la première, celle d'origine. */
/* ── La touche violette : permanente, et legerement mobile en permanence ───────────────────
   Le mot porte desormais DEUX couches de degrade, toutes deux coupees aux glyphes :
     - dessous, la bande ambre de la marque, pilotee par la position de fond (le script au
       defilement et au clic, l'image cle sans script) — rien y change ;
     - dessus, la touche violee du bouton « diagnostic » (--logo-touch), toujours peinte.
   --lgv (0 % a 26 %, un tour de son motif) anime les ARRETS de la couche violette, pas sa
   position : la position de fond reste libre pour le balayage ambre, et les deux mouvements
   sont independants. Le motif a une periode de 26 % : parcourir 26 % ramene la touche exactement
   sur elle-meme, donc la boucle est sans raccord, legere, et ne s'arrete jamais — c'est
   le mouvement constant demande, il ne remplace pas l'accent du defilement et du clic.
   La couche violette est pleine (background-size:100 %), donc la position de fond que le script
   ecrit en inline ne deplace RIEN sur elle : c'est ce qui garde les deux regimes (avec et sans
   script) identiques sans toucher au script.
   Les arrets sont melanges avec transparent en oklab (68 % d'encre au repos) : la touche reste un
   voile sur l'encre du dessous, pas un aplat. Encres mesurees en peinturant le composite (_lgw-scan,
   voir plus bas) : au pire 6,0:1 en sombre (viole sur ambre) et 7,9:1 en clair. Une regle sans color-mix() la redit en touches pleines, aux MEMES positions.
   @property rend l'arret interpolable, et la regle declare les deux reglages A LEUR VALEUR DE REPOS
   (--lgv:0%, --lgw:32 %) : la ou @property n'existe pas (Safari < 16.4, Firefox < 128) l'arret n'est
   pas interpole mais il reste RESOLU — sans cette declaration, --lgv et --lgw seraient indefinis, le
   degrade entier deviendrait invalide et le mot ne serait plus peint du tout. La ou @property
   existe, une custom property non enregistree saute a mi-course : pour --lgv le saut de 0 % a 26 % ne se voit
   pas, car 26 % est LA PERIODE du motif et ramene la touche exactement ou elle etait ; pour --lgw
   le saut de 46 % a 22 % se lit comme deux legeres pulsations de la touche — elle reste peinte et
   le mot garde ses deux couleurs. Sans animation, la touche reste peinte a son --lgw de repos.

   LE MOUVEMENT CONSTANT, MESURE (outils _lgmoved / _lgidle / _lgw-scan, accueil a 1280 px, sans
   geste) : le glissement seul ne deplacait que 421 pixels sur 8 771 par seconde, delta moyen
   2,5/255 — le mouvement etait la mais ne se voyait pas. Le cycle descend donc a 6,5 s et la TOUCHE
   elle-meme se met a respirer avec lui (--lgw, le transparent du melange : 46 % pale -> 22 % forte
   -> 46 %, une montee par cycle, sans raccord). Les deux reglages sont dans la MEME image cle : un
   seul nom, une seule duree, les deux mouvements restent synchronises et l'un ne peut pas couper
   l'autre.
   L'amplitude est bornee par le contraste REEL de l'encre, mesure en peinturant le composite :
   la touche semi-transparente posee sur l'encre OPAQUE du dessous (blanc de la marque ou ambre),
   relue en sRGB par le moteur et rapportee au bandeau — jamais la touche seule sur le bandeau, cet
   etat n'existe pas puisque la couche du dessous est opaque. La borne basse du cycle (22 %) est un
   reglage de LOOK : a 0 % de transparent la touche devient un aplat qui masque la bande d'ambre —
   elle passerait encore (5,8:1) mais le mot ne serait plus bicolore ; la borne haute (46 %) est le
   point ou la touche devient trop discrete. Aux deux bouts et partout entre les deux : 6,0:1 en sombre (viole sur ambre, a la borne la plus forte de
   l'encre) et 7,9:1 en clair (a la borne la plus pale : l'ambre claire du theme clair monte avec
   l'encre, le fond sombre du theme nuit descend avec elle) — jamais sous le seuil du texte courant.
   Le blanc de la marque, lui, va de 8,1:1 a 11,7:1 en sombre et de 11,5:1 a 13,5:1 en clair. Au
   repos (sans animation, mouvement reduit, navigateur sans @property)
   --lgw vaut 32 % : la touche est peinte comme avant, seulement immobile. */
@property --lgv{syntax:"<percentage>";inherits:false;initial-value:0%}
@property --lgw{syntax:"<percentage>";inherits:false;initial-value:32%}
@supports (background-clip:text) or (-webkit-background-clip:text){
  .logo .lg-wm{--gs:26%;--gw:26%;--lgh:0%;background-image:linear-gradient(105deg,transparent 0,var(--logo-touch) 28%,transparent 38%,transparent 45%,var(--logo-touch) 54%,transparent 64%,transparent 71%,var(--logo-touch) 80%,transparent 90%,transparent 97%,var(--logo-touch) 106%,transparent 116%),linear-gradient(105deg,var(--fg) calc(var(--gs) - var(--gw)),var(--accent) var(--gs),var(--fg) calc(var(--gs) + var(--gw)));background-image:linear-gradient(105deg,transparent calc(var(--lgv) - 26%),color-mix(in oklab,var(--logo-touch),transparent var(--lgw)) calc(var(--lgv) - 17%),transparent calc(var(--lgv) - 7%),transparent calc(var(--lgv) + 19%),color-mix(in oklab,var(--logo-touch),transparent var(--lgw)) calc(var(--lgv) + 28%),transparent calc(var(--lgv) + 38%),transparent calc(var(--lgv) + 45%),color-mix(in oklab,var(--logo-touch),transparent var(--lgw)) calc(var(--lgv) + 54%),transparent calc(var(--lgv) + 64%),transparent calc(var(--lgv) + 71%),color-mix(in oklab,var(--logo-touch),transparent var(--lgw)) calc(var(--lgv) + 80%),transparent calc(var(--lgv) + 90%),transparent calc(var(--lgv) + 97%),color-mix(in oklab,var(--logo-touch),transparent var(--lgw)) calc(var(--lgv) + 106%),transparent calc(var(--lgv) + 116%),transparent calc(var(--lgv) + 123%),color-mix(in oklab,var(--logo-touch),transparent var(--lgw)) calc(var(--lgv) + 132%),transparent calc(var(--lgv) + 142%)),linear-gradient(105deg,var(--fg) calc(var(--gs) - var(--gw)),color-mix(in oklab,var(--accent),var(--accent-lo) var(--lgh)) calc(var(--gs) - .42 * var(--gw)),color-mix(in oklab,var(--accent),var(--accent-hi) var(--lgh)) calc(var(--gs) + .42 * var(--gw)),var(--fg) calc(var(--gs) + var(--gw)));background-size:100% 100%,200% 100%;background-repeat:no-repeat,repeat;background-position:var(--gs) 0;-webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent;--lgv:0%;--lgw:32%;animation:a2t-lg-sweep 11s linear infinite,a2t-lg-touch 6.5s linear infinite}
}
/* Les deux couches sont posees a la MEME position de fond ; la couche violette etant pleine
   (background-size:100 %), un deplacement de position ne deplace rien sur elle (0 px de jeu) : le
   balayage ambre ne l'entraine pas, et le script reste ecrit tel quel. */
/* 50 % = LA MOITIE de la periode (13 % du motif) : le glissement garde la meme vitesse sur les
   deux segments (linear), seule l'encre respire. La boucle se referme sans raccord. */
@keyframes a2t-lg-touch{0%{--lgv:0%;--lgw:46%}50%{--lgv:13%;--lgw:22%}100%{--lgv:26%;--lgw:46%}}
.logo{transition:transform .18s ease}
.logo:active{transform:translateY(1px)}
@keyframes a2t-lg-sweep{from{background-position:0% 0}to{background-position:200% 0}}
/* Le script pilote background-position en style inline ; une image clé est plus prioritaire
   qu'une déclaration inline, donc elle écraserait sa position : coupée dès qu'il pilote. */
/* Le script ne pilote que la POSITION : il coupe l'image cle du balayage ambre, pas la boucle de
   la touche violee, qui doit rester mobile au repos. Liste de noms raccourcie a un seul : il faut
   redire la duree, sinon la premiere (11 s) s'appliquerait a la touche. */
.logo .lg-wm.lg-run{animation-name:a2t-lg-touch;animation-duration:6.5s}
/* Mouvement reduit : les deux boucles se coupent, la touche violette reste peinte (posnee a son
   --lgv de repos), le mot n'est jamais gris ni ampute d'une de ses deux couleurs. */
@media (prefers-reduced-motion:reduce){.logo .lg-wm{animation:none}.logo{transition:none}.logo:active{transform:none}}
@media (forced-colors:active){.logo .lg-wm{background:none;color:CanvasText;-webkit-text-fill-color:CanvasText}}
/* un nav d'onglets plie sur deux rangees coutait 63 px de barre et 121 px de contenu pousse
   (mesure a 920 px) : sous la largeur ou les cinq onglets tiennent, ce ne sont plus les onglets
   qui se replient mais les liens qui passent dans le volet deploye — donc wrap reste la valeur
   de base, elle ne sert plus qu'au repli sans script (voir « en-tete responsive »). */
.topnav nav{display:flex;gap:var(--nav-gap);align-items:center;flex-wrap:wrap}
.topnav nav a{font-size:13px;color:var(--muted);text-decoration:none;padding:8px 0;border-bottom:1px solid transparent}
.topnav nav a:hover{color:var(--fg);border-bottom-color:var(--fg-3)}
.topnav nav a[aria-current="page"]{color:var(--fg);border-bottom-color:var(--fg)}
/* Le CTA de l'en-tête n'hérite pas du padding de .btn (la règle .topnav nav a est plus
   spécifique) : il est redéclaré ici, en couleur principale. Son creux horizontal ne suit plus la
   largeur de l'ecran : le clamp 20 → 28 px donnait 139 px de boitie a 1024 px comme a 1440 px, il
   ne gagnait rien a l'agrandissement de la page et coQtait 33 px a la rangee. Fixe a 18 px, la
   paire soudee passe de 192 px a 166 px (diagnostic 47 + devis 119) ; les 11 px verticaux et la
   hauteur rendue de 47 px ne bougent pas, le CTA reste a 44 px tactiles au minimum. */
.topnav nav a.btn{padding:11px 18px;border:1px solid var(--accent-fill);background:var(--accent-fill);color:var(--on-accent);border-radius:0 var(--radius) var(--radius) 0;font-weight:700;letter-spacing:.01em}
.topnav nav a.btn:hover{background:var(--accent-fill-hi);border-color:var(--accent-fill-hi);color:var(--on-accent)}
.topnav nav a.btn:active{background:var(--accent-fill-lo);border-color:var(--accent-fill-lo);color:var(--on-accent)}
/* Bouton « Diagnostic de toiture », colle au CTA « devis » dont il reprend la forme, pas la
   couleur : il prend le violet --diag-fill, en icône seule dans l'en-tête (loupe + signal). La
   paire se lit comme une seule action, et le plus petit des deux ne vole pas le premier
   regard. Le mot est dans le HTML, jamais dans une infobulle : il fait le nom accessible
   du lien (donc un lecteur d'ecran l'entend sans survol) et il se discover au survol,
   au clavier et au doigt, ou une souris n'existe pas.

   La zone tactile reste a 44 px (brand-spec.md) : le pseudo-lement deborde le boitier
   de 4 px sur tout le tour, le visuel de 36 px ne change pas. Le mot n'est masque que
   par sa propre largeur, jamais par un overflow sur le bouton, sinon le pseudo-lement
   serait rogne.

   Demande 2026 : le mot est ecrit en clair PARTOUT, sauf dans l'en-tête. Le corps de page
   n'a pas de compte a faire, il affiche donc le label comme le bouton « devis » voisin, au
   meme calibre (14 px / 600) ; seul l'en-tête le replie en icône seule (mesure : un mot de
   plus y fait passer la barre sur deux lignes a 1024 px). */
.diag-cta{position:relative;display:inline-flex;align-items:center;gap:9px;height:36px;padding:0 15px;border:1px solid var(--diag-fill);border-radius:var(--radius);background:var(--diag-fill);color:var(--diag-ink);text-decoration:none;cursor:pointer;transition:background .2s ease,border-color .2s ease}
.diag-cta::after{content:"";position:absolute;inset:-4px}
/* Loupe + signal (electrocardiogramme dans la lentille) : le diagnostic ne se lit plus comme un
   battement, il se lit comme une inspection. L'encre est le jeton --diag-ink, pas currentColor
   herite de l'etiquette : le dessin doit rester blanc sur l'aplat violet dans les deux themes.
   22 px (demande 2026, 19 px avant) : le trait suit, 1,9 -> 2,1, sinon la loupe parait maigre a
   cette taille. Le dessin reste sous la hauteur de texte + marges du boitier : l'icone ne pousse
   jamais la hauteur du bouton, donc pas le duo ni l'en-tete (verifie : hauteur de barre et
   alignement du « devis» identiques). */
.diag-cta svg{flex:0 0 auto;width:22px;height:22px;fill:none;stroke:var(--diag-ink);stroke-width:2.1;stroke-linecap:round;stroke-linejoin:round}
/* En clair, et donc au corps de page : le mot est dans le bouton, comme celui du « devis »,
   meme calibre que lui. */
.diag-txt{display:inline-flex;align-items:center;font-size:14px;font-weight:600;letter-spacing:.01em;white-space:nowrap}
/* Dans l'en-tete seul, le mot ne tient pas dans la rangee : un mot de plus y faisait passer la
   barre sur deux lignes a 1024 px. Il n'est donc pas ecrit dans le boitier, mais il reste dans
   le HTML et dans le nom accessible du lien (retrait a 1 px + clip-path, jamais display:none, qui
   le ferait disparaitre de la lecture d'ecran). C'est l'infobulle du dessous qui le montre a
   l'oeil. */
.topnav nav .diag-txt{position:absolute;width:1px;height:1px;padding:0;border:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.diag-cta:hover,.diag-cta:focus-visible,.diag-cta:focus-within{background:var(--diag-fill-hi);border-color:var(--diag-fill-hi);color:var(--diag-ink)}
.diag-cta:active{background:var(--diag-fill-lo);border-color:var(--diag-fill-lo);color:var(--diag-ink)}
.diag-cta:focus-visible{outline:2px solid var(--accent);outline-offset:3px}

/* Infobulle, demande 2026 : le mot est dit HORS du bouton, pose a 10 px au-dessous de lui (le
   bouton d'en-tete est colle au bord haut de la page, une bulle au-dessus n'aurait aucune place),
   et jamais colle a lui : le creux de 10 px est celui du volet deploye sous l'en-tete.
   - le fond est celui du bandeau du theme courant (--band : #08101F en sombre, #F1F5FB en clair),
     avec le filet --border-2 et l'etiquette --fg du theme courant : 19:1 en sombre, 16,3:1 en
     clair. La bulle suit donc la bascule sans regle de theme separée.
   - ancrée par sa droite (right:0) et non par son centre : le bouton d'en-tete touche le bord
     droit de la page a toutes les largeurs, une bulle centree deborderait du document et ferait
     apparaitre une ascenseur horizontal (interdit par la demande sur l'entete).
   - le mot du bouton reste a cote (corps de page) ou replie (en-tete) ; la bulle est aria-hidden,
     le lien est deja nomme par .diag-txt : la bulle ne fait pas un second nom accessible.
   - elle ne mange pas le survol : pointer-events:none, sinon la souris en allant vers elle
     quitterait le bouton et la ferait clignoter.
   Sans script : la bulle est dans le HTML, elle se discover au survol et au clavier en CSS pur. */
.diag-tip{position:absolute;right:0;top:calc(100% + 10px);z-index:30;padding:7px 11px;border:1px solid var(--border-2);border-radius:var(--radius);background:var(--band);color:var(--fg);font-size:12.5px;font-weight:600;line-height:1.4;letter-spacing:.01em;white-space:nowrap;text-align:center;opacity:0;visibility:hidden;pointer-events:none;box-shadow:0 10px 22px rgba(0,0,0,.35);transition:opacity .18s ease,transform .22s cubic-bezier(.22,.7,.2,1),visibility 0s linear .22s}
.diag-cta:hover .diag-tip,.diag-cta:focus-visible .diag-tip,.diag-cta:focus-within .diag-tip{opacity:1;visibility:visible;transition:opacity .18s ease,transform .22s cubic-bezier(.22,.7,.2,1),visibility 0s}
/* Le bouton de l'en-tête est littéralement colle au CTA « devis » : le creux du nav est annule
   par une marge negative de la valeur meme de var(--nav-gap), donc le collant tient aux trois
   largeurs ou le creux change (22 / 14 / 12 px). Les deux moities ne forment plus qu'un seul
   aplat : rayon plein a gauche pour le diagnostic, a droite pour le devis. Son creux interne
   rejoint celui du devis (11 px verticaux, 13 px horizontaux — un peu plus que les 10 px du
   corps de page) et sa hauteur n'est pas declaree : il s'etire sur la hauteur de la ligne du
   nav, donc exactement celle du « devis » (mesure : 47 px, pas 44 — le creux vertical du devis
   + sa ligne de texte les font), a condition de ne jamais descendre sous les 44 px tactiles. */
.topnav nav .diag-cta{height:auto;align-self:stretch;min-height:44px;padding:11px 13px;margin-right:calc(-1 * var(--nav-gap,22px));border-radius:var(--radius) 0 0 var(--radius);color:var(--diag-ink)}
/* L'encre est redite ici : le mot masque du bandeau herite de la couleur de la rangee de nav (un
   gris) et non de l'aplat. Cache, il ne se voit pas ; au 3e palier il reprend sa place dans le
   boitier (hover:none) et il serait gris sur le violet. */
/* Une fois colle, le boitier tactile de 4 px ne doit plus deborder sur le « devis » voisin :
   il voleralt les clics a 1 px de la couture. Le debordement se retire sur le seul cote devis. */
.topnav nav .diag-cta::after{inset:-4px -1px -4px -4px}
/* Au doigt, le survol n'existe pas : dans l'en-tête le mot reprend sa place dans le bouton,
   sinon il ne dirait jamais ce qu'il est avant de le quitter des yeux. Le corps de page n'a
   rien a corriger, son mot y est deja ecrit.
   Le repli ne vaut qu'au-dessus du point de rupture : dans la rangee du haut d'un en-tete a
   volet, le mot reduit tiendrait plus (a 390 px, mot + devis depassent de 176 px la rangee),
   et le volet lui meme affiche deja le diagnostic en clair dans le corps de chaque page.
   Son seuil n'est pas celui du volet : c'est le point ou la rangee COMPLETE tient avec le mot
   ecrit. Le mot en clair ajoute 142 px au bouton, et a creux de 16 px la rangee logo + cinq
   onglets + bascule + paire avec le mot ne tient qu'a partir de 1200 px (mesure au doigt : 74 px a
   1200 px, 137 px a 1190 px). La valeur precedente faisait donc plier la barre d'un portable
   tactile entre 1022 et 1199 px. Sous 1200 px le bouton au doigt reste en icone seule : il est
   deja nomme par son aria-label, et toute page affiche le diagnostic en clair dans son corps. */
@media (hover:none){
  /* Au doigt, le survol n'existe pas : un appui mene a la page du diagnostic, ou le mot est ecrit
     en clair dans le corps de chaque page. La bulle n'a donc rien a rattraper. */
  .diag-tip{display:none}
}
/* Au doigt ET a l'aise en largeur (mesure : la rangee complete tient avec le mot a partir de
   1200 px), le mot du bouton d'en-tete reprend sa place dans le boitier, comme avant : le bouton
   ne doit pas rester icone seule la ou il y a de la place et ou le survol n'existe pas. */
@media (hover:none) and (min-width:1200px){
  .topnav nav .diag-txt{position:static;width:auto;height:auto;padding:0;clip-path:none}
}
/* Bascule de thème : un bouton, dans l'en-tête des 19 pages, entre le dernier onglet et le
   CTA. Icône seule (le libellé est porté par aria-label) : le mot « Thème » ajouterait ~70 px
   a un en-tête déjà plein, et l'icône soleil/lune se lit sans traduction.
   - 44 × 44 px : la cible tactile minimale, alors que l'onglet voisin n'a que 8 px de creux.
     Le carré est déclaré ici (et non laisse a la taille de l'icone) pour que le bouton ne
     change pas de dimension au changement d'icone : aucun décalage des liens voisins.
   - le bouton n'apparait que si le script du <head> s'est exécuté (classe a2t-js posée avant
     la peinture, donc pas de bouton qui saute en place) : sans JavaScript, un bouton qui
     semble éclairer la page et ne fait rien serait un faux lien. Le thème clair reste donc
     réservé aux navigateurs qui exécutent le script ; le contenu, lui, est identique.
   - le dessin suit les autres éléments de l'en-tête : fond transparent, filet --border-2,
     couleur --muted, et le meme trio au survol que les onglets.
   - les trois icônes sont écrites dans le HTML, une seule est masquée par la feuille : le bouton
     n'attend pas le script pour être à l'état, et le passage de l'une a l'autre ne coute
     aucune mesure.
   - Demande 2026 : troisième règle « système », qui suit l'appareil. La bascule tourne sur trois
     modes (sombre → clair → système → sombre) et l'icône montrée reste CELE DU MODE OU L'ON VA,
     comme avant (sombre → soleil, clair → écran de contraste, système → lune). Le dessin qui
     décide est porté par `data-theme-rule` (la REGLE choisie), pas par `data-theme` (le thème
     PEINT) : les deux vivent séparément, sinon un visiteur en « système » dont l'appareil passe
     en clair le soir verrait son icône changer de destination toute seule.
   - aucune couleur d'état sur le bouton : il garde le dessin des autres éléments de l'en-tête
     (fond transparent, filet --border-2, --muted), donc aucun second accent n'entre dans la barre. */
.theme-btn{display:none}
html.a2t-js .theme-btn{display:inline-flex;align-items:center;justify-content:center;width:44px;height:44px;padding:0;border:1px solid var(--border-2);border-radius:var(--radius);background:transparent;color:var(--muted);cursor:pointer;transition:background-color .2s ease,border-color .2s ease,color .2s ease}
.theme-btn:hover{color:var(--fg);border-color:var(--fg-3);background:var(--surface)}
.theme-btn:active{transform:translateY(1px)}
.theme-btn svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round}
.theme-btn .tb-core{fill:currentColor;stroke:none}
.theme-btn .tb-moon,.theme-btn .tb-auto{display:none}
/* Regle « clair » : le bouton mene au mode système, il montre donc l'ecran de contraste. */
html[data-theme-rule="light"] .theme-btn .tb-sun{display:none}
html[data-theme-rule="light"] .theme-btn .tb-auto{display:block}
/* Regle « système » : le bouton mene au sombre, il montre donc la lune. */
html[data-theme-rule="system"] .theme-btn .tb-sun{display:none}
html[data-theme-rule="system"] .theme-btn .tb-moon{display:block}
.theme-btn:focus-visible{outline:2px solid var(--accent);outline-offset:3px}

/* ── en-tete responsive ──
   Le point de rupture n'est pas choisi, il est mesure : a 951 px de large la barre tient en une
   rangee (178 px de logo + 605 px de nav : cinq onglets + bascule de theme + les deux CTA soudes,
   dans un retrait de 8 % de chaque cote, et 76 px de jeu) ; a 950 px elle plie sur deux rangees et
   passe de 74 px a 137 px. Meme valeur sur les pages a onglets (le mot « Etancheite » est le plus
   large des cinq, les onglets font 297 px a eux cinq). Le volet entre donc a 950 px : 71 px de
   plage desktop de plus, gagnee sur la paire de CTA (192 → 166 px) et le creux de rangee
   (22 → 16 px), sans toucher au calibre des cinq onglets (13 px). Un ecran de 1024 px garde donc
   ses liens en clair, a souris comme au doigt : les deux CTA de droite ne se contractent qu'a
   partir de 561 px.

   En dessous, ce sont les CINQ ONGLETS qui quittent la barre, pas la barre qui grandit. Le bouton
   « Menu » deploie un volet accroche sous l'en-tete (top:100% du bandeau colle) :
   - les deux CTA de droite restent dans la rangee du haut a toutes les largeurs, soudes l'un a
     l'autre, jamais retractes jusqu'a disparaitre : le diagnostic garde son icone (son mot n'est
     deja pas dans la boitie, il se discover au survol comme au-dessus de 950 px), le devis garde
     « Devis gratuit » jusqu'a 561 px puis « Devis » ;
   - l'en-tete ne change jamais de hauteur : 74 px ferme, 74 px ouvert. Le volet est un calque pose
     SOUS le bandeau, il ne pousse ni la barre ni le contenu, et comme le bandeau est colle il suit
     l'ecran. Il se referme au premier lien suivi, a Echap, et des qu'on remonte au-dessus de
     950 px ; la page ne defile plus tant qu'il est ouvert (un volet ouvert sur une page qui
     defile n'est plus axe sur son bouton).
   La bascule de theme n'est pas un CTA : elle descend dans le volet avec les liens, dont elle suit
   l'ordre du DOM.

   Sans JavaScript : pas de bouton « Menu » (il ne proposerait rien) et les cinq onglets restent
   dans la barre, en deux rangees comme avant. C'est le seul cas ou l'en-tete peut depasser 74 px,
   et il ne concerne que les navigateurs qui n'executent pas le script : le contenu, lui, reste
   entierement accessible. Le volet ne peut pas cacher la navigation sans script : ses regles
   portent la classe a2t-js, posee par le script du <head> avant la peinture. */
.nav-burger{display:none}
/* Le conteneur des liens est transparent au-dessus de 950 px : display:contents ne genere aucune
   boite, les regles .topnav nav a et l'ordre du DOM restent exactement ce qu'ils etaient. */
.nav-links{display:contents}
@media (max-width:950px){
  /* 44 × 44, meme dessin que la bascule de theme. Le bouton n'apparait que si le script du <head>
     est passe (classe a2t-js) : sans JavaScript le volet ne peut pas se deployer, donc aucun
     bouton ne le propose et les onglets restent en clair dans la barre, en deux rangees. */
  html.a2t-js .nav-burger{display:inline-flex;align-items:center;justify-content:center;width:44px;height:44px;padding:0;border:1px solid var(--border-2);border-radius:var(--radius);background:transparent;color:var(--muted);cursor:pointer;transition:background-color .2s ease,border-color .2s ease,color .2s ease}
  .nav-burger:hover{color:var(--fg);border-color:var(--fg-3);background:var(--surface)}
  .nav-burger:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
  .nav-burger svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;transition:transform .26s cubic-bezier(.22,.7,.2,1)}
  /* Les trois traits se replient, la croix apparait : les deux dessins sont ecrits dans le SVG,
     l'un est masque par l'etat (comme les deux icones de la bascule de theme) — le bouton n'attend
     aucune mesure pour etre a l'etat, et aucun trace n'est anime par transform. La rotation de
     180° ne se voit que pendant le geste (une croix est symetrique) : elle rend le remplacement
     lisible au lieu d'un simple clignotement. */
  html.nav-open .nav-burger svg{transform:rotate(180deg)}
  .nav-burger .nb-bars line{transition:opacity .16s ease,transform .2s ease;transform-box:view-box;transform-origin:12px 12px}
  .nav-burger .nb-x{opacity:0;transition:opacity .2s ease}
  html.nav-open .nav-burger .nb-bars line{opacity:0;transform:scaleX(.55)}
  html.nav-open .nav-burger .nb-x{opacity:1}

  /* Le nav garde la rangee du logo (il n'y a plus que le bouton Menu et les deux CTA) : pas de
     pleine largeur, pas de repli. Tout ce qui transforme le conteneur en volet porte la classe
     a2t-js : sans script le volet ne peut pas s'ouvrir, donc les liens restent dans la barre et ne
     peuvent jamais etre caches. visibility:hidden ferme le volet a la navigation clavier (un lien
     masque ne doit pas etre atteignable) ; le retard de 0,30 s a la fermeture laisse la sortie se
     jouer. Le volet part de 10 px au-dessus de sa place, comme une rangee qui se deploie, avec la
     courbe du site ; il prend le retrait de la barre, donc ses cartes s'alignent sur le logo. */
  html.nav-open{overflow:hidden}
  html.a2t-js .nav-links{position:absolute;left:0;right:0;top:100%;display:flex;flex-wrap:wrap;gap:10px;padding:14px var(--pad) 22px;margin:0;background:rgba(var(--band-rgb),.98);backdrop-filter:blur(14px);border-top:1px solid var(--border);box-shadow:0 14px 26px rgba(0,0,0,.3);opacity:0;visibility:hidden;transform:translateY(-10px);transform-origin:top center;pointer-events:none;transition:opacity .18s ease,transform .3s cubic-bezier(.22,.7,.2,1),visibility 0s linear .3s}
  html.a2t-js.nav-open .nav-links{opacity:1;visibility:visible;transform:none;pointer-events:auto;transition:opacity .22s ease,transform .3s cubic-bezier(.22,.7,.2,1),visibility 0s}

  /* Les cinq onglets en clair, deux par deux, plus la bascule de theme en sixieme carte. Les
     regles portent .topnav nav : la feuille de base (.topnav nav a, et son etat actif) est plus
     specifique qu'un simple .nav-links a et laisserait un filet de fond transparent sous les
     cartes. */
  html.a2t-js .topnav nav .nav-links a{flex:1 1 calc(50% - 5px);min-height:44px;display:flex;align-items:center;justify-content:center;padding:8px 10px;border:1px solid var(--border-2);border-bottom:1px solid var(--border-2);border-radius:var(--radius);background:var(--surface);font-size:14px}
  html.a2t-js .topnav nav .nav-links a:hover{border-color:var(--fg-3);background:var(--inset)}
  html.a2t-js .topnav nav .nav-links a[aria-current="page"]{border-color:var(--accent-line);background:var(--accent-soft)}
  html.a2t-js .nav-links .theme-btn{flex:1 1 calc(50% - 5px);width:auto;min-height:44px;border-color:var(--border-2)}
  /* Le bouton « Menu » se place a la gauche du groupe de droite, contre le logo, puisque les deux
     CTA gardent l'extreme droite a toutes les largeurs. Le creux du nav tombe a 12 px, et
     --nav-gap avec lui : le collage du diagnostic au devis se calcule sur cette valeur, donc la
     paire reste soudee a ce palier. Budget mesure, creux de 12 px : a 950 px, 806 px utiles pour
     178 de logo + 44 de menu + 45 de diagnostic + 121 de devis + 24 de creux = 412, soit 394 px de
     jeu : la rangee du haut n'a jamais ete aussi peu chargee. */
  .nav-burger{order:-1}
  .topnav nav{--nav-gap:12px}
  .topnav{gap:12px}
}

/* La rangee du haut ne contient plus le logo complet et le mot complet du devis sous 561 px.
   Budget mesure (largeurs rendues, creux de 12 px, retrait de 8 %) : a 561 px, 471 px utiles pour
   178 de logo avec son sous-titre + 44 de menu + 53 de diagnostic + 139 de devis + 24 de creux
   = 438, soit 33 px de jeu ; a 560 px, 470 utiles pour 462 une fois le sous-titre retire (121) et
   le devis retracte (62) + 44 + 45 + 24 = 296, 174 px de jeu. Le palier est donc place a 560,
   40 px avant la limite reelle (521 px), pour ne pas dependre du rendu exact des glyphes.
   - le sous-titre du logo tombe : le mot « A2T Couvreur » reste, la zone est deja dite par la
     page de titre, le fil d'Ariane et la legende du hero ;
   - le libelle du devis se retracte a « Devis » par la meme mecanique que celui du diagnostic : le
     mot est rogne par la largeur du boitier, jamais par un retrait de texte, donc le texte reste
     entier dans le HTML et le nom accessible ne change pas. */
@media (max-width:560px){
  .logo small{display:none}
  .topnav nav a.btn{overflow:hidden;white-space:nowrap;width:62px;padding:11px 12px}
  .topnav nav .diag-cta{padding:11px 12px}
}

/* Dernier palier : mesuree a 320 px, la rangee fait 268,8 px utiles et la rangee en reclame
   269,2 (107 de mot + 44 de menu + 44 de diagnostic + 58 de devis + 16 de creux) : elle casse
   sur une DEUXIEME ligne, et l'en-tete collant vole alors 107 px sur 900 a la page. Le mot du
   logo passe a 14 px (99,8 au lieu de 107, 7,4 px de jeu) : c'est le seul element de la rangee
   qui puisse perdre des pixels sans rogner un gabarit tactile de 44 px ni couper un CTA en
   overflow:hidden. Les 44 px de hauteur et le collage des deux CTA ne bougent pas, et le retrait
   de l'en-tete reste aligne sur celui des sections. */
@media (max-width:360px){
  .topnav nav{--nav-gap:8px}
  .topnav{gap:8px}
  .logo{font-size:14px}
  .topnav nav a.btn{width:58px;padding:11px 10px}
  .topnav nav .diag-cta{padding:11px 10px}
}
.tag-n{background:transparent;border-color:var(--border-2)}
.tag-n span{color:var(--muted);letter-spacing:.14em}
.topnav a:focus-visible,.btn:focus-visible,.faq summary:focus-visible,.zones a:focus-visible,.xlink:focus-visible,.foot nav a:focus-visible,.crumbs a:focus-visible,.legal a:focus-visible{outline:2px solid var(--accent);outline-offset:3px}

/* ── boutons ── */
.btn{display:inline-flex;align-items:center;gap:10px;min-height:44px;padding:13px 24px;border-radius:var(--radius);font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border-2);background:transparent;color:var(--btn-fg);transition:background .2s ease,border-color .2s ease,color .2s ease}
.btn:hover{background:var(--surface);border-color:var(--fg-3);color:var(--fg)}
.btn-primary{background:var(--accent-fill);border-color:var(--accent-fill);color:var(--on-accent);font-weight:700}
.btn-primary:hover{background:var(--accent-fill-hi);border-color:var(--accent-fill-hi);color:var(--on-accent)}
.btn-primary:active{background:var(--accent-fill-lo);border-color:var(--accent-fill-lo)}
.btn .chip{border:1px solid var(--border-2);background:var(--inset);border-radius:999px;padding:4px 12px;font-size:12px;color:var(--fg);font-weight:600}
.actions{display:flex;gap:14px;flex-wrap:wrap;align-items:center;container-type:inline-size}
/* Le bouton « diagnostic » du corps de page colle au « devis » qui le precede (demande 2026) :
   meme boite que lui — 13 px de creux vertical, pas de hauteur imposee — donc la MEME hauteur
   rendue (mesure : 53 px contre 36 px avant, le « devis » etant plus haut que le diagnostic).
   Le mot du diagnostic est plus long que le sien : la paire la plus large du site mesure 524 px
   (« Devis gratuit a Bretteville-l'Orgueilleuse » + « Diagnostic de toiture »). Le seuil de
   soudure n'est donc pas une largeur d'ecran mais la largeur REELLE du conteneur, mesuree par une
   requete de conteneur sur .actions : 540 px, c'est-a-dire la paire la plus large + 16 px. En
   dessous, le diagnostic passerait sur la ligne du dessous en etant attire vers la gauche : il
   garde alors ses quatre angles et son jeu de 14 px, et rien ne se chevauche. */
.actions > .diag-cta{height:auto;padding:13px 15px}
@container (min-width:540px){
  /* Soudure : le jeu de 14 px se retire sur la seule couture devis/diagnostic (marge negative,
     le `gap` du conteneur ne change pas, le troisieme bouton garde son jeu), et les angles se
     repartissent comme dans l'en-tete : pleins vers l'exterieur, droits sur la couture.
     Les DEUX moities de la couture sont declarees DANS la requete : la mesure (tools/_radius3.cjs,
     les 50 pages, 4 largeurs) montre que la paire la plus large du site fait 524 px mais que les
     plus etroites font 392 px. Entre la largeur ou la paire tient en une ligne (pair + 14) et le
     seuil de soudure de 540 px, les boutons sont EN LIGNE SANS ETRE COLLES : un angle droit herite
     hors soudure y laissait au « devis » un flanc carre flottant a 14 px du diagnostic. Meme chose
     quand le diagnostic passe a la ligne : le devis gardait ses deux angles droits de couture. */
  .actions > .btn-primary:has(+ .diag-cta){border-radius:var(--radius) 0 0 var(--radius)}
  .actions > .diag-cta{margin-left:-14px;border-radius:0 var(--radius) var(--radius) 0}
}

/* ── fil d'Ariane ── */
.crumbs{padding:16px var(--pad);border-bottom:1px solid var(--border);font-family:var(--font-mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--fg-3)}
.crumbs a{color:var(--muted);text-decoration:none}
.crumbs a:hover{color:var(--fg)}
.crumbs span{margin:0 8px;color:var(--border-2)}

/* ── hero métier ── */
.svc{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);gap:clamp(28px,4vw,60px);align-items:center;padding:72px var(--pad);border-bottom:1px solid var(--border)}
.svc h1{font-size:clamp(32px,3.6vw,46px);font-weight:900;line-height:1.06;letter-spacing:-.03em;margin-bottom:20px}
/* 4,7vw et non 5vw : la ligne « Couvreur à Caen, » mesure 8,8 × la taille de police en Montserrat 900.
   À 5vw elle dépassait la colonne (546 px pour 538 px à 1280) et repartait sur 4 lignes une fois la
   police chargée : c'était ça, le saut de mise en page. Le plafond 60px garde de la marge. */
.svc.home h1{font-size:clamp(38px,4.7vw,60px);line-height:1.02}
.svc .lead{margin-bottom:30px}
.facts-row{display:flex;gap:26px;flex-wrap:wrap;margin-top:26px;padding-top:22px;border-top:1px solid var(--border)}
.facts-row div{font-size:13px;color:var(--muted)}
.facts-row b{display:block;font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--fg-3);font-weight:500;margin-bottom:4px}
.photo-frame{position:relative;margin:0;border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden}
.photo-frame img{width:100%;height:auto;transition:transform .6s ease}
.photo-frame:hover img{transform:scale(1.04)}
.photo-frame figcaption{position:absolute;left:12px;right:12px;bottom:12px;background:rgba(var(--band-rgb),.84);backdrop-filter:blur(6px);border:1px solid var(--border);border-radius:var(--radius);padding:9px 12px;font-family:var(--font-mono);font-size:11px;color:var(--muted);letter-spacing:.04em}
.photo-frame.tall{background:var(--inset)}
.photo-frame.tall img{width:auto;max-width:100%;max-height:min(64vh,540px);margin-inline:auto}

/* ── blocs texte + visuel ── */
.split{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:clamp(28px,4vw,60px);align-items:flex-start}
.split + .split{margin-top:64px}
.split h2{font-size:clamp(22px,2.4vw,28px);font-weight:800;letter-spacing:-.02em;line-height:1.15;margin-bottom:16px}
.split p{font-size:14px;color:var(--muted);line-height:1.85;margin-bottom:14px}
.split figure{margin:0;border-radius:var(--radius-lg);overflow:hidden}
.split figure.tall{background:var(--inset)}
.split figure.tall img{width:auto;max-width:100%;max-height:min(58vh,480px);margin-inline:auto}
.split img{width:100%;height:auto}

/* ── image collante ──
   Une photo ne peut suivre le défilement que s'il reste de la place sous elle dans son
   conteneur : en grille, sa case est aussi haute que la colonne de texte voisine, et l'image
   reste bloquée en dessous une fois ce texte lu. Le collant n'est donc déclaré qu'à partir de
   921 px, là où la grille est sur deux colonnes (en colonne unique il n'y a plus de colonne
   voisine à accompagner). Le script mesure la place disponible et pose .a2t-still — retour en
   statique — quand il n'y en a pas : une image plus haute que sa rangée ne bouge pas. Le cadre
   est déjà en overflow:hidden, la photo ne déborde jamais et ne passe pas sur l'en-tête. Le
   décalage vient du collant natif : aucun transform, donc aucun décalage de mise en page
   (CLS = 0) et aucune concurrence avec le survol qui zoome.

   L'attache est réglée image par image (--pin posé par le script) pour que la photo se cale au
   milieu de l'écran pendant tout son trajet, et non sous l'en-tête. Le collant natif ne peut
   que pousser l'image vers le bas, jamais la faire monter : une photo déjà plus basse que le
   milieu de l'écran y est ramenée, une photo plus haute que l'écran retombe sur la garde basse
   (juste sous l'en-tête), c'est le mieux possible sans la rogner.

   Reste donc a la poser assez haut dans son flux : le collant ne fait que la retenir, jamais la
   remonter. En mode centre, la photo d'un hero (514 px) etait rejetee a 336 px du bord, son bas
   a 850 px : elle sortait de l'ecran a l'arrivee sur la page. En tete de page, la colonne image
   s'aligne en haut de sa rangee et l'attache calculee (--pin) la descend seule au milieu de
   l'ecran des que le defilement commence — centrage et collant intacts. Sa taille est deja
   bornee a 64vh, elle tient donc entierement a l'ecran des l'arrivee. */
@media (min-width:921px){
  header.svc{align-items:start}
  .photo-frame,.split figure{position:sticky;top:var(--pin,var(--stick-top,112px))}
  .photo-frame.a2t-still,.split figure.a2t-still{position:static}
}
ul{list-style:none;margin:0;padding:0}
.check li{display:flex;gap:11px;align-items:flex-start;margin-bottom:11px;font-size:13.5px;color:var(--muted);line-height:1.7}
.check li::before{content:"";flex:0 0 auto;width:9px;height:9px;margin-top:6px;border-right:1.5px solid var(--fg-3);border-bottom:1.5px solid var(--fg-3);transform:rotate(45deg)}
.check li strong{color:var(--fg-hi);font-weight:600}

/* ── cartes prestations ── */
.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin-top:40px}
.pcard{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:26px;transition:border-color .2s ease}
.pcard:hover{border-color:var(--border-2)}
.icon-mark{width:44px;height:44px;display:grid;place-items:center;border:1px solid var(--border-2);border-radius:var(--radius);color:var(--fg);margin-bottom:16px}
.pcard h3{font-family:var(--font-body);font-size:15px;font-weight:600;margin-bottom:8px}
.pcard p{font-size:13.5px;color:var(--muted);line-height:1.75}

/* ── tableau matériaux ── */
.spec{width:100%;border-collapse:collapse;font-size:14px;margin-top:36px}
.spec th,.spec td{padding:14px 16px;text-align:left;border-bottom:1px solid var(--border);vertical-align:top}
.spec th{font-family:var(--font-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);font-weight:500}
.spec tbody tr:hover{background:var(--fg-soft)}
.spec td:first-child{font-weight:600;color:var(--fg-hi)}
.spec td{color:var(--muted)}

/* ── encart inset ── */
.inset-card{background:var(--inset);border:1px solid var(--border);border-radius:var(--radius-lg);padding:26px;position: sticky;top:25vh}
.inset-card.center{text-align:center}
.inset-card .check{text-align:left}
.name{font-family:var(--font-display);font-size:17px;font-weight:800}
.prose p{font-size:14px;color:var(--muted);margin-bottom:14px}
.foot p + p{margin-top:14px}
.spec caption{text-align:left;color:var(--fg-3);font-size:13px;padding-bottom:10px}
/* Le cartouche artisan est dans .split : la regle .split img{width:100%} etirait le
   portrait a la largeur de la colonne (326 px mesure). Deux classes suffisent a la battre,
   la taille dessinee de 96 px reprend sa valeur. */
.split .portrait,.portrait{width:96px;height:96px;border-radius:50%;object-fit:cover;border:3px solid var(--accent);margin:0 auto 16px}
.inset-card h3{font-family:var(--font-body);font-size:14px;font-weight:600;margin-bottom:14px}
/* Coche du cartouche artisan : un chevron symetrique de 9 px en gris se lisait comme une
   fleche. La coche reelle a un bras court et un bras long (7 x 12 px, trait 2 px), en
   couleur primaire. L'animation a2t-check (rotation de 45 degre) est conservee. */
.inset-card .check li::before{width:7px;height:12px;margin-top:5px;border-right:2px solid var(--accent);border-bottom:2px solid var(--accent)}

/* ── FAQ ── */
.faq{margin-top:32px;border-top:1px solid var(--border)}
.faq details{border-bottom:1px solid var(--border)}
.faq summary{display:flex;justify-content:space-between;gap:20px;align-items:center;padding:20px 0;font-size:15px;font-weight:600;cursor:pointer;list-style:none}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"+";font-family:var(--font-mono);font-size:18px;color:var(--muted);flex:0 0 auto}
.faq details[open] summary::after{content:"–"}
.faq summary:hover{color:var(--fg)}
.faq summary:hover::after{color:var(--fg)}
.faq details[open] summary{color:var(--fg)}
.faq details[open] summary::after{color:var(--fg)}
.faq p{font-size:14px;color:var(--muted);line-height:1.85;padding-bottom:20px;max-width:70ch}

/* ── zones ── */
.zones{display:flex;flex-wrap:wrap;gap:10px;margin-top:32px}
.zones a,.zones span{display:inline-flex;align-items:center;padding:8px 15px;border:1px solid var(--border-2);border-radius:999px;font-size:13px;color:var(--muted);text-decoration:none;transition:color .16s ease,border-color .16s ease}
.zones a:hover{color:var(--fg);border-color:var(--fg)}

/* ── étapes ──
   L'escalier est volontairement ample (96 px par rang) : il donne de l'air vertical et,
   surtout, il décale les hauts de colonne. Chaque étape étant observée pour elle-même,
   la suite se déroule à l'écran au rythme du défilement plutôt que d'un bloc. */
.steps{--stn:320px;--dw:calc(var(--stn) + 24px);--nb:4;--f:4;display:grid;grid-template-columns:repeat(4,1fr);margin:2rem 0 1rem;row-gap:80px}
.steps.five{--nb:5;grid-template-columns:repeat(5,1fr)}
.steps.five .step:nth-child(5){--st:calc(var(--stn) * 4)}
/* Le retrait en escalier passe par une variable (--st) plutot que par padding-top : la
   hauteur de defilement de l'etape se calcule alors dessus (voir .steps.act) sans repeter
   la valeur, et l'annulation en mobile tient en une ligne. */
/* align-self:start : sans ca, la grille etire chaque colonne a la hauteur de la plus grande
   (boite = hauteur max du conteneur). Chaque etape reprend sa HAUTEUR NATURELLE, et le rail
   de chacune mesure exactement cette hauteur. */
.step{--st:0px;position:relative;align-self:start;padding:0 26px;padding-top:var(--st);border-left:1px solid var(--border)}
.step:first-child{padding-left:0;border-left:0}
.step:nth-child(2){--st:var(--stn)}
.step:nth-child(3){--st:calc(var(--stn) * 2)}
.step:nth-child(4){--st:calc(var(--stn) * 3)}
.step b{display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:50%;margin-bottom:22px;font-family:var(--font-display);font-size:15px;font-weight:900;color:var(--muted);border:2px solid var(--border-2)}
.step:first-child b{background:var(--accent-fill);border-color:var(--accent-fill);color:var(--on-accent)}
.step h3{font-family:var(--font-body);font-size:16px;letter-spacing:.1em;text-transform:uppercase;font-weight:700;color:var(--muted);margin:0 0 12px}
.step p{font-size:15px;color:var(--muted);line-height:1.85}

/* ── maillage interne ── */
.xlinks{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin-top:36px}
.xlink{display:block;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:24px;text-decoration:none;transition:border-color .2s ease,transform .2s ease}
.xlink:hover{border-color:var(--border-2);transform:translateY(-2px)}
.xlink h3{font-family:var(--font-body);font-size:15px;font-weight:600;margin-bottom:8px}
.xlink p{font-size:13px;color:var(--muted);line-height:1.7;margin-bottom:14px}
/* flèche des liens croisés : dessinée en CSS et allongée au survol, plutôt que
   « Voir → » répété sur chaque carte du site */
.xlink .arr{display:block;width:26px;height:1px;background:var(--fg-3);position:relative;transition:width .2s ease,background-color .2s ease}
.xlink .arr::after{content:"";position:absolute;right:-1px;top:-4px;width:8px;height:8px;border-top:1.5px solid var(--fg-3);border-right:1.5px solid var(--fg-3);transform:rotate(45deg);transition:border-color .2s ease}
.xlink:hover .arr{width:38px;background:var(--fg)}
.xlink:hover .arr::after{border-color:var(--fg)}

/* ── CTA ── */
.cta{text-align:center}
.cta h2{font-size:clamp(28px,3.4vw,38px);font-weight:900;letter-spacing:-.02em;line-height:1.1;margin-bottom:14px}
.cta p{font-size:14px;color:var(--fg-3);margin-bottom:34px}
.cta .actions{justify-content:center}

/* ── pied de page ── */
.foot{padding:56px var(--pad) 32px;border-top:1px solid var(--border);background:var(--band)}
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:36px}
.foot h3{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--fg-3);margin:0 0 14px;font-weight:500}
.foot nav{display:flex;flex-direction:column;gap:9px}
.foot nav a{font-size:13px;color:var(--muted);text-decoration:none}
.foot nav a:hover{color:var(--fg)}
.foot p{font-size:13px;color:var(--muted);line-height:1.8}
.legal{margin-top:40px;padding-top:22px;border-top:1px solid var(--border);font-size:12px;color:var(--fg-3);line-height:1.8}
.legal a{color:var(--muted)}

/* ── sommaire (ancres internes) ── */
.toc{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2px 28px;margin-top:30px;max-width:760px}
.toc a{display:flex;gap:12px;align-items:baseline;padding:11px 0;border-top:1px solid var(--border);font-size:14px;color:var(--muted);text-decoration:none}
.toc a:hover{color:var(--fg)}
.toc a i{font-family:var(--font-mono);font-size:11px;color:var(--fg-3);font-style:normal;flex:0 0 auto;letter-spacing:.08em}
.toc a[aria-current="section"]{color:var(--fg);border-top-color:var(--fg-3)}
.toc a[aria-current="section"] i{color:var(--muted)}

/* ── encart note : filet supérieur + libellé mono (pas de barre colorée à gauche) ── */
.note{background:var(--inset);border:1px solid var(--border);border-top:2px solid var(--border-2);border-radius:var(--radius-lg);padding:24px 26px;margin-top:34px}
.note h3{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--fg-3);font-weight:500;margin:0 0 10px}
.note p{font-size:14px;color:var(--muted);line-height:1.8}
.note p strong{color:var(--fg-hi);font-weight:600}

/* ── barre de progression de lecture (décorative, posée par le script) ── */
.readbar{position:fixed;top:0;left:0;right:0;height:2px;z-index:30;pointer-events:none;transform:scaleX(0);transform-origin:0 50%;background:var(--accent)}

/* ── section visée par une ancre : rappel bref du point d'entrée ── */
section{transition:background-color .6s ease}
section:target{background:var(--fg-soft)}

/* ── animations ──
   Le masque n'est posé que par le script partagé (classe .watch) : sans JavaScript, avec
   un crawler qui n'exécute pas le script, ou si le script échoue, tout reste affiché et
   indexable. L'apparition passe par une animation (pas une transition) : les delays ne
   débordent pas sur les survols et l'état final ne fige pas les micro-interactions.
   Seuls opacity et transform sont animés : aucun décalage de mise en page (CLS = 0) et
   le hero, qui porte l'image LCP, n'est jamais masqué. */
/* La cascade est générique : le script marque d'un seul nom (.a2t-item) tout ce qui doit
   entrer, et pose le décalage dans la variable --d. Un conteneur n'est jamais masqué en
   même temps que ses enfants : le script ne marque soit l'un, soit les autres. Ainsi la
   règle ne dépend plus d'une liste de classes et couvre les colonnes du pied de page,
   les lignes de tableau et les coches sans toucher au HTML des 15 pages. */
.a2t-reveal.watch:not(.in) .a2t-item{opacity:0;transform:translateY(16px)}
.a2t-reveal.in .a2t-item{animation:a2t-in .55s cubic-bezier(.22,.7,.2,1) backwards;animation-delay:var(--d,0ms)}
@keyframes a2t-in{from{opacity:0;transform:translateY(16px)}}

/* une entrée par nature d'élément : même coût, lecture différente selon le rôle */
.a2t-reveal.in .a2t-item.eyebrow{animation-name:a2t-side}
@keyframes a2t-side{from{opacity:0;transform:translateX(-12px)}}
.a2t-reveal.in .faq > .a2t-item,
.a2t-reveal.in .spec .a2t-item{animation-name:a2t-fade}
@keyframes a2t-fade{from{opacity:0}}
.a2t-reveal.in .zones > .a2t-item{animation-name:a2t-chip}
@keyframes a2t-chip{from{opacity:0;transform:translateY(8px) scale(.94)}}
/* image de section : le cadre entre en fondu, la photo se pose (recul 7 % → 1).
   Le cadre est déjà en overflow:hidden, la photo ne déborde jamais. Le hero, qui porte
   l'image LCP, n'est pas une section révélée : son image n'est jamais masquée. */
.a2t-reveal.in figure.a2t-item,
.a2t-reveal.in .photo-frame.a2t-item{animation-name:a2t-fade}
.a2t-reveal.in figure.a2t-item img,
.a2t-reveal.in .photo-frame.a2t-item img{animation:a2t-settle 1.15s cubic-bezier(.22,.7,.2,1) backwards;animation-delay:calc(var(--d,0ms) + 80ms)}
@keyframes a2t-settle{from{transform:scale(1.07)}}

/* Pastille d'étape : se pose quand SON étape entre dans le cadre, pas à la révélation de
   la section. transform uniquement → aucun décalage de mise en page. */
.a2t-step.in:not(.sub) b{animation:a2t-pop .5s cubic-bezier(.22,.7,.2,1) backwards;animation-delay:.2s}
@keyframes a2t-pop{from{transform:translateY(calc(var(--pyy,0px) * var(--pk,1))) scale(.72);opacity:0}}

/* ── étapes : défilement vertical + parallaxe ──
   Deux temps, et les deux ne sortent que du script (classe .watch) : sans JavaScript, avec
   un crawler qui n'exécute pas le script, ou si le script échoue, les quatre colonnes sont
   affichées et indexables.
   1. Révélation : chaque colonne est observée pour elle-même et se redresse au défilement
      qui l'amène à l'écran. L'escalier (96 px par rang) étale les quatre passages.
   2. Parallaxe : une fois en place, chaque colonne descend à sa propre vitesse (--py posé
      par le script, l'amplitude croît avec le rang : 6 px pour la 1ʳᵉ, 24 px pour la 4ᵉ).
      Les colonnes de droite traînent derrière celles de gauche : la suite se creuse sous le
      pouce puis se referme à la sortie de la section. La course est centrée (±amplitude),
      donc la position finale est exactement celle de la mise en page statique.
   Le keyframe de révélation démarre à 34 px + le décalage du moment et se termine sur cette
   position : les deux effets se composent au lieu de se marcher dessus, sans saut à la fin
   de l'entrée.
   Seuls opacity et transform sont animés : la hauteur de la grille ne bouge jamais, donc
   aucun décalage de mise en page, et l'image LCP du hero n'est pas concernée. */
.a2t-step{transform:translateY(var(--py,0px))}
.a2t-reveal.watch .a2t-step:not(.in){opacity:0;transform:translateY(calc(34px + var(--py,0px)))}
.a2t-step.in{animation:a2t-step .75s cubic-bezier(.22,.7,.2,1) backwards}
@keyframes a2t-step{from{opacity:0;transform:translateY(calc(34px + var(--py,0px)))}}
/* Dans une etape coupee en couches, la colonne ne fait que glisser : le fondu est porte par
   chaque couche, sinon la colonne et ses couches fondraient en meme temps et le decalage ne
   se lirait plus. */
.a2t-step.sub.in{animation-name:a2t-step-move}
@keyframes a2t-step-move{from{transform:translateY(calc(34px + var(--py,0px)))}}
/* Dans chaque étape, les trois éléments entrent à leur tour : la pastille, puis le titre,
   puis le texte. Le décalage part de l'entrée de SA colonne, pas de celle de la section.
   Le masque parent (opacity:0) couvre déjà les enfants : pas besoin de règle :not(.in). */
.a2t-step.in:not(.sub) h3{animation:a2t-in-py .5s cubic-bezier(.22,.7,.2,1) backwards;animation-delay:.3s}
.a2t-step.in:not(.sub) p{animation:a2t-in-py .55s cubic-bezier(.22,.7,.2,1) backwards;animation-delay:.42s}
@keyframes a2t-in-py{from{opacity:0;transform:translateY(calc(14px + var(--pyy,0px) * var(--pk,1)))}}

/* ── sous-couches d'une étape : le parallaxe descend jusqu'au détail ──
   Chaque étape est coupée en trois couches observées séparément (pastille, titre, texte) et
   chaque couche a sa PROPRE VITESSE de défilement : le coefficient --pk (1,6 pour la pastille,
   1,2 pour le titre, 1 pour le texte) multiplie le décalage --pyy posé par le script. À
   l'intérieur d'une colonne, l'écart entre la pastille et son paragraphe passe donc de 0 à
   ~10 px pendant la traversée de l'écran, puis revient à 0 à la sortie.
   Les deux courses sont centrées : au repos, la position est exactement celle de la mise en
   page statique → aucune mise en page animée, aucun décalage de défilement.
   Les couches ne sont jamais masquées si le script ne les observe pas (classe .sub posée par
   lui) : sans JavaScript, avec un crawler qui n'exécute pas le script, ou en mouvement réduit,
   le paragraphe reste affiché et indexable dans sa colonne.
   Le keyframe de chaque couche repart de 26 px + son décalage du moment : révélation et
   parallaxe se composent au lieu de se marcher dessus. transform et opacity uniquement. */
.a2t-step b{--pk:1.6}
.a2t-step h3{--pk:1.2}
.a2t-step p{--pk:1}
.a2t-step b,.a2t-step h3,.a2t-step p{transform:translateY(calc(var(--pyy,0px) * var(--pk,1)))}
.a2t-reveal.watch .a2t-step.sub > .a2t-layer:not(.in){opacity:0;transform:translateY(calc(26px + var(--pyy,0px) * var(--pk,1)))}
.a2t-step.sub > .a2t-layer.in{animation:a2t-layer .62s cubic-bezier(.22,.7,.2,1) backwards}
.a2t-step.sub > h3.in{animation-delay:.09s}
.a2t-step.sub > p.in{animation-delay:.18s}
@keyframes a2t-layer{from{opacity:0;transform:translateY(calc(26px + var(--pyy,0px) * var(--pk,1)))}}

/* ── section « Signes d'alerte » : pastille en couleur principale + escalier accentue ──
   La pastille de CHAQUE etape prend la couleur principale, pas seulement celle de la 1re. Elle
   se pose a l'entree de SON etape (l'observateur dedie pose .in couche par couche) : l'ambre
   apparait donc progressivement au fil du defilement, etape par etape.
   L'escalier passe de 96 px a 150 px par rang (marges 112/56 px au lieu de 96/40) : les hauts
   de colonne sont plus decalés, les quatre passages s'étalent davantage. L'amplitude de
   parallaxe (--amp, lue par le script) suit le meme rang : 10 px pour la 1re colonne, 22 px
   pour la 4e, et les sous-couches suivent la meme amplitude (la feuille les multiplie par le
   coefficient de vitesse --pk propre a la section : 1,8 pastille / 1,35 titre / 1,1 texte).
   Garde geometrique : l'ecart RELATIF entre deux colonnes voisines vaut
   (amp2 - amp1) x pk = 4 x 1,8 = 7,2 px, et a l'interieur d'une colonne la fermeture maximale
   vaut (22 x 1,8 - 22 x 1,1) = 15,4 px, contre 22 px + 12 px de marges reelles entre pastille,
   titre et texte. Rien ne se recouvre, en desktop comme en mobile (colonnes empilees, ecart
   porte a 104 px).
   Le separateur vertical est retire ici : l'escalier de 450 px laisserait un filet de 450 px
   sans colonne a separer.
   Perimetre : la section marquee (#symptomes de zinguerie.html) et son seul homologue reel
   (#symptomes d'etancheite.html, meme section, meme libelle de sommaire). Les 12 autres pages
   ont un bloc .steps d'une autre nature (#deroule, deroule de chantier) : il garde le regime
   general, une seule pastille ambre et l'escalier de 96 px. */
#symptomes .steps{--stn:300px;margin:112px 0 56px}
/* Les regles de couleur et d'amplitude de cette section ont ete retirees : le regime
   general .steps.act (ligne par etape, numero ambre pour l'etape en cours, amplitude
   par rang) les couvre desormais a l'identique. Il ne reste ici que son escalier plus
   ample et ses coefficients de vitesse propres. */
#symptomes .steps .step b{--pk:1.8}
#symptomes .steps .step h3{--pk:1.35}
#symptomes .steps .step p{--pk:1.1}
#symptomes .steps::before{display:none}

/* Parallaxe annule pour TOUS les blocs .steps : le script lit l'amplitude dans --amp. Elle est
   a zero parce que chaque etape est DESORMAIS EPINGLEE au milieu du cadre pendant sa fenetre
   (voir le bloc @media (min-width:921px) plus bas) : le script designe l'etape en cours par la
   geometrie de mise en page (offsetTop / offsetHeight / padding-top) et ne voit pas le
   transform. Une colonne poussee de 76 px serait donc epinglee 76 px a cote du milieu de
   l'ecran tout en restant « l'etape en cours ». Le glissement d'entree des colonnes et des
   sous-couches est conserve (34 px -> 0) : il finit a la position de mise en page, donc le
   collage tombe juste. */
.steps .step{--amp:0px}

/* Centrage vertical du bloc : la rangee de colonnes reste a equidistance du haut et du bas
   du cadre pendant qu'elle traverse l'ecran. Declare ici et non sous .act : c'est une
   regle de mise en page, pas un effet de script — la hauteur de page ne change donc pas
   quand le script se charge (aucun saut de mise en page). min-height ne fait que pousser la
   rangee : le contenu depasse deja ce minimum sur la plupart des pages. */
.steps{align-content:center;min-height:calc(100vh - var(--nav-h,96px) - 152px);min-height:calc(100svh - var(--nav-h,96px) - 152px)}

/* ── toutes les sections .steps : etape « desactivee » puis activee au defilement ──
   Regime general, applique aux 18 blocs .steps du site. Il ne vit que sous la classe
   .act, posee par assets/a2t.js : sans le script (crawler, script bloque), sans
   IntersectionObserver et en mouvement reduit, la classe n'existe pas, les etapes sont a
   opacite pleine et la 1re pastille reste ambre comme avant. Rien n'est masque.

   1. En retrait / en cours : chaque colonne part a --dim (.5) et revient a son opacite
      initiale quand elle est EN COURS (le script pose .cur sur l'etape dont le centre est le
      plus proche du milieu de l'ecran). Les autres restent en retrait. Le masque d'entree est
      abaisse a --dim au lieu de 0 sous ce regime : l'etape reste visible, elle n'est pas
      effacee. Le passage se fait en transition d'opacite. Le critere etant la POSITION et non
      l'ordre de passage, l'effet est reversible : en remontant, l'etape du dessus reprend
      l'ambre et celle du dessous repart en retrait.
   2. La pastille du numero prend la couleur principale pour l'etape en cours seulement (fond
      ambre, chiffre nuit), la meme que porte la 1re etape en statique. Regle plus specifique
      que .step:first-child b, donc l'ambre n'est plus reserve a la premiere colonne.
   3. Rail vertical : un trait ambre PAR etape, de la hauteur naturelle de cette etape, qui se
      trace vers le bas pendant que l'etape traverse l'ecran (progression ecrite dans --rp,
      lue en clip-path). Pseudo-element en position absolute hors du flux,
      pointer-events:none : il ne peut rien deplacer ni rien recouvrir (il tient dans la
      gouttiere gauche de la colonne, a 18 px du texte, pour un retrait de 20 a 120 px).
   4. Les sous-couches (pastille, titre, texte) gardent leur entree decalée mais ne fondent
      plus : c'est la colonne qui porte l'opacite, sinon le produit des deux rendrait
      l'etape invisible a l'etat desactive au lieu de la montrer en retrait.
   Contraste de l'etat desactive : le paragraphe (--muted) descend a 2,8:1 a .5 ; c'est un
   etat transitoire qui se resout a l'activation, jamais l'etat repose. Le reglage tient
   dans un seul nombre (--dim). */
.steps.act{--dim:.5}
.steps.act .a2t-step{opacity:var(--dim);transition:opacity .55s cubic-bezier(.22,.7,.2,1)}
.a2t-reveal.watch .steps.act .a2t-step:not(.in){opacity:var(--dim);transform:translateY(calc(34px + var(--py,0px)))}
.steps.act .a2t-step.cur{opacity:1}
/* Sous ce regime, l'entree de la colonne ne joue que le glissement : l'opacite est portee
   par la transition --dim → 1 de la regle .cur. Sans cette regle, l'animation d'entree
   (plus prioritaire qu'une regle) ferait fondre la colonne de 0 a 1 et l'etat « en retrait »
   ne se lirait pas. */
.steps.act .a2t-step.in{animation-name:a2t-step-move}
/* Idem pour les elements d'une etape non coupee en couches : leur entree garde son decalage
   (pastille, titre, texte) mais ne fond plus, pour ne pas multiplier l'opacite de la colonne. */
.steps.act .a2t-step.in:not(.sub) b{animation-name:a2t-pop-move}
.steps.act .a2t-step.in:not(.sub) h3,.steps.act .a2t-step.in:not(.sub) p{animation-name:a2t-in-move}
@keyframes a2t-pop-move{from{transform:translateY(calc(var(--pyy,0px) * var(--pk,1))) scale(.72)}}
@keyframes a2t-in-move{from{transform:translateY(calc(14px + var(--pyy,0px) * var(--pk,1)))}}
.steps.act .a2t-step b{background:transparent;border-color:var(--border-2);color:var(--muted);transition:background-color .5s cubic-bezier(.22,.7,.2,1),border-color .5s cubic-bezier(.22,.7,.2,1),color .5s cubic-bezier(.22,.7,.2,1)}
/* Le numero ne prend la couleur principale que pour L'ETAPE EN COURS (.cur, pose par le
   script sur l'etape dont le centre est le plus proche du milieu de l'ecran) : les autres
   restent grises et en retrait. Une seule pastille
   ambre a la fois dans le bloc, l'oeil sait ou on en est de la suite.
   Sans le script, la regle de base .step:first-child b garde la 1re pastille ambre. */
.steps.act .a2t-step.cur b{background:var(--accent-fill);border-color:var(--accent-fill);color:var(--on-accent)}
/* Rail vertical : UN trait ambre PAR etape, decale de 26 px a gauche du contenu de SON etape,
   de la HAUTEUR NATURELLE DU BLOC (la colonne est en align-self:start : sa boite n'est pas
   etiree a la hauteur du conteneur, donc le rail mesure exactement le bloc, retrait d'escalier
   exclu). Il se trace vers le bas UNIQUEMENT pendant que le numero de cette etape est allume
   (.cur) : la fenetre de progression du script est bornee aux points de passage du milieu
   d'ecran entre etapes voisines, donc le trait part avec l'ambre et finit quand l'ambre passe
   a l'etape suivante. La feuille ne fait que lire --rp (0 a 100 %) pose par le script, et le
   deplacage passe par clip-path (aucune mise en page animee, aucune mesure de hauteur, donc
   rien qui tremble sous le parallaxe). pointer-events:none.
   Le degagement de 27 px se lit sur les quatre etapes : les colonnes 2 a 4 ont un filet de
   1 px + un padding gauche de 26 px, donc le rail pose sur leur filet (-1 px, epaisseur 2 px)
   tombe a 28 px de leur contenu ; la 1re colonne n'a ni filet ni padding gauche, son rail est
   pousse de 27 px hors du bloc, dans le retrait de section (>= 73 px a 921 px de large). Vers
   la gauche du texte precedent, il reste au moins 24 px de gouttiere : rien n'est recouvre. */
.steps.act .step::after{content:"";position:absolute;left:-1px;top:var(--st);height:calc(100% - var(--st));width:2px;border-radius:2px;background:var(--accent);pointer-events:none;clip-path:inset(0 0 calc(100% - var(--rp,0%)) 0)}
.steps.act .step:first-child::after{left:-27px}

/* ── Etape EN COURS = etape EPINGLEE au milieu du cadre, pour TOUS les blocs .steps ──
   Meme contrat que le bloc « deroule » de l'accueil : pendant toute sa fenetre d'ambre, le
   bloc de l'etape en cours (numero + titre + texte) reste FIGE sur la ligne du milieu de
   l'ecran, et les colonnes voisines restent repliees sur leur numero jusqu'a leur passage.
   Ce qui bouge, c'est le reste de la page ; ce qui est lu ne defile plus.

   TROIS NOMBRES LIES, regles une fois ici et partages par les 18 blocs du site :
   - --stn : retrait d'escalier PAR RANG (le degagement AU-DESSUS du contenu). C'est la course
     de defilement d'une etape : le script designe l'etape dont le CENTRE de boite de mise en
     page est le plus proche du milieu d'ecran, donc une etape garde l'ambre pendant l'ecart
     qui separe deux centres voisins, c'est-a-dire --stn.
   - --dw : reserve LIBRE EN DESSOUS du contenu (--stn + 24, les 24 px de marge negative du
     masque). C'est la course du collage : un element collant ne glisse que dans la boite de
     CONTENU de son parent (mesure : 344 px demands en padding-bottom, 46 px obtenus), donc la
     reserve est un bloc VIDE EN FLUX sous l'etape, pas une marge interne.
   - --ch : hauteur du bloc colle, ecrite par le script (colonne par colonne, mesuree au
     redimensionnement) : le point de collage est 50vh - --ch / 2, c'est ce qui pose le bloc
     exactement au milieu. 212 px = valeur de repli sans script.
   Course de l'etape = course de l'epingle : le texte est fige pendant TOUTE sa fenetre. C'est
   le prix de ce contrat, et c'est la raison pour laquelle la reserve compte dans la hauteur de
   page (~344 px de vide par etape, aucun paragraphe etire, aucun interligne gagne).
   Un seul element collant : deux enfants collants ne restent pas solidaires (la course d'un
   collant vaut hauteur du parent - sa hauteur), donc une pastille de 40 px et un masque de
   174 px se libereraient a ~130 px d'ecart et le numero partirait avant son paragraphe.

   Perimetre : desktop uniquement (>= 921 px) et sous @supports (width:100cqi) — la largeur
   ouverte est exprimee en unite de conteneur. Sans script (.act absent), sans @supports, en
   mouvement reduit et sous 921 px : rien n'est replie, rien n'est elargi, tout reste affiche
   et indexable ; la reserve et le collage sont annules en mobile. */
@media (min-width:921px){
  /* Le degagement est partage : moitie AU-DESSUS (--st, designe l'etape), moitie EN DESSOUS
     (--dw, la maintient epinglee). Uniquement du degagement en haut laisserait le texte defiler
     pendant sa propre fenetre (mesure avant regle : 343 a 558 px de course pendant la fenetre).
     Reserve posee HORS de .act : posee sous .act, chaque page se rallongerait de ~344 px a
     l'apparition de la classe (mesure : pire saut de mise en page 0,0023 -> 0,0087).
     Le rail de la feuille est repris ici sur le bloc colle (regle .step-body::after) : pose sur
     la colonne, il suivrait la position de mise en page pendant que le contenu monte et se
     dessinerait a cote de son etape. Ce pseudo-element est donc recycle en reserve vide. */
  /* Les DEUX selecteurs sont necessaires : la reserve doit exister HORS de .act (sinon la page
     se rallongerait de 4 x --dw a l'apparition de la classe du script) ET battre la regle du rail
     (.steps.act .step::after, ligne 569, plus specifique) quand .act est pose. Meme selecteur,
     place APRES la regle du rail : a specificite egale, l'ordre de source tranche. */
  .steps .step::after,.steps.act .step::after{content:"";position:static;display:block;width:1px;height:var(--dw);background:none;border-radius:0;clip-path:none}
  @supports (width:100cqi){
    /* LA PART DE LA RANGEEE QUE PREND LA COLONNE EN COURS. Un seul nombre, --f (declare sur
       .steps) : il sert a la fois a flex-grow et a --slotw, qui doit valoir exactement la part
       de flex de la colonne (sinon le texte depasse son cadre, ou ne l'remplit pas).
       Une part vaut (100cqi - 238px) / (--nb - 1 + --f) ; --nb porte le nombre d'etapes du bloc.
       --f est monte de 2,2 a 3 puis a 4 (demandes 2026) : le titre et le paragraphe ont grandi,
       il leur fallait plus de place, et elle vient du CONTENEUR (flex) et non des blocs de texte.
       Mesure a 1280 px, --f = 4 : texte 418 px contre 359 a --f = 3 et 297 a 2,2 ; les colonnes
       repliees descendent a 158 px (cinq etapes) et 173 px (quatre), elles ne montrent que leur
       pastille de 40 px, donc rien n'y est ecrase ni coupe.
       flex-wrap:wrap ne sert qu'a garantir le centrage vertical de la rangeee dans la
       min-height declaree plus haut : align-content n'agit sur une rangee UNIQUE que si le
       conteneur est multi-rangees. Avec flex-basis:0 et min-width:0 les colonnes ne debordent
       jamais, donc elles ne reviennent jamais a la ligne. */
    .steps.act{display:flex;flex-wrap:wrap;align-content:center;container-type:inline-size;--slotw:calc((100cqi - 238px) * var(--f) / (var(--nb) - 1 + var(--f)))}
    /* meme duree et meme courbe que la largeur du masque : colonne et contenu s'ouvrent ensemble */
    .steps.act .step{flex:1 1 0;min-width:0;transition:flex-grow .7s cubic-bezier(.22,.7,.2,1)}
    .steps.act .step.cur{flex-grow:var(--f)}
    .steps.act .step-frame{width:0;overflow:hidden;padding:24px 0;margin:-24px 0;transition:width .7s cubic-bezier(.22,.7,.2,1)}
    .steps.act .step-slot{width:var(--slotw)}
    /* text-wrap:pretty : la mesure etant plus large que la colonne repliee, la derniere ligne
       ne doit pas se retrouver avec un seul mot. */
    .steps.act .step-slot p{text-wrap:pretty}
    .steps.act .step.cur .step-frame{width:calc(100% + 26px)}
    /* Garde de lisibilite : le bloc colle n'est jamais pousse sous l'en-tete collant (--nav-h,
       96 px declares, 74 px rendus) ; le collage bascule sur la garde la plus proche. --ch est
       plus petit que l'ecran a tous les points de rupture concernes, le second terme ne gagne
       que sur les fenetres tres basses. */
    .steps.act .step-body{position:sticky;top:max(calc(50vh - var(--ch,212px) / 2), calc(var(--nav-h,96px) + 8px))}
    /* le rail ambre, repris sur le bloc colle : a sa hauteur, 27 px a gauche du contenu (sur le
       filet de la colonne, comme la feuille le pose ailleurs). Sans transition : --rp est ecrit a
       chaque image par le script, le trait se trace donc au rythme du defilement. */
    .steps.act .step-body::after{content:"";position:absolute;left:-27px;top:0;width:2px;height:100%;border-radius:2px;background:var(--accent);pointer-events:none;clip-path:inset(0 0 calc(100% - var(--rp,0%)) 0)}
    /* Le parallaxe est annule (--amp:0px, regle plus haut) : il deplace la COLONNE par transform,
       une valeur que le critere du script ne mesure pas. Le glissement d'entree de la colonne est
       conserve (34 px -> 0) : il FINIT a la position de mise en page, donc le collage tombe juste.
       Le filet du haut (--sy) suit la pente des colonnes : il n'y a plus de pente a suivre. */
    .steps.act .step{--amp:0px}
    .steps.act::before{transform:none}
  }
}
@media (prefers-reduced-motion:reduce){
  /* rien a retirer ici : le script ne pose pas .act en mouvement reduit, donc ni la reserve
     (regle desktop .steps .step::after) ni le rail ne sont crees. */
}
.a2t-reveal.watch .steps.act .a2t-step.sub > .a2t-layer:not(.in){opacity:1}
.steps.act .a2t-step.sub > .a2t-layer.in{animation-name:a2t-layer-move}
@keyframes a2t-layer-move{from{transform:translateY(calc(26px + var(--pyy,0px) * var(--pk,1)))}}
@media (max-width:920px){
  /* en colonne unique le bloc depasse deja l'ecran : le centrage n'aurait rien a centrer. */
  .steps{min-height:0}
}

/* coche de liste : le trait se pose après son texte, même décalage hérité que le <li>.
   Le keyframe reprend la rotation de 45° du trait : sans elle, la coche se redresserait
   à la fin de l'animation. */
.a2t-reveal.in .check li::before{animation:a2t-check .4s cubic-bezier(.22,.7,.2,1) backwards;animation-delay:calc(var(--d,0ms) + 150ms)}
@keyframes a2t-check{from{transform:rotate(45deg) scale(.55);opacity:0}}
/* flèche des liens croisés : se découvre de la gauche vers la droite à l'entrée
   (clip-path : aucune mise en page animée, le chevron n'est pas déformé), puis reste
   réglable au survol. */
.a2t-reveal.in .xlink .arr{animation:a2t-arr .5s cubic-bezier(.22,.7,.2,1) backwards;animation-delay:calc(var(--d,0ms) + 160ms)}
@keyframes a2t-arr{from{clip-path:inset(0 100% 0 0)}}

/* Filet des étapes : se dessine de la gauche vers la droite quand la section entre, et suit
   la pente de la première colonne (--sy) pour rester solidaire de la suite au lieu de
   rester figé pendant qu'elle glisse.
   Le tracé replié passe par clip-path et non par transform : transform est réservé au
   parallaxe, sinon l'animation (plus prioritaire que la règle) effacerait le décalage.
   Le tracé replié n'existe que si le script a posé .watch : sans JS, le filet est là. */
.steps{position:relative}
.steps::before{content:"";position:absolute;left:0;right:0;top:0;height:1px;background:var(--border-2);transform:translateY(var(--sy,0px))}
.a2t-reveal.watch:not(.in) .steps::before{clip-path:inset(0 100% 0 0)}
.a2t-reveal.in .steps::before{animation:a2t-line .7s .1s cubic-bezier(.22,.7,.2,1) forwards}
@keyframes a2t-line{from{clip-path:inset(0 100% 0 0)}}

/* en-tête collant : le fond se densifie une fois la page défilée. Seule la couleur
   change — pas de padding, donc aucun saut de mise en page sous l'en-tête collant. */
.topnav{transition:background-color .25s ease,border-color .25s ease}
.topnav.compact{background:rgba(var(--band-rgb),.97);border-bottom-color:var(--border-2)}

/* carte prestation : léger décollement, filet et pictogramme réagissent ensemble */
.pcard{transition:border-color .2s ease,transform .2s ease}
.pcard:hover{border-color:var(--border-2);transform:translateY(-2px)}
.icon-mark{transition:border-color .2s ease}
.pcard:hover .icon-mark{border-color:var(--fg-3)}
.btn:active{transform:translateY(1px)}

/* FAQ : ouverture animée sans JavaScript d'animation. Le texte est déjà dans le HTML
   (donc indexé). Le duo height:0 / height:auto n'est actif que si interpolate-size est
   supporté : sinon la règle est ignorée et le contenu reste visible. */
@supports (interpolate-size:allow-keywords){
  .faq details{interpolate-size:allow-keywords}
  .faq details::details-content{height:0;overflow:clip;transition:height .3s ease,content-visibility .3s allow-discrete}
  .faq details[open]::details-content{height:auto}
}
.faq summary::after{transition:color .2s ease}

@media (max-width:920px){
  .svc,.split,.foot-grid{grid-template-columns:1fr}
  .split figure{order:-1}
  .grid-3,.xlinks{grid-template-columns:1fr}
  .steps,.steps.five{grid-template-columns:1fr;gap:80px}
  /* en colonne unique : plus d'epingle ni de reserve — la regle qui recycle le pseudo-element en
     reserve vide est dans @media (min-width:921px), donc ici le pseudo-element de la colonne
     reste le RAIL ambre de la feuille (le masquer ici supprimerait les rails en mobile).
     Le bloc depasse deja l'ecran : il n'y a rien a centrer. */
  .step,.step:first-child{padding:0;border-left:0}
  .step:nth-child(2),.step:nth-child(3),.step:nth-child(4),.steps.five .step:nth-child(5){--st:0px}
  /* en colonne unique il n'y a plus de filet a recouvrir : le rail se decalle a 16 px a
     gauche du contenu, dans le retrait de section (>= 25,6 px a 320 px de large). first-child
     est repris ici : sa regle desktop est plus specifique que celle du media. */
  .steps.act .step::after,.steps.act .step:first-child::after{left:-16px}
  .spec caption{padding-bottom:8px;display:block}
  .spec thead{display:none}
  .spec,.spec tbody,.spec tr,.spec td{display:block;width:100%}
  .spec tr{border-bottom:1px solid var(--border-2);padding:14px 0}
  .spec td{border:0;padding:4px 0}
  .spec td:first-child{font-family:var(--font-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--fg-3);font-weight:500}
  /* sommaire sur deux colonnes : en une seule il coûte ~300 px de défilement avant
     la photo du hero, et le hero mobile est déjà long */
  .toc{grid-template-columns:repeat(2,minmax(0,1fr));gap:0 18px}
  .toc a{padding:8px 0;gap:8px;font-size:13px}
  .toc a i{font-size:10px}
  .steps::before{display:none}
  /* escalier annule en colonne unique (il faut le re-annuler pour cette section : la rege
     #symptomes est plus specifique que .step:nth-child), mais l'ecart reste plus ample */
  #symptomes .steps{gap:104px}
  #symptomes .steps .step:nth-child(2),#symptomes .steps .step:nth-child(3),#symptomes .steps .step:nth-child(4),#symptomes .steps .step:nth-child(5){--st:0px}
}
/* Le nav d'onglets ne doit jamais etre collant sur deux lignes : 137 px retenus en haut
   d'un écran de 844 px, c'est 16 % de la hauteur utile.

   Remplace (demande 2026) le resserrement 901-1060 px et le retour a « position:static » sous
   900 px. Les deux ne servent plus : sous 961 px les cinq onglets ne sont plus dans la barre,
   donc la barre ne se replie plus et tient 74 px a toutes les largeurs (mesure : 74 px a 320 px
   comme a 1440 px, barre deployee 156 px). L'en-tete reste donc colle partout, y compris au doigt
   — il ne depasse plus 74 px que lorsque le volet est ouvert, et le volet est un etat voulu.

   Le volet ouvert est la seule largeur ou l'en-tete depasse sa hauteur declaree : les regles qui
   reglent la position des blocs colles sur la hauteur de l'en-tete sont donc reprisent a la
   valeur reelle. --nav-h est releve par le script sur la hauteur rendue de la barre ; ouvert,
   le volet ajoute la hauteur du nav. Le rail du bloc colle passe en non-colle, comme sous
   921 px : un rail de 502 px dans un ecran de 800 px, avec la barre deployee, ne se lit plus. */
/* Le volet est un calque sous le bandeau : la hauteur rendue de l'en-tete reste 74 px ouvert
   comme ferme, donc la reserve de 96 px que les ancres et le rail du bloc colle se donnent
   (--nav-h) reste juste a toutes les largeurs. C'est pour cela que la paire « en-tete statique
   sous 900 px + --nav-h:24 px » a disparu : elle corrigeait un en-tete de deux rangees, qui
   n'existe plus. L'en-tete est colle partout, y compris au doigt. */
@media (prefers-reduced-motion:reduce){
  .diag-cta,.diag-txt,.diag-tip{transition:none}
  .a2t-reveal.watch:not(.in) .a2t-item{opacity:1;transform:none}
  .a2t-reveal.in .a2t-item,
  .a2t-reveal.in figure.a2t-item img,
  .a2t-reveal.in .photo-frame.a2t-item img,
  .a2t-reveal.in .check li::before,
  .a2t-reveal.in .xlink .arr,
  .a2t-step.in,.a2t-step.in b,.a2t-step.in h3,.a2t-step.in p,.a2t-reveal.in .steps::before,
  .a2t-step.sub.in,.a2t-step.sub > .a2t-layer.in{animation:none}
  .a2t-reveal.watch .a2t-step:not(.in){opacity:1;transform:none}
  .a2t-reveal.watch .a2t-step.sub > .a2t-layer:not(.in){opacity:1}
  .a2t-step,.a2t-step b,.a2t-step h3,.a2t-step p,.steps::before{transform:none}
  #symptomes .steps .step b{transition:none}
  .readbar{display:none}
  .topnav{transition:none}
  .pcard:hover,.xlink:hover,.btn:active{transform:none}
  .theme-btn:active{transform:none}
  .xlink .arr,.xlink .arr::after{transition:none}
  .xlink:hover .arr{width:26px}
  .photo-frame:hover img{transform:none}
  /* le volet et le bouton menu se contentent d'apparaitre : plus de glissement, plus de rotation */
  .nav-links,.nav-burger svg,.nav-burger .nb-bars line,.nav-burger .nb-x{transition:none}
  /* le collant est une animation de défilement : retiré, l'image reprend sa place dans sa case */
  .photo-frame,.split figure{position:static}
  .photo-frame img,.split figure img{transform:none}
  /* les bandes-photo du corps ont leur propre bloc de mouvement reduit, a la fin de leur section :
     declarees ici, leurs regles seraient battues par les regles `@media (min-width:921px)` plus
     bas (meme specificite, ecrites apres). Mesure : translate 0px et scale 1.1 survivaient. */
  .ncard:hover{transform:none}
  .ncard .arr,.ncard .arr::after{transition:none}
  .ncard:hover .arr{width:26px}
}

/* ── pages commune ── */
.svc h1.long{font-size:clamp(28px,3vw,38px)}
.lead.also{margin-top:28px;font-size:14px}
.lead.also a{color:var(--muted)}
.lead.also a:hover{color:var(--fg)}
.meta.tight{margin:5px 0 22px}
@media (max-width:920px){
  .svc h1.long{font-size:clamp(24px,6.2vw,32px)}
}

/* ── articles de chantier ──
   La méta-ligne d'un article (date · commune · durée · surface · matériau) doit se lire
   comme une fiche, pas comme un paragraphe : monospace, petite, et elle se replie en
   colonne sans que les « · » restent en fin de ligne. */
/* Le corps d'article n'est plus une colonne centre : chaque temps du chantier est un BLOC,
   et la photo du temps devient l'arriere-plan du bloc (voir « bandes » plus bas). Le texte
   n'est plus centre non plus : il est aligne et JUSTIFIE (demande 2026), les deux rives de la
   colonne tombent au meme endroit d'un paragraphe a l'autre.
   Les cartes de fin de corps (note, liste) gardent une colonne bridee : elles ne portent pas
   de photo, un bloc pleine largeur y ferait des lignes de 90 caracteres. */
.art{text-align:left}
.art .note,.art-check{max-width:74ch;margin-inline:auto}
.svc .ameta{font-family:var(--font-mono);font-size:12px;letter-spacing:.04em;color:var(--fg-3);margin:0 0 18px;line-height:1.9}
.svc .ameta a{color:var(--muted);text-decoration:none;border-bottom:1px solid var(--border-2)}
.svc .ameta a:hover{color:var(--fg);border-bottom-color:var(--fg-3)}
/* Le corps d'article est le seul endroit du site où un titre de paragraphe n'est pas
   un titre de section : il reste sous le h2 de section en taille, au-dessus en graisse.
   IL EST A LA COULEUR PRIMAIRE (--accent) : le titre est le seul repere du temps qui ne se lisite
   pas sur le texte — il doit donc s'extraire de la colonne. Le contraste est porte par le plateau du
   degrade sous la colonne (opaque a .93, regle du § 2) et par la vitre : mesure en deux themes, au
   repos comme a l'etat revele, la page garde 4,5:1 et plus sur tout le corps (outils de mesure). */
.art h2{font-size:clamp(19px,2vw,23px);font-weight:800;letter-spacing:-.015em;line-height:1.2;margin:0 0 16px;color:var(--accent)}
/* 24 px entre deux paragraphes : chaque temps du chantier se relit comme un bloc. Le texte
   est justifie (les deux rives alignees), avec la césure francaise pour que la justification
   ne sorte pas des « trous » entre les mots sur une colonne etroite ; la ligne reste bridee a la
   colonne du bloc (--txtw, regle du § 2) : un paragraphe plus etroit que sa colonne laisserait la
   photo a nu sous la rive droite du texte. */
.art p{font-size:15px;color:var(--muted);line-height:1.9;max-width:var(--txtw,44ch);margin:0 0 24px;text-align:justify;text-justify:auto;hyphens:auto;-webkit-hyphens:auto}
.art .photo-frame{margin:34px 0}
/* la bande ne prend PAS cette marge : elle est en absolu, calee sur les deux bords ET sur toute la
   hauteur du bloc (inset-block:0). Une marge de 34 px sur un absolu ne se replie pas, elle
   RETRECIT le cadre : mesuree a 1440 px, la bande faisait 461 px dans un bloc de 529 px, soit 34 px
   de fond nu en haut et en bas — et la course du parallaxe (reserve --zb) devenait plus grande que
   ce que l'image avait a manger. Meme specificite que la regle du dessus, elle la gagne donc en
   etant ecrite apres. */
.art .art-band{margin:0}
.art-check{margin-top:34px;padding-top:26px;border-top:1px solid var(--border)}

/* Le texte du post est dans la page : il se lit, se sélectionne et se copie tel quel.
   Les sauts de ligne sont ceux du post, pas ceux d'un paragraphe justifié. */
.post{max-width:64ch;margin-top:32px;background:var(--inset);border:1px solid var(--border);border-radius:var(--radius-lg);padding:26px}
.post p{font-size:14px;color:var(--muted);line-height:1.85;margin:0 0 14px;white-space:pre-line}
.post p:last-child{margin-bottom:0}
.post-hook{font-family:var(--font-display);font-size:19px;font-weight:800;color:var(--fg);line-height:1.35}
.post-cap{border-top:1px solid var(--border);padding-top:16px}
.post-tags{font-family:var(--font-mono);font-size:12px;letter-spacing:.02em}
.post-tags a{color:var(--muted);text-decoration:none}
.post-tags a:hover{color:var(--accent)}

/* Le fil des chantiers : une carte par article, vignette à gauche. La vignette est
   déjà en 1200×630 (le format de partage) : elle n'est pas recadrée ici. */
.news{display:grid;gap:22px;margin-top:36px}
/* Sur l'accueil, la section prix ne garde qu'une carte : la grille trois colonnes
   laisserait deux vides à droite. */
.xlinks.one{grid-template-columns:minmax(0,1fr)}
.ncard{display:grid;grid-template-columns:minmax(0,320px) minmax(0,1fr);gap:24px;align-items:center;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:20px;text-decoration:none;transition:border-color .2s ease,transform .2s ease}
.ncard:hover{border-color:var(--border-2);transform:translateY(-2px)}
.ncard img{width:100%;height:auto;border-radius:var(--radius)}
.ncard h2{font-size:clamp(18px,2vw,22px);font-weight:800;letter-spacing:-.015em;line-height:1.2;margin:6px 0 10px}
.ncard p{font-size:14px;color:var(--muted);line-height:1.8;margin:0 0 14px;max-width:62ch}
.ncard .arr{display:block;width:26px;height:1px;background:var(--fg-3);position:relative;transition:width .2s ease,background-color .2s ease}
.ncard .arr::after{content:"";position:absolute;right:-1px;top:-4px;width:8px;height:8px;border-top:1.5px solid var(--fg-3);border-right:1.5px solid var(--fg-3);transform:rotate(45deg);transition:border-color .2s ease}
.ncard:hover .arr{width:38px;background:var(--fg)}
.ncard:hover .arr::after{border-color:var(--fg)}
.nmeta{font-family:var(--font-mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--fg-3);margin:0}
@media (max-width:920px){
  .ncard{grid-template-columns:minmax(0,1fr);gap:18px}
  .svc .ameta{line-height:2.1}
}


/* ============================================================================
   BLOC DECALE DE LA BALISE <style> DE index.html (demande 2026) : la feuille
   partagee est la seule source de style, une regle ecrite ici est donc lisible
   par les 49 pages. Le bloc est pose EN FIN de feuille : l'ordre relatif des
   regles est celui qui etait mesure quand il suivait la feuille, donc aucune
   collision n'apparait. Les regles ci-dessous portent presque toutes un prefixe
   .svc.home, que seule la page d'accueil porte (verifie sur les 48 autres).
   Les trois regles qui ne portent pas de prefixe ont ete elargies, la
   specification d'id #chantier retiree comme demande ; leur effet hors accueil
   est mesure ici, pas suppose.
   ========================================================================== */

/* ── etapes du deroule, regles elargies ──
   Les trois regles qui suivaient #chantier ne le portent plus. Effet mesure sur les 49 pages :
   - la reserve de la 5e etape ne s'applique qu'au bloc qui a CINQ colonnes : les 44 autres
     blocs .steps du site n'en ont que quatre, leur ::after n'existe pas, leur reserve de
     344 px reste donc celle de la feuille (mesure outillage : 1 bloc a cinq colonnes, 44 a
     quatre, 5e::after = 120px sur le seul premier) ;
   - .steps{padding-top:32px} descend desormais sur les 45 blocs : la premiere pastille se
     pose a 66 px du haut de la grille au lieu de 34 (le filet .steps::before est en absolu,
     un padding n'abaisse que les colonnes) ;
   - .note{margin-top:56px} descend sur les 60 encarts .note du site, contre 34 px dans la
     feuille. C'est le changement le plus large du deplacement ; la page du questionnaire
     garde son .diag .note{margin-top:6px}, son style de page etant charge apres la feuille. */
/* Étapes de « Après votre appel » : par défaut le contenu est replié, seul le numéro se
   voit. Il se déplie quand l’étape devient ACTIVE, c’est-à-dire quand son numéro passe à
   l’ambre (.cur, pose par assets/a2t.js sur l’etape la plus proche du milieu de l’ecran) :
   le masque (.step-frame) part a largeur nulle et grandit avec sa colonne.
   L’etape ACTIVE est aussi la plus LARGE : la rangee passe en flex et .cur prend flex-grow
   2,2 contre 1 aux quatre autres (35,4 % de la rangee contre ~16 %). Le masque grandit avec sa
   colonne, dont le bord droit reste a 26 px de la pastille de l’etape suivante : le texte ne
   passe jamais sur elle, et l’animation finit quand la colonne a atteint sa taille.
   Le contenu (.step-slot) est epingle a une largeur FIXE au lieu d’epouser la largeur du
   masque : sans ca, la largeur 0 le replierait mot par mot, la colonne changerait de hauteur a
   chaque etape et la page sauterait pendant le defilement (les cinq etapes sont dans la meme
   rangee). Le texte ne se re-plie donc jamais et les cinq colonnes gardent la meme hauteur
   pendant que les largeurs bougent.
   Cette largeur fixe est exactement la largeur de CONTENU de la colonne large. Les colonnes sont
   en box-sizing:content-box : flex-basis:0 se repartit donc sur l’espace RESTANT de la rangee,
   c’est-a-dire 100cqi moins les retraits horizontaux des cinq colonnes (26 px pour la 1re,
   1 + 26 + 26 = 53 px pour les quatre autres, soit 238 px au total). Une part vaut
   (100cqi - 238px) / 6,2 et la colonne large en prend 2,2 parts. Le meme calcul vaut pour TOUTES
   les positions : la 1re colonne perd moins de retraits mais recoit la meme part, donc une seule
   valeur suffit. Le texte ne peut alors etre plus large que son conteneur : il ne depasse ni le
   masque ni la colonne.
   shortcut: les 238 px de retraits comptent les CINQ colonnes de ce bloc (.steps.five) ; a refaire
   si ce bloc changeait de nombre d’etapes.
   Le masque, lui, s’ouvre a 100 % + 26 px : son bord droit se pose sur le bord droit de la
   COLONNE, pas sur le bord droit du texte. Le paragraphe garde ainsi les 26 px de retrait droit
   de la colonne a l’interieur du masque : le balayage se finit avant le dernier caractere, donc
   le texte ne parait jamais coupe par le bord du masque. Le masque reste a l’interieur de la
   boite de sa colonne (26 px = exactement son retrait droit) : il ne passe jamais sur la pastille
   de l’etape suivante, dont le bord gauche est a 26 px de la.
   L’unite cqi est declaree sur la rangee (.steps), pas sur la colonne, pour que la mesure du
   texte ne suive pas la colonne qui grandit. Les colonnes repliees gardent cette mesure : leur
   texte est plus large que leur colonne de 16 %, mais le masque a largeur 0 le rogne.
   Le masque prend 24 px de marge interieure verticale, compensee par une marge negative
   egale (aucune mise en page ne bouge) : le deplacement vertical des elements sous le
   parallaxe (jusqu’a 18 px) reste dans la zone vue et n’est pas rogne par overflow:hidden.
   Exception assumée a la regle « transform et opacity seulement » : la largeur et la
   repartition des colonnes sont animees parce que c’est l’effet demande, et une seule
   colonne est ouverte a la fois.
   Sous .act, sous @supports et au-dessus du point de rupture mobile (921 px) : sans script,
   sans IntersectionObserver, en mouvement réduit, sans unité de conteneur ou en colonne
   unique, rien n’est replié ni élargi — le contenu reste affiché et indexable. */
/* Réserve de la 5e etape : la reserve de --dw (320 + 24 = 344 px, bloc « etapes » de assets/a2t.css) est
   la moitie EN DESSOUS du degagement qui permet a une etape de rester epinglee au milieu de
   l’écran pendant sa fenetre d’ambre. Sur les colonnes 1 a 4 elle se superpose au contenu des
   colonnes suivantes de la rangee : elle ne se voit pas. Sur la 5e, derniere de la rangee, elle
   ne recouvre rien et creuse 344 px + 40 px de marge sous le dernier paragraphe, soit ~384 px
   avant la section suivante (le rythme du site entre deux sections vaut 76 + 76 = 152 px).
   La ramener a 120 px supprime ce vide : l’etape 5 ne garde plus que 120 px de course EN DESSOUS au
   lieu de 344, son bloc se detache donc ~224 px plus tot, dans la fin de sa fenetre d’ambre
   (les etapes 1 a 4 ne bougent pas). Elle etait reglee dans le style de la page, ou elle ne touchait que le bloc de l'accueil ; la voila
   dans la feuille partagee et donc sur les 45 blocs .steps du site. Mesure apres le deplacement :
   seule la 5e colonne de l'accueil a un ::after (les 44 autres blocs n'ont que quatre colonnes),
   donc la reserve de 344 px des autres blocs reste inchangee.
   Desktop seulement : sous 921 px, le meme pseudo-element est le RAIL ambre de la colonne, pas
   une reserve (bloc « etapes », section mobile de assets/a2t.css). */
@media (min-width:921px){
  .steps .step:nth-child(5)::after,.steps.act .step:nth-child(5)::after{height:120px}
}
/* Deux degages autour de la liste d’étapes, mesures sur la mise en page repos
   (assets/a2t.css : filet .steps::before en haut de la grille, border-top de .note en bas).
   EN HAUT : la premiere pastille est posee SUR le filet horizontal qui se dessine au-dessus de
   la grille (0 px, et meme -8 px pendant que le parallaxe descend le filet de --sy jusqu’a
   14 px). Le filet est en absolu sur la boite interieure de la grille : un padding-top ne le
   deplace pas, il abaisse les colonnes. 32 px garde plus de 18 px meme au parallaxe maximal.
   Regle desktop seulement : le filet est display:none sous 921 px (bloc « etapes », section mobile de la feuille), donc le
   degagement n’a plus de ligne a ecarter en colonne unique.
   EN BAS : 136 px au repos sous la bordure haute de l’encart, mais l’etape en cours est epinglee
   (position:sticky) et sa course descend a 40 px sous cette bordure. Margeur la marge propre a
   l’encart (34 -> 56, la marge de la grille n’est pas touchee) rend ces 40 px lisibles sans
   rallonger la reserve de la 5e etape : la hauteur de page ne gagne que 22 px. */
@media (min-width:921px){
  .steps{padding-top:32px}
}
.note{margin-top:56px}
/* ── Visuel du hero : la photo comme fond du bandeau, plus de la moitie de l'ecran,
   et le texte pose sur des formes givrees ──
   La photo n'est plus un cadre dans une colonne : elle remplit le bandeau du hero du bord
   droit jusqu'a 38 % de la largeur, de son bord haut a son bord bas. Le hero passe en flux
   libre (display:block) et l'image est en absolu, etiree en cover dans cette boite. Les
   regles ne ciblent que header.svc.home : les 18 autres heroes (class="svc") gardent leur
   grille deux colonnes et leur cadre photo.

   Surface : a 1280 x 800 la boite mesure 794 x 1006, soit 77 % de la largeur utile et 98 %
   de l'ecran ; a 1600 x 900, 992 x 1006 = 62 % de l'ecran ; a 921 x 700, 571 x 968 = 74 %.
   Partout plus de la moitie de l'ecran, ce qui etait demande (l'ancienne photo en contain
   faisait 33 % a 1280 x 800).

   Le fichier (assets/hero-toiture-p.jpg, 1056 x 1408, ratio 0,75) est plus etroit que la
   boite : en cover il est done rogne en haut et en bas, jamais sur les cotes. Les mesures du
   fichier, balayage par bandes de 10 % :
   - le premier dixieme (le ciel) est a 251 de moyenne sur 255 : c'est la zone la plus vide du
     fichier, elle disparait dans le rognage haut ;
   - le sujet (le couvreur, les mains, le rang d'ardoises a poser) tient de y 25 % a y 75 % en
     moyenne, avec un relief de 14 a 102 sur 255 : la fenetre d'affichage est calee a 55 % pour
     que le geste reste au centre de la boite quelle que soit la hauteur rogne ;
   - le rognage porte donc sur le ciel et sur le bas du pan de toit, pas sur l'ouvrier.

   Lisibilite : c'est le point dur, parce que le fichier est CLAIR (moyenne 149 sur 255,
  55 % de ses pixels au-dessus de 140) alors que le texte du hero est blanc ou gris. Trois
   etages, et un seul mecanisme de couleur : la couleur du bandeau.
   1. LE VOILE est deplace sur le hero lui-meme (.svc.home::before) : avant, il etait taille
      sur la boite de l'image, qui ne couvrait que 42 % de la largeur ; la moitie gauche de
      l'ecran ne recevait donc aucun voile. Il est maintenant en inset:0 sur tout le bandeau,
      et ses arrets ont ete serres a cause du fichier clair : a l'horizontal 1,0 jusqu'a 26 %,
      .92 a 34 %, .74 a 44 %, .50 a 58 %, .20 a 74 %, 0 a 88 % ; au vertical 1,0 jusqu'a 9 %,
      .82 a 18 %, .54 a 32 %, .34 a 48 %, .50 a 64 %, .78 a 82 %, 1,0 a 94 %. Les deux axes
      utilisent la MEME couleur, donc ils se composent en alpha = a1 + a2 x (1 - a1) : sous le
      paragraphe (x ~40 %, y ~55 %) le voile vaut deja .80 avant le verre.
   2. LES FORMES GIVREES : cinq plaies posees sur les blocs de texte (la balise + le titre,
      le paragraphe, les chiffres, le sommaire), decallees les unes par rapport aux autres
      (le titre depasse de 26 px a gauche, le paragraphe de 30, les chiffres de 22, le sommaire
      de 20) : ce ne sont pas quatre bandes alignees mais des formes reparties derriere le texte.
      Le padding va vers l'exterieur (marge negative egale) : la largeur de contenu de chaque bloc
      reste celle qui etait mesuree avant, donc aucun ne se recharge autrement et la hauteur du
      hero ne bouge pas (CLS au chargement inchange). Chacune : fond de bandeau a .46, flou
      d'arriere-plan 26 px + saturation 150 %, et un filet de 1 px a la couleur du texte a 6 % pour
      que son bord se lise. Le flou efface le relief (le rang d'ardoises derriere le paragraphe ne
      se lit plus) et le voile ramene la zone a la couleur du bandeau.
   3. LE THEME : le hero reste fonce dans les deux themes, en sombre comme en clair, et TOUTE
      son interface suit le jeu sombre. Ce n'est pas un gout : avec une photo claire derriere du
      texte, les jetons clairs sont a l'envers (texte #131723 sur une photo a ~150 de moyenne =
      1,4:1). Les jetons d'interface du hero (fonds, textes, filets, canal de bandeau ET ambre)
      sont donc redéfinis sur .svc.home avec les valeurs du theme sombre ; les formes lisent
      var(--band-rgb), donc leur voile suit automatiquement. Le fichier partage ne declare
      aucune regle pour .svc.home (verifie : seule cette page en porte), donc rien d'exterieur
      ne vient ecraser ces jetons. L'ambre du theme clair n'est pas remplace ici : le bandeau du
      hero est redefinition en BLANC dans ce theme (regle --band plus bas), et l'ambre clair
      demande en 2026 (#A8500A) y tient 5,5:1. Seuls les fonds restent a l'envers.

   Ce que ces chiffres ont donne, mesure pixel par pixel sur la page rendue (tools/measure-hero-text.cjs,
   qui releve les pixels autour des glyphes et sous les plaies, et non une moyenne) :
   PIRE cas des six blocs de texte, aux trois tailles d'ecran et aux deux themes :
     titre 12,3:1 · paragraphe 10,8:1 · chiffres 11,4:1 · sommaire 10,6:1 · liens 11,7:1 ·
     badge 8,2:1 · en-tete collant 12,8:1. Seuil AA 4,5:1 : tenu partout avec plus du double
   de marge, y compris sur le pixel le plus clair du fichier.

   Derivations (parallaxe) : les trois couches gardent leurs vitesses (texte 1,00 x, halo
   1,08 x, photo 1,21 x). La course est toujours 22 % de la HAUTEUR RENDUE de l'image, donc sa
   valeur absolue ne change pas (le rendu vertical est inchange) ; comme la boite est plus
   large, le rapport image/hero passe de 0,70 a ~0,60 et la vitesse mesuree a l'ecran de
   1,15 x a ~1,13 x le defilement. Le garde tools/guard-hero-parallax.cjs (course 20-23 % de
   l'image, vitesse 1,10-1,24 x, place figee inchangee, CLS < 5e-4) passe sans seuil retouche.
   Comme avant : le decalage part de 0 en haut de page, ne devient jamais positif (la photo
   monte, elle ne franchit pas le bord bas), et le cadre reste en overflow:visible sans
   rognage additionnel — la coupe haute se fait derriere l'en-tete collant et sous un voile a
   .82-.100, la coupe basse sous un voile a .78-1,0.
   Le masque radial qui dissolvait les aretes du contain est retire : en cover, les quatre
   aretes de l'image tombent sur des bords du hero (droit, bas) ou sous un voile a 1 (haut,
   gauche). Le voile remplace le masque.
   Le survol qui zoome est neutralise par pointer-events:none sur le cadre, et le collant des
   images (assets/a2t.js) ne doit pas lui rendre un position:sticky : la regle ci-dessous est
   plus specifique que .photo-frame.

   Sous 921 px, la photo devient un bandeau au bas du hero (46 vh) : le texte occupe toute la
   largeur et les plaies ne couvriraient plus que du texte sur du texte. Le bandeau garde son
   voile a un seul axe et ne voyage pas. */
.svc.home{position:relative;display:block;min-height:92vh;padding-top:84px;overflow:hidden}
/* En clair le degrade du bandeau se construit a partir du BLANC, en sombre a partir du bleu
   nuit : le jeton --band du hero suit le theme demande au lieu d'imposer un bandeau fonce.
   Le jeton est redefinI ici, pas sur la feuille partagee : les 18 autres heroes et le reste de
   l'accueil gardent les fonds et l'ambre du theme demande. */
.svc.home{ --band:#08101F; --band-rgb:8,16,31; }
html[data-theme="light"] .svc.home{ --band:#FFFFFF; --band-rgb:255,255,255; }
/* Le filet des plaques givrees est a 6 % du texte (var(--fg-soft), meme valeur en clair qu'en
   sombre dans la feuille partagee) : sur la photo, un trait a la couleur du fond ne se voit pas.
   En clair ce filet de 6 % disparait sur une plaque quasi blanche ; il est raide a 12 % dans le
   hero seul. L'ambre n'est pas touche : l'ambre clair demande en 2026 (#A8500A) mesure 5,5:1
   dans la pastille
   du hero en theme clair (mesure, tools/measure-hero-text.cjs) — il tenait deja. */
html[data-theme="light"] .svc.home{ --fg-soft:rgba(19,23,35,.12); }
.svc.home>div{position:relative;z-index:4;max-width:62%}
.svc.home::before{content:"";position:absolute;inset:0;z-index:2;pointer-events:none;background:
  linear-gradient(90deg,var(--band) 0%,var(--band) 26%,rgba(var(--band-rgb),.92) 34%,rgba(var(--band-rgb),.74) 44%,rgba(var(--band-rgb),.50) 58%,rgba(var(--band-rgb),.20) 74%,rgba(var(--band-rgb),0) 88%),
  linear-gradient(180deg,var(--band) 0%,var(--band) 9%,rgba(var(--band-rgb),.82) 18%,rgba(var(--band-rgb),.54) 32%,rgba(var(--band-rgb),.34) 48%,rgba(var(--band-rgb),.50) 64%,rgba(var(--band-rgb),.78) 82%,var(--band) 94%,var(--band) 100%)}
/* Les memes arrets servent aux deux themes : seul le jeton change, le voile se construit donc a
   partir du blanc en theme clair et du bleu nuit en sombre. Les paliers n'ont pas besoin d'etre
   montes pour le texte fonce du theme clair : ce qui porte le contraste en clair, ce sont les
   plaques givrees (fond a .90, regle plus bas), pas le voile. Mesure, les deux themes a trois
   tailles d'ecran (tools/measure-hero-text.cjs) : avec les paliers du sombre, tout le texte du
   hero reste au-dessus de 4,96:1 en clair. Le flanc droit de la photo garde son relief (la
   colonne lit 95-255 a 1280 px, comme en sombre ou elle lit 7-173) : un voile monte a .98
   effacait le couvreur pour une raison qui n'existait pas. */
.svc.home::after{content:"";position:absolute;z-index:1;left:30%;right:0;bottom:0;height:82%;pointer-events:none;transform:translateY(var(--pxh,0px));background:
  radial-gradient(48% 54% at 66% 72%,var(--accent-soft),rgba(var(--band-rgb),0) 68%),
  radial-gradient(30% 34% at 92% 22%,var(--accent-soft),rgba(var(--band-rgb),0) 66%),
  radial-gradient(26% 30% at 46% 92%,var(--accent-soft),rgba(var(--band-rgb),0) 64%)}
.svc.home .photo-frame.hero-cut{position:absolute;z-index:1;inset:0;margin:0;border:0;border-radius:0;overflow:hidden;background:none;pointer-events:none}
.svc.home .hero-cut img{position:absolute;z-index:1;left:38%;right:0;top:0;bottom:0;width:auto;height:auto;max-width:none;max-height:none;-o-object-fit:cover;object-fit:cover;-o-object-position:center 55%;object-position:center 55%;transition:none}
/* Les corps mous derriere le texte : cinq formes SVG irregulieres, posees en absolu derriere les
   blocs, avec un flou d'arriere-plan et une touche de la couleur du site. Elles remplacent les
   plaques givrees : six verres qui epousaient exactement la boite de chaque bloc (balise, titre,
   paragraphe, boutons, chiffres, sommaire), alignes et bordés d'un filet de 1 px. Le fond lisible
   est conserve (c'est ce qui porte le contraste du texte) mais il ne suit plus la geometrie du
   contenu : les formes sont decoupees au clip-path, decalees les unes sur les autres, et leur
   contour ne coincide avec aucun bloc.

   Pourquoi un <i> clippe et non un <path> : mesure dans le moteur de rendu (tools/_probe-bdf.cjs),
   backdrop-filter ne s'applique PAS a un <path> ni a un <g> SVG — les rayures derriere restent
   nettes. Il s'applique a un element HTML clippe par url(#...) (tools/_probe-bdf2.cjs) et survit au
   filter:blur() du meme element (tools/_probe-bdf3.cjs). Le decoupage est donc porte par le SVG, le
   flou d'arriere-plan par le <i>.

   DEUX CHOSES ont change par rapport au premier montage, et elles sont mesurees (tools/_blobcap.cjs,
   tools/measure-hero-text.cjs) :

   1. LES FORMES SONT MOINS FLUES ET DESSATUREES. Le flou d'element tombe de 16-22 px a 5-6 px : la
      silhouette se relit comme une forme, plus comme une buee. La saturation forcee (+150 % du fond)
      est supprimee des deux axes, backdrop-filter comme filter : elle jaunissait la photo derriere
      le texte et c'est ce qui rendait le verre "apparent". Ce qui reste sous le texte, c'est un
      voile de bandeau en degrade radial + un flou d'arriere-plan de 20 px qui efface le relief de
      l'ardoise. Le fond ne peut toujours pas etre plat : a alpha constant, le bord du clip-path
      resterait visible comme un gabarit, donc l'alpha descend a 0 avant le bord de la boite.

   2. ELLES SONT CONTRAINTES A 2rem DU TEXTE. Les cinq <i> sont enfants de la colonne de texte (la
      meme boite que .svc.home>div, qui est en position:relative) et non d'un porte-formes pleine
      largeur : la contrainte n'est donc pas un calcul de pourcentages par taille d'ecran, c'est la
      boite d'accueil. Les corps sortent de cette boite de 30 px au plus (les blocs debordent eux-
      memes de 26 a 30 px par leurs marges negatives, donc le corps s'arrete a la boite du bloc, pas
      au dela), leur derive est de 8 px au plus et l'etalement du flou de 5 px au plus : 30 + 8 + 5 =
      43 px comptes depuis la COLONNE, mais seulement 13 px depuis l'union des boites de texte. C'est
      cette derniere valeur qui est la contrainte demandee (2rem = 32 px) et c'est elle que verifie
      tools/_blobcap.cjs, en echantillonnant les cinq phases de la derive aux trois tailles d'ecran.
      Le corps le plus a droite (hb-2, derriere le paragraphe) sort de 46 px de la colonne : 16 px au
      dela de la boite du paragraphe, donc 29 px de son union, sous les 32 px. Sans cette marge, une
      ligne du paragraphe a 921 px tombait sur la photo a nuef (fond 165, 2,97:1 en theme clair).

      Les deux corps de couleur (hb-4 hb-5) ne portent aucun texte : ils sont cales sur le bord droit
      de la colonne, la ou la photo commence (le calage de l'image est a 38 % du bandeau) et ou le
      voile n'est qu'a .50. C'est la qu'ils portent l'ambre. Ils ne sont pas places plus loin sur le
      flanc de la photo comme avant : ce serait hors des 2rem et cela re-chauffait la bande de la
      pastille (mesure 4,38:1 sur le libelle ambre, sous le seuil AA).

   La derive : cinq vitesses differentes (17 a 44 s), en alternate, sans raccord, en translation seule
   (pas de scale : une echelle de 1,05 sur une boite de 600 px de large ajoutait 15 px hors de la
   boite, hors contrainte). Elle ne deplace aucun texte (corps en absolu, hors flux : CLS inchange).
   Elle est coupee en mouvement reduit ; les corps restent en place, la composition figee est la meme. */
.svc.home>.hb-clip{position:absolute;width:0;height:0;overflow:hidden}
.hb{position:absolute;display:block;pointer-events:none;z-index:-1;filter:blur(8px);
  backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);
  background:
    radial-gradient(52% 56% at var(--hb-hx,34%) var(--hb-hy,26%),var(--accent-soft),rgba(var(--band-rgb),0) 70%),
    radial-gradient(72% 68% at 46% 46%,rgba(var(--band-rgb),var(--hb-hi,.90)),rgba(var(--band-rgb),var(--hb-lo,.62)) 60%,rgba(var(--band-rgb),0) 92%)}
.hb-1{--hb-hx:70%;--hb-hy:62%;left:-30px;right:-26px;top:-12px;height:33%;clip-path:url(#hb-a);animation:a2t-hb-a 44s ease-in-out infinite alternate}
.hb-2{--hb-hi:.88;left:-30px;right:-46px;top:25%;height:33%;clip-path:url(#hb-b);filter:blur(4px);animation:a2t-hb-b 36s ease-in-out infinite alternate}
.hb-3{--hb-hx:64%;--hb-hy:28%;--hb-hi:.86;left:-30px;right:-30px;bottom:-12px;height:40%;clip-path:url(#hb-c);animation:a2t-hb-c 41s ease-in-out infinite alternate}
/* Les deux corps de couleur : alpha de bandeau plus bas, touche ambre doublee. Ils sont cales sur
   le bord droit de la colonne (la ou la photo commence), jamais plus loin sur son flanc : plus loin,
   ils sortaient des 2rem et re-chauffaient la bande de la pastille (mesure 4,38:1 sur le libelle
   ambre, sous le seuil AA). */
.hb-4{--hb-hi:.60;--hb-lo:.34;right:-30px;top:5%;width:38%;height:22%;clip-path:url(#hb-d);filter:blur(4px);animation:a2t-hb-d 24s ease-in-out infinite alternate}
.hb-5{--hb-hi:.56;--hb-lo:.30;right:-30px;top:56%;width:34%;height:26%;clip-path:url(#hb-e);filter:blur(4px);animation:a2t-hb-e 17s ease-in-out infinite alternate}
.hb-4,.hb-5{background:
    radial-gradient(56% 60% at 36% 30%,var(--accent-soft),rgba(var(--band-rgb),0) 74%),
    radial-gradient(56% 60% at 64% 68%,var(--accent-soft),rgba(var(--band-rgb),0) 74%),
    radial-gradient(70% 66% at 46% 46%,rgba(var(--band-rgb),var(--hb-hi)),rgba(var(--band-rgb),var(--hb-lo)) 58%,rgba(var(--band-rgb),0) 90%)}
@keyframes a2t-hb-a{from{transform:translate3d(0,0,0)}to{transform:translate3d(-8px,6px,0)}}
@keyframes a2t-hb-b{from{transform:translate3d(0,0,0)}to{transform:translate3d(7px,-8px,0)}}
@keyframes a2t-hb-c{from{transform:translate3d(0,0,0)}to{transform:translate3d(-6px,-9px,0)}}
@keyframes a2t-hb-d{from{transform:translate3d(0,0,0)}to{transform:translate3d(8px,7px,0)}}
@keyframes a2t-hb-e{from{transform:translate3d(0,0,0)}to{transform:translate3d(-8px,-6px,0)}}
@media (prefers-reduced-motion:reduce){.svc.home .hb{animation:none}}
/* La geometrie des blocs ne change pas : les marges negatives restent Egales au padding, donc la
   largeur de contenu (et les retours a la ligne) est celle qui etait mesuree avant et la hauteur du
   hero ne bouge pas. Seuls le fond, le verre, le filet et l'arrondi sont retires : c'etaient eux
   qui epousaient la forme de chaque contenant. Le libelle du bouton fantome (fond transparent)
   est garde sous un corps de fond comme il etait sous une plaque : a 1280 px il tombait sur le
   flanc de la photo, la ou le voile n'est qu'a .50, et valait 1,06:1 en theme clair. */
.svc.home>div>.tag,.svc.home>div>h1,.svc.home>div>.lead,.svc.home>div>.actions,.svc.home>div>.facts-row,.svc.home>div>.toc{background:none;border:0;border-radius:0}
/* L'ombre portee de la legende est calculee sur --band-rgb (feuille partagee) : a 70 % de blanc
   elle ne portait rien en clair. Elle prend le noir du theme clair, a 14 %. */
html[data-theme="light"] .svc.home .hero-cut figcaption{box-shadow:0 8px 28px rgba(19,23,35,.14)}
/* Le hero du carnet (header.svc.home lui aussi) a une colonne de texte plus large que la
   d'accueil : aucun element ne porte de fond, donc le paragraphe lit le voile seul. Sur
   921-1279 px sa derniere ligne tombe la ou le voile ne monte qu'a ~.63 : mesure du fond
   reel 169 de moyenne sur 255, soit 3,08:1 pour un texte a #131723, sous le seuil AA 4,5.
   Le voile est ferme sur cette plage uniquement (au-dela, la colonne n'atteint plus la
   photo). Deux mesures : pixels les plus clairs des lignes porteuses de glyphes, 11
   largeurs de 921 a 1920, deux themes. Pire cas releve apres correctif : 6,03:1
   (clair 1360x850, paragraphe). Le hero d'accueil passe le meme balayage sans echec. */
@media (min-width:921px) and (max-width:1279px){
  .svc.home::before{background:
    linear-gradient(90deg,var(--band) 0%,var(--band) 30%,rgba(var(--band-rgb),.96) 44%,rgba(var(--band-rgb),.88) 56%,rgba(var(--band-rgb),.80) 68%,rgba(var(--band-rgb),.55) 80%,rgba(var(--band-rgb),.20) 92%),
    linear-gradient(180deg,var(--band) 0%,var(--band) 12%,rgba(var(--band-rgb),.9) 22%,rgba(var(--band-rgb),.78) 38%,rgba(var(--band-rgb),.72) 55%,rgba(var(--band-rgb),.82) 72%,var(--band) 92%,var(--band) 100%)}
}
/* La ligne de fiche du hero du carnet (date, commune, duree) herite de --fg-3, calcule pour
   un fond uni. Sur le hero a photo elle lit un fond clair mesure a 203-208/255 en theme
   clair : 3,08:1 a 1600x900, sous le seuil AA. Elle prend --fg-2 comme les autres corps
   poses sur la photo ; le lien garde --muted, qui passait deja (6,5:1). Pire cas releve
   apres correctif, 11 largeurs x 2 themes : 5,70:1. */
.svc.home .ameta{color:var(--fg-2)}
.svc.home>div>.tag{padding:9px 18px;margin-left:-18px}
.svc.home>div>h1{padding:22px 26px 20px;margin:-6px -26px 0}
.svc.home>div>.lead{padding:22px 30px;margin-left:-30px;margin-right:-30px}
.svc.home>div>.actions{padding:16px 30px;margin-left:-30px;margin-right:-30px}
.svc.home>div>.facts-row{padding:22px;margin-left:-22px;margin-right:-22px}
.svc.home>div>.toc{padding:16px 20px 8px;margin-left:-20px;margin-right:-20px}
.svc.home .hero-cut figcaption{left:auto;right:clamp(16px,3vw,48px);bottom:clamp(16px,3vh,34px);width:max-content;max-width:44%;font-size:10px;letter-spacing:.16em;text-transform:uppercase;padding:8px 12px;box-shadow:0 8px 28px rgba(var(--band-rgb),.7)}
/* Le decalage est lu ici, apres toutes les regles du montage : seule la photo voyage (la legende
   reste ancree dans son coin), le halo a sa propre course. Le masque qui dissolvait les aretes du
   contain a ete retire avec le contain : en cover, les aretes de l'image tombent sur des bords du
   hero ou sous un voile a 1. Pas de will-change : le site n'en declare nulle part et l'ecriture est
   deja limitee a un seul transform par couche.
   Le decalage n'est declare qu'au-dela de 921 px : le bandeau mobile n'a pas de jeu sous lui (le
   voile y est ouvert a 86 %), toute derive y laisserait une bande de fond entre la photo et le bas
   du hero. La bascule suit le meme seuil que le montage en absolu. */
@media (min-width:921px){
  .svc.home .hero-cut img{transform:translateY(var(--px,0px))}
}

/* Sous 921 px, le texte occupe toute la largeur : la photo ne peut plus passer derriere lui et
   les formes givrees ne seposeraient que du texte sur du texte. Ce n'est pas une question de
   gout mais de contraste : le fichier monte a 254 sur 255 et le texte du hero est en --muted
   (16 px, texte normal, seuil 4,5:1) ; meme sous un voile a .92 un tel pixel ne laisserait plus
   que ~4:1. Le montage en absolu derriere le texte n'est donc garde qu'au-dela de 921 px.
   En dessous, la photo redevient un bandeau au bas du hero : le hero gagne un creux de 46 vh
   (padding-bottom) et la boite de l'image, en absolu cale en bas, le remplit pleine largeur. Elle
   est rognee en haut et en bas (cover), calee a 62 % pour garder le couvreur dans le bandeau, pas
   le ciel. Le voile n'a plus qu'un axe vertical (ferme au-dessus du bandeau, ouvert a 86 %) pour
   que la photo se revele en descendant ; le halo reprend toute la largeur.
   Les formes givrees sont retirees : il n'y a plus de photo derriere le texte, donc plus rien a
   flouter, et quatre cartes derriere du texte sur fond plat ne serviraient a rien. Le filet du
   bloc « Reponse / Zone / SIREN » revient, c'est celui de la feuille partagee.
   La legende reste, ancree dans le coin bas droit du bandeau : elle ne touche ni le visage ni le
   geste, et le paragraphe du hero nomme deja le chantier. */
@media (max-width:920px){
  .svc.home{min-height:0;padding-top:48px;padding-bottom:calc(46vh + 4px)}
  .svc.home>div{max-width:100%}
  .svc.home .hero-cut img{left:0;right:0;top:auto;bottom:0;width:auto;height:46vh;-o-object-position:center 62%;object-position:center 62%}
  .svc.home::before{background:linear-gradient(180deg,var(--band) 0%,var(--band) 40%,rgba(var(--band-rgb),.86) 52%,rgba(var(--band-rgb),.42) 70%,rgba(var(--band-rgb),0) 86%,rgba(var(--band-rgb),0) 100%)}
  .svc.home::after{left:0;right:0;height:40%;transform:none}
  .svc.home>div>i.hb{display:none}
  .svc.home>div>.tag,.svc.home>div>h1,.svc.home>div>.lead,.svc.home>div>.actions,.svc.home>div>.facts-row,.svc.home>div>.toc{padding:0;margin-left:0;margin-right:0}
  .svc.home>div>.tag{padding:6px 15px}
  .svc.home>div>h1{margin-bottom:20px}
  .svc.home>div>.lead{margin-bottom:30px}
  .svc.home>div>.facts-row{border-top:1px solid var(--border)}
  .svc.home .hero-cut figcaption{max-width:none;font-size:11px;letter-spacing:.14em}
}

/* ── Localite du titre : rotation lettre par lettre ──
   Le <em class="loc"> du titre porte la localite ; il tourne sur les 27 communes de la section
   « Zone d’intervention », dans l’ordre de leurs pastilles et de areaServed, donc le mot vu au
   chargement (Caen) n’est pas efface au premier cycle.
   Entree : les lettres entrent une par une, de la gauche vers la droite. Le mot est ensuite REMPLI de
   la couleur principale seule, 1,4 s. Sortie : les lettres sortent une par une, avant que le mot
   suivant n’entre.
   La peinture est un seul degrade partage par tout le mot, pas 27 degrades independants : chaque lettre
   recoit la MEME image (--locw + 2 --ga), decalee de sa distance au bord gauche du mot (--off). Les
   arrets sont donc exprimes en pixels dans les coordonnees du mot, et une seule valeur (--pa, ecrite
   par le script sur .loc, heritee par les lettres) fait avancer le front. « to right » et non un angle :
   a angle oblique, la longueur de la ligne de degrade dependrait de la largeur de chaque glyphe et le
   calage commun se deformerait.
   Le degrade est blanc (--fg), noir (--bg), couleur principale (--accent) : les deux premiers sont les
   deux couleurs du site, aucun hex n’est invente, donc le theme clair retourne le balayage au lieu de le
   casser. Derriere le front tout est a la couleur principale ; devant, blanc ; le noir tient le front
   lui-meme (.55 --ga de chaque cote). Le front court donc au dela du bord droit (w + 1,7 --ga) : sans
   cette course, la queue du front resterait dans le fondu sur la fin du mot et le mot ne serait jamais
   plein a la couleur principale seule.
   Un mot trop large pour la ligne est REDUIT (--fit), jamais laisse se replier : « Hérouville-Saint-
   Clair, » prend 1,42 x la place restante a 44,65 px. La fente reste en ligne et le calibre réduit
   toujours plus petit que le titre : la ligne de base est commune a la rangee, la hauteur de rangee reste
   celle du texte plein calibre (line-height 1,02) et le paragraphe ne saute pas. Le titre garde ses trois
   lignes a chaque cycle.
   Les lettres sont en inline-block (il le faut pour les deplacer) : ligatures et crénage ne jouent plus
   entre elles, donc la largeur du mot est relevee sur le mot decompose, au calibre final, jamais
   supposee. Le mot est deja suivi d’une virgule dans le fichier : elle fait partie du mot anime.
   Tout est suspendu a la classe .loc.on, que seul le script pose : sans elle, en mouvement reduit ou sous
   921 px, aucune lettre n’existe, la fente reste en ligne et le <em> est a la couleur principale comme
   tous les <em> du site. Le mot reste du texte reel (background-clip:text est une peinture de glyphe) ;
   sans ce support il est simplement de la couleur principale et la rotation continue.
   Le <span class="loc-sr">, vide dans le fichier, ne recoit du texte que quand la rotation demarre : le
   mot anime passe alors en aria-hidden et « Caen, » reste dans le titre pour les lecteurs d’ecran. Sans
   script la fente reste vide et le mot n’est lu qu’une fois. */
.loc-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
.svc.home h1 .loc.on{white-space:nowrap;font-size:calc(1em * var(--fit,1))}
.svc.home h1 .loc.on .loc-l{display:inline-block}
.svc.home h1 .loc.on.in .loc-l{animation:a2t-loc-in .42s cubic-bezier(.16,.86,.28,1) both;animation-delay:calc(var(--i,0) * 46ms)}
.svc.home h1 .loc.on.out .loc-l{animation:a2t-loc-out .3s cubic-bezier(.55,0,.86,.3) both;animation-delay:calc(var(--i,0) * 30ms)}
@keyframes a2t-loc-in{from{opacity:0;transform:translateY(.3em) scaleY(.84)}to{opacity:1;transform:none}}
@keyframes a2t-loc-out{from{opacity:1;transform:none}to{opacity:0;transform:translateY(-.22em)}}
/* La peINTure n’est active qu’avec .loc.on.paint : les trois valeurs du degrade (--locw, --ga, --pa)
   viennent de .loc par heredite, donc les declarer sur la lettre les écraserait (le mot deviendrait
   transparent, une image de largeur nulle ne peint rien). Le script met .paint apres avoir mesure le
   mot : entre la decomposition et la mesure, les lettres sont a la couleur principale seule. */
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .svc.home h1 .loc.on.paint .loc-l{--off:0px;background-image:linear-gradient(to right,var(--accent) 0,var(--accent) calc(var(--pa) - .55 * var(--ga)),var(--fg) calc(var(--pa) - .28 * var(--ga)),var(--bg) var(--pa),var(--fg) calc(var(--pa) + .28 * var(--ga)),var(--fg) calc(var(--locw) + 2 * var(--ga)));background-repeat:no-repeat;background-size:calc(var(--locw) + 2 * var(--ga)) 100%;background-position:calc(-1 * (var(--ga) + var(--off))) 0;-webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent}
}
@media (forced-colors:active){.svc.home h1 .loc.on.paint .loc-l{background:none;color:CanvasText;-webkit-text-fill-color:CanvasText}}

/* ============================================================================
   ARTICLE DE CHANTIER : ORDRE SUR PETIT ÉCRAN, IMAGE LE LONG DU TEXTE,
   CARROUSEL, LIENS DU POST   (demande 2026)
   Quatre choses qui n'existaient pas sur les pages de chantier.
   1. ORDRE. Mesuré avant : à 320 px le hero de l'article mesurait 1212 px et le
      premier paragraphe commençait à 1707 px — un écran et demi de défilement
      (titre, deux boutons, trois faits, sommaire, puis la photo) avant le texte.
      Sous 921 px le hero se remet donc dans l'ordre de lecture : visuel, titre,
      méta, accroche, boutons, sommaire, faits. C'est un ordre de FLUX (order sur
      des enfants de grille rendus blocs), pas un empilement absolu. La grille du
      hero ne passe à une colonne qu'à 921 px : la requête est prise au même
      palier que celui de la feuille, et la règle d'ordre vit aussi dans la
      requête « mouvement réduit », qui remet les images en statique.
      Deux choses descendent avec lui, mesurées à 390 px : le sommaire passe en
      rangée horizontale (7 entrées empilées faisaient 185 px de chrome avant le
      texte, une rangée de pastilles en fait 40) et la fiche de faits repart en
      grille (3 faits empilés 133 px, une rangée 44 px).
   2. IMAGE LE LONG D'UN PARAGRAPHE. Le site n'avait AUCUN flottant : chaque photo du corps
      d'article était un cadre pleine largeur, et la photo 4:5 du post mesurée 425 px de haut à
      1280 px (806 px à 768 px) — plus haute que le paragraphe qu'elle accompagnait.
      Un cadre .photo-frame posé en float est réglé par un clamp : 46 % de la colonne de texte,
      jamais sous 260 px ni au-delà de 340 px (borne HAUTE sur la largeur, le flottant ne doit
      jamais ronger la colonne). Mesuré colonne de 700 px, paragraphe le plus long 587
      caractères, police 15/28,5 : à 322 px l'image fait 402 px de haut et le texte voisin 216 px
      (7 lignes) dans 354 px de colonne restante, soit 78 caractères de ligne ; à 340 px l'image
      ferait 425 px pour un texte de 243 px (8 lignes) dans une colonne de 336 px. Le texte
      d'un paragraphe est plus court que l'image qu'il borde : la borne haute est donc celle du
      ratio, pas celle de la colonne. DEUX flottants ne peuvent pas se chevaucher : chaque image
      flottante repart sous la précédente. Sous le palier de 560 px la colonne de texte ne fait
      plus que 280 px : un flottant y laisserait 108 px de texte, la ligne descendrait sous 30
      caractères. Le flottant devient donc pleine largeur sous 560 px.
   3. CARROUSEL. Il défile par pas d'une diapositive : scroll-snap natif + deux boutons qui
      appellent scrollBy. Aucun transform, aucune position écrite image par image : le défilement
      natif garde la sélection tactile, le zoom du navigateur et le défilement du clavier. Une
      diapositive = un cadre .photo-frame, mêmes limites que la feuille : 4:5 et 3:4 sont bornés
      en hauteur, 1,90:1 et 1,34:1 se règlent sur leur ratio, rien n'est rogné. La largeur d'une
      diapositive se regle sur la largeur du rail (pourcentage borne en pixels) et non
      sur la fenetre : mesure, une largeur en vw debordait du carrousel (carrousel de 328 px,
      diapositive de 312 px en vw + ecart de 16 = 328 : le cadre suivant ne depassait pas, le
      pointage ne voyait plus de suite) et le rail se taillait une largeur de fenetre (390 px).
      Les deux boutons sont masqués si la diapositive tient sans défilement
      (.a2t-fit posé par le script, et ils ne sont jamais nécessaires si le
      script ne tourne pas : la rangée défile à la main). Le compteur « i / n »
      est posé dans le HTML, le script ne fait que le reprendre.
   4. LIENS DU POST. Le bloc de partage n'avait que le TEXTE du post : rien pour
      aller le lire sur le réseau. Les quatre liens sont construits sur l'URL de
      l'article (partage Facebook et LinkedIn, profil Instagram, copier le lien)
      et portent le texte du post lui-même ; le texte est donc dans le HTML, pas
      dans le script. Un lien de PROFIL ne peut pas s'inventer : le site publié
      n'annonce aucun compte (la règle de tools/guard-news.cjs l'interdisait), le
      lien est donc désarmé tant que les profils ne sont pas renseignés, et le
      partage reste actif.
   Paliers : 921 / 920 px (une colonne) et 560 px (retrait de la barre) — les deux
   que la feuille utilise déjà. Aucune valeur hors de la grille de 8 px, aucun
   rayon hors des trois étages, aucune couleur hors des tokens.
   ========================================================================== */

/* ── 1. ordre du hero sur une colonne ──
   le visuel ouvre la page (c'est lui qui dit de quoi la page parle), puis ce qui doit
   être lu avant de cliquer : titre, méta, accroche, boutons, plan, fiche de faits.
   La colonne de texte devient une colonne flex pour que « order » s'applique à ses
   enfants : ses enfants sont déjà des blocs, align-items stretch par défaut, donc
   .actions, .toc et .facts-row gardent leur largeur pleine et la requête de conteneur
   de .actions continue de marcher. */
@media (max-width:920px){
  .a2t-article .svc > figure{order:1;margin:0 auto 8px;max-width:380px}
  .a2t-article .svc > div{display:flex;flex-direction:column;min-width:0;order:2}
  .a2t-article .svc > div > .tag{order:1}
  .a2t-article .svc > div > h1{order:2}
  .a2t-article .svc > div > .ameta{order:3}
  .a2t-article .svc > div > .lead{order:4}
  .a2t-article .svc > div > .actions{order:5}
  .a2t-article .svc > div > .toc{order:6}
  .a2t-article .svc > div > .facts-row{order:7}
  /* le visuel du hero ouvre la page : le jeu du dessus se retire (la barre est
     déjà collante), celui du bas reste, c'est le creux avant le titre */
  .a2t-article .svc{padding-top:28px}
  /* la méta se relit comme une ligne de fiche : chaque « · » reste collé au champ
     qu'il introduit (mesuré : à 320 px les points cassaient en fin de ligne et se
     retrouvaient orphelins au-dessus du champ suivant) */
  .a2t-article .svc .ameta .am-field{white-space:nowrap}
  /* le plan de la page devient une rangee de pastilles qui se fait glisser : sept
     entrées empilées à deux colonnes faisaient 185 px de chrome avant le premier
     paragraphe, la rangée en fait 40. La fiche « durée / surface / pluie » garde le
     flex-wrap de la feuille : trois puces tiennent en une rangée dès 390 px (mesuré
     47 px de haut) et ne font plus 133 px empilées. */
  .a2t-article .toc{display:flex;flex-wrap:nowrap;gap:8px;overflow-x:auto;margin-top:20px;max-width:none}
  .a2t-article .toc a{flex:0 0 auto;border:1px solid var(--border-2);border-radius:var(--radius);
    background:var(--surface);padding:9px 12px;gap:8px}
  .a2t-article .toc a[aria-current="section"]{border-color:var(--fg-3)}
}

/* ── 2. la photo derriere le texte du temps de chantier ──
   Un temps = un bloc (.art-block). La photo du temps est posee DERRIERE le texte, en absolu,
   ET a TOUTE LA LARGEUR de l'ecran : le cadre est ancre des deux cotes a la gouttiere de la
   section (inset-inline:-pad), donc il couvre exactement la boite pleine largeur, sans passer
   par 100vw (100vw inclut la barre de defilement et ouvrirait un derapement horizontal).
   Il court sur toute la HAUTEUR du bloc, qui est plus haut que la colonne de texte (le padding
   vertical du bloc, regle plus bas) : la photo depasse les paragraphes PAR-DESSUS et PAR-EN-BAS.

   LA COLONNE ALTERNE : temps IMPAIR a DROITE, temps PAIR a GAUCHE (nth-child sur le bloc, pas de
   classe a porter dans le HTML). Le texte est plaque contre le bord de la zone utile du bloc, la
   photo se lit sur tout le bord libre oppose.

   LA BOITE DE TEXTE FAIT LA MEME HAUTEUR QUE LA PHOTO ET SE CENTRE VERTICALEMENT DEDANS : le bloc
   n'a plus de padding bas, sa hauteur EST la hauteur de la colonne, et la colonne porte
   min-height:100% (la hauteur du bloc est connue, elle vient de la colonne elle-meme : un pourcentage
   se resout, et l'unique colonne pleine hauteur fixe cette hauteur — pas de cercle). Le contenu,
   lui, est centre par flex. Le pied de la photo ne descend donc plus sous les paragraphes : ce qui
   faisait respirer le bloc etait la place libre du cote, pas une bande vide sous le texte.

   Trois couches, dans cet ordre de peinture :
     la bande photo (degrade compris) — z 1 au repos, z 7 une fois revelee ;
     la vitre derriere les paragraphes  — z 2, jamais deplacee ;
     les lignes de texte               — z 5 au-dessus de la bande au repos.
   AU REPOS le texte est au-dessus de la photo, qui se devine derriere lui. REVELE la bande MONTE
   au-dessus de tout (z 7, au-dessus des lignes comme de la vitre) et son degrade s'eteint : la
   photo se donne alors a voir EN ENTIER, sans voile. La vitre ne bouge pas de son rang : c'est la
   bande qui traverse, une seule inversion de couche, pas deux.

   QUI DECLENCHE LA REVELATION : la PHOTO, pas le temps. Le survol porte sur .art-band ; la colonne
   de texte et sa vitre sont au-dessus de la bande et captent le pointeur a leur place — donc passer
   la souris sur le paragraphe ne revele rien, et le texte ne disparait jamais « sous » la souris.
   Il faut aller sur la photo elle-meme (bord libre), ou la cliquer. Le clavier atteint la commande
   par le cadre lui-meme (tabindex pose par assets/a2t.js).

   Le cadre reste un <figure> avec son <figcaption> : le texte descriptif de l'image est conserve,
   epingle dans un coin, et bride pour ne jamais atteindre la colonne centree. */
/* LA COLONNE EST EN PIXELS, PAS EN `ch` : un `ch` se resout avec la police du MORCEAU de feuille ou
   il est utilise, pas avec celle ou la variable est declaree. Mesure : `--txtw:min(44ch,46vw)` rendu
   416 px dans .art-txt (corps a 15 px) mais 265 px dans la legende (10 px) et dans le degrade de la
   bande — donc le voile et la plaquette se calculaient sur une colonne fantome de 265 px, et la
   legende passait sous les paragraphes des 1280 px. Le `min(...,46vw)` garde la colonne sous la
   moitie de l'ecran tout en haut du seuil. */
.art-block{position:relative;--txtw:min(416px,46vw);--half:calc(var(--txtw) / 2);--cap-x:clamp(16px,3vw,48px);--free:calc(50% - var(--half) - var(--cap-x) - 24px)}
.art-block + .art-block{margin-top:clamp(52px,6vw,84px)}
/* l'anneau de clavier est sur la COMMANDE : le cadre de la photo (tabindex/role poses sur lui par
   assets/a2t.js). Decale DEDANS la photo (offset negatif) : la bande fait toute la largeur de
   l'ecran, un anneau exterieur sortirait de la page. */
.art-band:focus-visible{outline:2px solid var(--accent);outline-offset:-8px;border-radius:var(--radius)}
.art-band{position:absolute;inset-block:0;inset-inline:calc(-1 * var(--pad));width:auto;margin:0;border:0;border-radius:0;background:none;overflow:hidden;z-index:1;
  /* le curseur dit que LA PHOTO est la commande ; il n'est declare qu'au-dessus du seuil, ou la
     bascule existe (sous le seuil la photo est un simple visuel en flux). */
  cursor:zoom-in;
  /* au depart l'image est recadrée et legerement agrandie (--zb) : la place prise par cet
     agrandissement est la seule course possible du parallaxe, c'est ce qui garantit qu'il ne
     peut pas ouvrir de vide dans le bloc. Le z-index ne bascule qu'apres le fondu en sortie
     d'etat (le z-index ne s'anime pas, il se decale) ; a l'entree il bascule tout de suite. */
  --zb:1.10; --zh:1.20;
  transition:z-index 0s .5s}
/* le degrade, pose SUR l'image. La colonne etant PLAQUEE D'UN COTE, il n'est plus symetrique mais
   DANS LE SENS DE LA BANDE : transparent sur le bord LIBRE (celui que la photo garde), plein sur
   toute la moitie ou tombe le texte. Son palier se calcule sur la colonne elle-meme : colonne a
   DROITE, plateau des `100% - pad - txtw` depuis le bord gauche ; colonne a GAUCHE, plateau jusqu'a
   `pad + txtw`. Il se construit sur la couleur de fond de la page (--bg-rgb), pas sur une valeur ecrite
   en dur : il se retire proprement en theme clair comme en sombre et rejoint le fond de la section
   sans bord visible.

   SES PALIERS NE SONT PAS DES POURCENTAGES ECRITS A LA MAIN, ils sont calcules SUR LA COLONNE :
   --pad est la gouttiere de la section, --txtw la largeur de la colonne : le texte s'arrete a la
   gouttiere, la bande court jusqu'au bord de l'ecran, donc la colonne commence a --pad du bord
   qu'elle occupe. Voile a 0 jusqu'a 8 vw AVANT la
   colonne, a .93 des les 16 px qui la PRECEDENT, plateau .93 jusqu'au bord. Le plateau DOIT couvrir
   la rive du texte, pas partir de son bord : mesure avec le plateau cale au bord de la colonne, le
   haut du gradient tombait sur les premieres lignes, 4,16:1 – 4,46:1 dans les deux themes.
   Les regles `nth-child` du seuil desktop donnent le palier selon le COTE ; la regle de base
   ci-dessous (paliers au centre) reste le repli d'une page qui ne presenterait pas ses temps en
   freres directs du conteneur : le garde-fou exige 4,5:1 dans les DEUX etats, il ne doit jamais
   tomber sur un etat sans voile. */
.art-band::after{content:"";position:absolute;inset:0;pointer-events:none;opacity:1;transition:opacity .5s ease;background:linear-gradient(90deg,
  rgba(var(--bg-rgb),0) 0,rgba(var(--bg-rgb),0) calc(50% - var(--half) - 8vw),rgba(var(--bg-rgb),.93) calc(50% - var(--half) - 16px),
  rgba(var(--bg-rgb),.93) calc(50% + var(--half) + 16px),rgba(var(--bg-rgb),0) calc(50% + var(--half) + 8vw),rgba(var(--bg-rgb),0) 100%)}
/* a la revelation, n'importe quel bord de la photo appelle la commande : le miroir selon le cote
   n'a plus de raison d'etre. */
/* le rattrapage de l'etat revele n'a plus de raison d'etre : a la revelation la bande passe
   AU-DESSUS des lignes et son degrade s'eteint, ce n'est plus le texte qui se lit par-dessus la
   photo. Un voile ajoute a cet instant ne ferait que blanchir la photo que l'on veut voir entier. */
/* la vitre derriere les paragraphes voisins : le flou d'arriere-plan seul, sans plaque de
   couleur. C'est lui qui porte la lisibilite la ou la photo se devine encore. Elle est posee en
   pseudo sur la colonne de texte, sous les lignes (z 2), donc les lignes ne sont jamais voilees. */
/* Les trois couches sont rangees dans le MEME contexte d'empilement (celui du bloc) : la colonne
   de texte ne doit donc PAS porter de z-index, elle creerait un contexte et ses lignes resteraient
   enfermees dessous. Bande 1, vitre 2, lignes 5 : au repos la photo est derriere la vitre (le flou
   porte bien sur la photo) et les lignes au-dessus. A la revelation, une SEULE couche bouge : la
   bande passe a 7, au-dessus des lignes comme de la vitre. Une seule inversion, pas deux : deux
   inversions simultanees en plein fondu sont ce qui faisait sauter la peinture (le « glitch »). */
/* La boite de texte est pleine hauteur (min-height:100%, voir le carton du dessus) et son contenu
   est centre verticalement. `align-items` et le texte aligne suivent le cote : un bloc plaque a
   droite se lit bord a droite, le marquee de la vitre reste sous les lignes. */
.art-txt{position:relative;width:var(--txtw);margin-inline:auto}
/* Le fond de la vitre est PORTe par backdrop-filter, pas par une couleur : sans le flou,
   la vitre ne signifie rien et il vaut mieux ne rien poser que de poser une plaque. Le filtre
   est ajoute seulement si le navigateur le sait faire (le flou est DECLARE en CSS ici, et lu
   ailleurs) : sur les navigateurs qui ne le connaissent pas (Chrome <76, Firefox <103), le
   paragraphe retrouve un fond plein, mesuree la colonne tombait entre 1,2:1 et 3,4:1 sur la
   photo sans lui. */
.art-txt::before{content:"";position:absolute;z-index:2;inset:-18px -20px 0;border-radius:var(--radius-lg);
  background:none;backdrop-filter:blur(16px) saturate(1.04);-webkit-backdrop-filter:blur(16px) saturate(1.04)}
/* Un flou d'arriere-plan ne se peint que la ou le navigateur le sait faire (Chrome >=76,
   Firefox >=103, Safari >=9). La ou il manque, la vitre ne peut plus rien porter : le paragraphe
   retrouve un fond plein, sinon le texte tombe sur la photo a nuef (meme mesure qu'avant,
   entre 1,2:1 et 3,4:1). */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .art-txt::before{background:rgba(var(--bg-rgb),.86)}
}
/* legende : plaquette dans un coin du bord LIBRE, BRIDEE pour ne jamais atteindre la colonne
   centree. Le cartouche general des cadres est en absolute left:12px/right:12px : sur une bande
   pleine largeur il s'etirerait sous les paragraphes (mesure a 921 px : un rectangle de paragraphe
   contenait la legende). --free = la moitie de la bande — la moitie de la colonne — l'ancre du coin
   (--cap-x) — 24 px de respiration : la plaquette finit TOUJOURS 24 px avant la colonne, quel que
   soit l'ecran. Le cote ne sert plus qu'a choisir le coin. */
.art-band figcaption{width:35vw;min-width:8rem;max-width:var(--free);left:var(--cap-x);right:auto;bottom:clamp(16px,3vh,34px);font-size:10px;letter-spacing:.16em;text-transform:uppercase;padding:8px 12px}
/* les lignes au-dessus de la bande AU REPOS. Elles ne sont plus au-dessus a la revelation : c'est
   la photo qui doit se voir en entier, l'etat est une bascule explicite (second clic, Echap, ou
   sortie du focus rendent le texte). */
.art-txt h2,.art-txt p,.art-txt .note{position:relative;z-index:5}

/* ── 3. etat revele : la photo passe AU-DESSUS du texte ──
   AU REPOS : la photo est en dessous, nette, voile par le degrade seul — elle se lit derriere les
   paragraphes, et son pied descend sous eux.
   CE QUI APPELLE LA REVELATION : LA PHOTO, PAS LE TEMPS. Le survol porte sur .art-band ; la colonne
   et sa vitre sont AU-DESSUS de la bande et captent le pointeur a leur place — donc promener la
   souris sur le paragraphe ne revele RIEN : le texte ne disparait jamais sous la souris. Il faut
   aller sur l'image elle-meme (un bord libre, le haut ou le bas du bloc) ou la cliquer. Le CLIC est
   filtre pareil dans assets/a2t.js : un clic dans la colonne ne bascule rien.
   AU CLIC ou au CLAVIER (la classe .is-seen) : la bande passe au-dessus de TOUT (z 7, devant les
   lignes comme devant la vitre) et son degrade s'eteint (opacity 0) : la photo se voit EN ENTIER.
   Bascule assumee : le texte est cache tant que la photo est revelee, un second clic sur l'image /
   Echap / la sortie du focus le rendent. La legende, elle, ne bouge pas.
   transform + opacite + z-index seulement : aucune mise en page animee, donc aucun saut.
   Sous 921 px la bande n'est plus en arriere-plan : pas de vitre, pas de revelation (regle plus bas). */
.art-band img{width:100%;height:100%;max-height:none;object-fit:cover;object-position:center 45%}
/* Une seule couche traverse le tas a la revelation : la bande, de 1 a 7. La vitre reste a son rang
   (elle n'a plus a « monter » : ce n'est plus elle qui doit proteger le texte d'une photo passee
   devant elle). Deux inversions simultanees en plein fondu, c'etait la seconde source du papillotement. */
/* Le survol qui revele est celui du CADRE, pas du bloc (voir le § 2). La bande quitte son rang a
   l'entree comme a la sortie sans transition : z-index ne s'anime pas. */
.art-band:hover,.art-block.is-seen .art-band{z-index:7;transition:z-index 0s 0s}
.art-band:hover::after,.art-block.is-seen .art-band::after{opacity:0}
.art-txt>*,.art-txt>p,.art-txt>h2{position:relative;z-index:5}
/* a la revelation la bande s'agrandit (--zb -> --zh) et son degrade s'eteint : la photo pleine se
   lit sans masque. Le deplacement de derfilement, lui, CONTINUE : il n'est pas coupe a la bascule,
   donc aucun saut a l'entree ni a la sortie (couper --bx aurait fait glisser la photo de toute sa
   course d'un coup). Ce qui ne se marche plus dessus, c'est la transition : seule `scale` est
   transitionnee, `translate` ne l'est jamais. */
.art-band:hover img,.art-block.is-seen .art-band img{scale:var(--zh)}
/* le zoom au survol des cadres (regle .photo-frame:hover img, transform:scale(1.04)) est REMPLACE
   ici, il ne s'ajoute pas : la bande est un .photo-frame, et sans cette remise a zero les deux
   agrandissements se COMPOSENT (transform 1.04 x la propriete scale 1.2 = 1.25 mesure, au lieu de
   --zh) et le second se transitionne en 600 ms par-dessus celui de 500 ms. Sur une bande, le
   mouvement de survol EST la revelation (--zh), pas un second agrandissement. */
.art-band:hover img{transform:none}
/* LE COTE : temps IMPAIR a DROITE, temps PAIR a GAUCHE. `nth-child` compte les blocs dans leur
   conteneur : aucune classe a porter dans le HTML, donc rien a oublier quand un temps s'ajoute ou se
   retire. Une seule origine pour trois choses qui doivent tourner ensemble : le cote de la colonne,
   l'alignement du texte, le coin de la legende et le sens du degrade. Tout est enferme dans le seuil
   desktop : sous le seuil, colonne pleine largeur, donc ni cote ni sens. */
@media (min-width:921px){
  .art-block{padding-bottom:0}
  /* MEME HAUTEUR QUE LA PHOTO, CONTENU CENTRE VERTICALEMENT. La bande est en absolu et court
     d'un bout a l'autre du bloc (inset-block:0) : sa hauteur EST la hauteur du bloc, donc la hauteur
     de la colonne. Sans padding bas, les deux boites ont exactement la meme hauteur. Une min-height
     en pixels (pas en %, un pourcentage sur un bloc dont la hauteur vient du contenu ne se resout
     pas) empeche un temps court d'ecraser la photo : le bloc ne descend jamais sous cette hauteur, la
     colonne s'y etend et le flex centre ses lignes dedans. */
  .art-txt{min-height:clamp(400px,30vw,560px);display:flex;flex-direction:column;justify-content:center}
  /* la marge basse du dernier paragraphe entrerait dans le bloc centre : le contenu paraitrait
     monte (mesure a 1440 px : 44 px au-dessus de la premiere ligne, 68 px sous la derniere). */
  .art-txt>p:last-child{margin-bottom:0}
  .art-block:nth-child(odd) .art-txt{margin-left:auto;margin-right:0;padding-left:3rem;text-align:right}
  .art-block:nth-child(even) .art-txt{margin-left:0;margin-right:auto;padding-right:3rem;text-align:left}
  /* le paragraphe reste justifie (choix de la feuille, avec coupures) : ce qui doit suivre le cote
     est sa DERNIERE ligne, sans quoi un temps a droite finit en queue de ligne a gauche. */
  .art-block:nth-child(odd) .art-txt p{text-align-last:right}
  .art-block:nth-child(even) .art-txt p{text-align-last:left}
  /* la vitre ne borde plus que la boite de texte : la boite FAIT la hauteur de la photo, un debord
     vertical irait noircir la photo au-dessus et au-dessous du texte centre. */
  .art-txt::before{inset:0}
  /* la legende se place sur le bord LIBRE, celui que le texte ne prend pas : un temps a droite a sa
     plaquette a gauche. Sa bride suit : la place libre fait toute la largeur moins la colonne, la
     gouttiere, l'ancre du coin et 24 px de respiration. */
  .art-band figcaption{max-width:calc(100% - var(--pad) - var(--txtw) - var(--cap-x) - 24px)}
  .art-block:nth-child(odd) .art-band figcaption{left:var(--cap-x);right:auto}
  .art-block:nth-child(even) .art-band figcaption{left:auto;right:var(--cap-x)}
  /* le degrade suit le cote : transparent sur le bord LIBRE, plateau derriere la colonne. --pad est
     la gouttiere de la section : le texte s'arrete a la gouttiere, la bande court jusqu'au bord de
     l'ecran, donc la colonne commence a --pad du bord qu'elle occupe. */
  .art-block:nth-child(odd) .art-band::after{background:linear-gradient(90deg,rgba(var(--bg-rgb),0) 0,rgba(var(--bg-rgb),0) calc(100% - var(--txtw) - var(--pad) - 8vw),rgba(var(--bg-rgb),.93) calc(100% - var(--txtw) - var(--pad) - 16px),rgba(var(--bg-rgb),.93) 100%)}
  .art-block:nth-child(even) .art-band::after{background:linear-gradient(90deg,rgba(var(--bg-rgb),.93) 0,rgba(var(--bg-rgb),.93) calc(var(--txtw) + var(--pad) + 16px),rgba(var(--bg-rgb),0) calc(var(--txtw) + var(--pad) + 8vw),rgba(var(--bg-rgb),0) 100%)}
  .art-block.is-seen .art-band{cursor:zoom-out}
  /* les deux couches qui bougent sont promues en couches de composition, desktop seulement (le
     parallaxe et la vitre n'existent pas sous le seuil, et sur mobile ces couches coutent cher en
     memoire) : sans cette annonce, l'image qui defile est repeinte a chaque image dans la couche du
     bloc, avec le degrade pose dessus et le flou d'arriere-plan de la vitre par-dessus. C'etait la
     premiere source du « glitch ». Le cadre, lui, n'est pas declare : rien ne le transforme, et son
     degrade se retrouve ainsi sur la couche du bloc, pas repeinte quand la couche de l'image bouge. */
  .art-band img{will-change:translate,scale}
  .art-txt::before{will-change:backdrop-filter}
/* Les deux mouvements sont poses sur DEUX PROPRIETES INDEPENDANTES : `translate` porte le
   deplacement de derfilement (mis a jour a chaque image par le script, JAMAIS transitionne),
   `scale` porte le zoom de la revelation (transitionne en 500 ms). Sur une seule propriete
   transform, chaque image de derfilement relancait la transition de 500 ms : la photo courait
   apres sa cible et papillotait derriere la vitre — le « glitch » releve. Separes, les deux ne
   se marchent plus dessus. */
  .art-band img{translate:0 var(--bx,0px);scale:var(--zb);transition:scale .5s cubic-bezier(.22,.7,.2,1)}
}

/* le seuil est a 920 px, pas 921 : `min-width:921px` et `max-width:921px` se recouvraient a
   exactement 921 px, ou l'on obtenait les deux a la fois (bande en flux ET padding bas, zoom,
   couches de composition declarees). Le script, lui, bascule a min-width:921px. */
@media (max-width:920px){
  /* sous le seuil, la bande redevient un visuel en flux, pleine largeur, legende epinglee dessus :
     le texte justifie garde tout l'ecran sur son fond plein. Ni vitre ni revelation : en colonne
     unique il n'y a pas de voisin a survoler, et un flou derriere du texte pleine largeur ne sert
     a rien. Le degrade devient vertical, pour la legende. */
  .art-block + .art-block{margin-top:40px}
  .art-block{padding-bottom:0}
  /* relative, pas static : le cartouche de la legende est en absolute. Laisser la bande hors du
     flux de positionnement la rattacherait au BLOC, et la plaquette descendrait par-dessus les
     paragraphes du temps (mesure a 390 px : 117 x 137 px de legende sur le 2e paragraphe). */
  /* le cadre reprend sa place dans le flux, pleine colonne lui aussi, avec une hauteur explicite :
     object-fit:cover a besoin d'un conteneur a remplir — sans hauteur, il retomberait sur la
     hauteur naturelle de l'image et ne « couvrirait » rien. 16/9 en dessous de 640 px, 2/1 au-dela :
     le cadrage reste large, la photo est recoupee et non deformee. */
  .art-band{position:relative;inset:auto;width:auto;aspect-ratio:16/9;min-height:0;border:0;border-radius:var(--radius-lg);z-index:auto;will-change:auto;cursor:auto}
  /* meme specificite que `.art .art-band{margin:0}` (regle du dessus, des 1222) : ecrite ici, sous
     le seuil elle reprend sa marge basse — 18 px entre la photo et les paragraphes. */
  .art .art-band{margin:0 0 18px}
  @media (min-width:640px){.art-band{aspect-ratio:2/1}}
  .art-band img{width:100%;height:100%;object-fit:cover;translate:none;scale:none;transform:none;filter:none;will-change:auto}
  .art-txt::before{will-change:auto}
  /* sous le seuil il n'y a PAS d'etat revele, meme a la souris sur un petit ecran : le cadre est en
     flux, le zoom de la releve ne ferait que grossir une photo deja pleine largeur. */
  .art-band:hover,.art-block:hover .art-band,.art-block.is-seen .art-band{z-index:auto}
  .art-band:hover::after,.art-block:hover .art-band::after,.art-block.is-seen .art-band::after{opacity:1}
  .art-band:hover img,.art-block:hover .art-band img,.art-block.is-seen .art-band img{scale:none}
  .art-band::after,.art-block:nth-child(odd) .art-band::after,.art-block:nth-child(even) .art-band::after{background:linear-gradient(180deg,rgba(var(--bg-rgb),0) 55%,rgba(var(--bg-rgb),.5) 78%,rgba(var(--bg-rgb),.85) 100%);opacity:1}
  /* le cartouche des blocs a droite est ancre a droite et bride en max-content par la feuille,
     une regle plus specifique que celle du simple .art-band : elle doit etre reprise ici aussi,
     sinon la plaquette reste etroite et haute sur la photo (mesure a 390 px : 117 x 137 px). */
  .art-band figcaption,.art-block:nth-child(odd) .art-band figcaption,.art-block:nth-child(even) .art-band figcaption{left:12px;right:12px;bottom:12px;width:auto;max-width:none;font-size:11px;letter-spacing:.04em;text-transform:none}
  .art-txt,.art-block:nth-child(odd) .art-txt,.art-block:nth-child(even) .art-txt{width:auto;margin:0;min-height:0;display:block;text-align:left}
  .art-txt p,.art-block:nth-child(odd) .art-txt p,.art-block:nth-child(even) .art-txt p{text-align:justify;text-align-last:auto}
  /* la bride de ligne revient a 44 caracteres sous le seuil : --txtw est la colonne CENTREE sur la
     photo, il n'a plus de sens en colonne pleine largeur (a 700 px il laisserait 172 px de blanc a
     droite du paragraphe, a 390 px il ne bridait rien). */
  .art p{max-width:44ch}
  .art-txt::before{display:none}
}

/* ── mouvement reduit, fin du montage des bandes : la bascule reste, le mouvement tombe ──
   Ce bloc est ecrit APRES toutes les regles des bandes (y compris celles des media queries) : a
   specificite egale, c'est la derniere ecrite qui gagne, et le garde-fou a mesure que translate
   (0px) et scale (1.1) survivaient au bloc general du haut de la feuille. En mouvement reduit la
   revelation reste une bascule d'etat — la bande passe devant le texte, le degrade s'eteint — mais
   l'image ne bouge plus : ni course de derfilement, ni zoom. */
@media (prefers-reduced-motion:reduce){
  .art-band{will-change:auto}
  .art-band img{translate:none;scale:none;transform:none;filter:none;will-change:auto}
  .art-band:hover img,.art-block:hover .art-band img,.art-block.is-seen .art-band img{scale:none}
  .art-txt::before{will-change:auto}
  .art-band,.art-band img,.art-txt::before,.art-band::after{transition:none}
}

/* ── 3. carrousel des images de l'actualité ── */
.gallery{margin-top:36px;padding-top:26px;border-top:1px solid var(--border)}
.gallery-head{display:flex;gap:16px;align-items:baseline;flex-wrap:wrap;margin-bottom:18px}
.gallery-count{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;color:var(--fg-3);margin-left:auto}
/* le rail est une rangee qui se fait glisser : les largeurs sont des pourcentages de SA
   largeur, bornees en pixels. Mesure : une largeur en vw (ou en pixels secs) taillait le rail
   plus large que le carrousel (390 px de rail dans 328 px de carrousel) et le bord du cadre
   suivant disparaissait — rien ne disait qu'il y avait une suite. Un pourcentage du rail regle
   les deux d'un coup : a 550 px de rail il retrouve exactement les deux largeurs validées de la
   feuille (84 % = 462, 58 % = 319), a 328 px de rail il laisse 37 px du cadre suivant visibles,
   et le rail ne depasse jamais sa propre largeur. */
.gal{display:flex;gap:16px;overflow-x:auto;scroll-snap-type:x mandatory;
  padding-bottom:8px;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain}
.gal > figure{flex:0 0 auto;scroll-snap-align:start;margin:0;max-width:100%}
/* chaque cadre garde son ratio (attributs width/height du HTML) et ne depasse
   jamais la hauteur utile ; .tall ne sert qu'au portrait de la feuille */
.gal figure{width:min(84%,460px)}
.gal figure.tall{width:min(58%,320px)}
.gal figure.tall img{max-height:min(56vh,440px)}
.gal-nav{display:flex;gap:8px;align-items:center;margin-top:12px}
.gal-btn{display:inline-flex;align-items:center;justify-content:center;width:44px;height:44px;padding:0;
  border:1px solid var(--border-2);border-radius:var(--radius);background:var(--surface);color:var(--muted);
  cursor:pointer;transition:background .2s ease,border-color .2s ease,color .2s ease}
.gal-btn:hover{color:var(--fg);border-color:var(--fg-3);background:var(--inset)}
.gal-btn:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.gal-btn svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.gal-btn[disabled]{opacity:.35;cursor:default}
.gal-hint{font-family:var(--font-mono);font-size:11px;letter-spacing:.06em;color:var(--fg-3);margin-left:8px}
/* tout tient sans défilement : les boutons n'ont rien à faire (le script pose
   .a2t-fit ; sans script la rangée défile à la main, les boutons restent actifs) */
.gallery.a2t-fit .gal-nav{display:none}

/* ── 4. liens du post sur les réseaux ── */
.post-links{display:flex;flex-wrap:wrap;gap:12px;margin-top:32px}
.plink{display:inline-flex;align-items:center;gap:10px;min-height:44px;padding:12px 18px;border-radius:var(--radius);
  border:1px solid var(--border-2);background:var(--surface);color:var(--btn-fg);font:600 14px/1.35 inherit;
  text-decoration:none;cursor:pointer;transition:background .2s ease,border-color .2s ease,color .2s ease}
/* un bouton .plink prend la police du site, pas celle par défaut du bouton */
button.plink{font-family:var(--font-body)}
.plink:hover{background:var(--inset);border-color:var(--fg-3);color:var(--fg)}
.plink:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.plink svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
/* le dessin du glyphe Instagram reste un contour ; le carré plein de Facebook est
   un aplat, pas un second accent : il prend l'encre du texte */
.plink.pl-on{background:var(--inset);border-color:var(--border-2);color:var(--fg)}
.plink[disabled]{opacity:.45;cursor:default}
.plink[disabled]:hover{background:var(--surface);border-color:var(--border-2);color:var(--btn-fg)}
.plink .pl-note{font-family:var(--font-mono);font-size:10.5px;letter-spacing:.1em;color:var(--fg-3);font-weight:500}
@media (max-width:560px){
  .plink{flex:1 1 calc(50% - 6px);justify-content:center;padding:12px 10px}
  .plink .pl-note{display:none}
}

/* parite cutover atomique: widgets html embarques sans marge kit de 20px; main garde max-width:none */
.elementor-13>.elementor-widget:not(:last-child){margin-block-end:0}
.elementor-13>main.a2t-main{max-width:none}
.elementor-229>.elementor-widget:not(:last-child){margin-block-end:0}
.elementor-229>main.a2t-main{max-width:none}
.elementor .lead.also a{text-decoration:underline}
/* reparation atomique batch-2 : les jetons locaux (0,2,0) ecrasent les media a2t (<=0,1,0) car le CSS genere passe APRES a2t.css. Regles a (0,3,0) MINIMUM. */
@media (max-width:920px){.elementor .steps .step,.elementor .steps .step:first-child{padding:0;border-left:0}}
@media (max-width:920px){.elementor .steps.steps{min-height:0}}
@media (min-width:921px){.elementor .steps.steps{padding-block-start:32px}}
.elementor .zones a{width:auto}
.elementor .zones>a{position:static}
.elementor .zones>a>span{padding:0;border:0}
.elementor .steps>.step:first-child{padding-inline-start:0px}
@media (min-width:921px){@supports (width:100cqi){.elementor .steps.act{display:flex}}}
/* reparation atomique batch-3 (topnav/footer/contact) */
.elementor .logo .logo-sub{display:block;font-family:var(--font-mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--fg-3);font-weight:400}
@media (max-width:560px){.elementor .logo .logo-sub{display:none}}
.elementor .theme-btn .e-svg-base{width:20px;height:20px;flex:0 0 auto}
.elementor .diag-cta .e-svg-base{width:22px;height:22px;flex:0 0 auto}
.elementor .nav-burger .e-svg-base{width:20px;height:20px;flex:0 0 auto}
.elementor .foot-grid>div{display:block;position:static;padding:0;margin:0;width:auto;border-radius:0}
@container (min-width:540px){.elementor .actions>.btn-primary:has(+ .diag-cta){border-radius:var(--radius) 0 0 var(--radius)}.elementor .actions>.diag-cta{margin-left:-14px;border-radius:0 var(--radius) var(--radius) 0}}
