@layer socle, modele;
@layer socle {
/* Système de design du squelette — produit par Skwik.
   Couple typographique : lecture (polices embarquées).
   Mise en page : dense.
   Le site doit inspirer confiance et proximité : une entreprise locale de plomberie-chauffage qui maîtrise son métier. La palette ardoise (gris froid, bleu d'encre) apporte la compétence technique, l'accent brun chaud rappelle le côté humain et artisanal. La mise en page dense permet de montrer rapidement l'étendue des services et des informations pratiques, tout en gardant une lecture confortable grâce à la typographie 'lecture' (serif pour les titres, sans pour le texte). L'ensemble est sobre, moderne et accessible, sans effets superflus. */

@font-face {
    font-family: 'Merriweather';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('fonts/merriweather-400-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Merriweather';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('fonts/merriweather-400-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Merriweather';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('fonts/merriweather-700-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Merriweather';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('fonts/merriweather-700-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Source Sans 3';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('fonts/source-sans-3-400-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Source Sans 3';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('fonts/source-sans-3-400-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Source Sans 3';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('fonts/source-sans-3-700-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Source Sans 3';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('fonts/source-sans-3-700-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
    --fond: #f5f7f9;
    --surface: #e9edf2;
    --texte: #14181d;
    --texte-faible: #4e5866;
    --primaire: #2b4c7e;
    --accent: #7a4b2a;
    --bordure: #d3dae2;
    --primaire-appui: #264169;
    --accent-appui: #644027;
    --primaire-texte: #2b4c7e;
    --primaire-texte-appui: #264169;
    --primaire-voile: #e5e9ef;
    --primaire-voile-fort: #d5dce5;
    --accent-voile: #e9e6e4;
    --surface-appui: #dfe3e8;
    --surface-creuse: #eceef0;
    --bordure-douce: #e6eaef;
    --bordure-forte: #90969d;
    --sur-primaire: #f5f7f9;
    --sur-accent: #f5f7f9;
    --police-titres: 'Merriweather', Georgia, 'Times New Roman', Times, serif;
    --police-corps: 'Source Sans 3', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    --corps: 1.000rem;
    --h1: clamp(2.100rem, 1.500rem + 3.000vw, 4.200rem);
    --h2: clamp(1.600rem, 1.314rem + 1.429vw, 2.600rem);
    --h3: clamp(1.200rem, 1.114rem + 0.429vw, 1.500rem);
    --petit: clamp(0.820rem, 0.804rem + 0.079vw, 0.875rem);
    --surtitre: clamp(0.720rem, 0.703rem + 0.086vw, 0.780rem);
    --poids-corps: 400;
    --poids-appui: 700;
    --poids-titre: 700;
    --lettrage-titre: -0.008em;
    --lettrage-surtitre: 0.1em;
    --interligne-corps: 1.65;
    --interligne-titre: 1.15;
    --interligne-h1: 1.05;
    --mesure: 58ch;
    --mesure-courte: 48ch;
    --espace-3xs: 0.375rem;
    --espace-2xs: 0.563rem;
    --espace-xs: 0.750rem;
    --espace-s: clamp(1.012rem, 0.980rem + 0.161vw, 1.125rem);
    --espace-m: clamp(1.125rem, 1.018rem + 0.536vw, 1.500rem);
    --espace-l: clamp(1.575rem, 1.382rem + 0.964vw, 2.250rem);
    --espace-xl: clamp(1.860rem, 1.534rem + 1.629vw, 3.000rem);
    --espace-2xl: clamp(2.340rem, 1.723rem + 3.086vw, 4.500rem);
    --espace-3xl: clamp(2.700rem, 1.757rem + 4.714vw, 6.000rem);
    --espace-4xl: clamp(3.420rem, 1.826rem + 7.971vw, 9.000rem);
    --espace: clamp(1.125rem, 1.018rem + 0.536vw, 1.500rem);
    --rayon-xs: 4px;
    --rayon: 8px;
    --rayon-l: 14px;
    --rayon-xl: 22px;
    --rayon-rond: 999px;
    --ombre-1: 0 1px 2px rgba(20, 24, 29, 0.05), 0 1px 3px rgba(20, 24, 29, 0.08);
    --ombre-2: 0 2px 4px rgba(20, 24, 29, 0.04), 0 10px 24px rgba(20, 24, 29, 0.09);
    --ombre-3: 0 6px 12px rgba(20, 24, 29, 0.05), 0 24px 56px rgba(20, 24, 29, 0.13);
    --duree-vive: 120ms;
    --duree: 220ms;
    --duree-ample: 420ms;
    --courbe: cubic-bezier(0.22, 0.7, 0.3, 1);
    --plan-decor: 0;
    --plan-media: 1;
    --plan-voile: 2;
    --plan-contenu: 3;
    --plan-flottant: 10;
    --plan-entete: 100;
    --voile: #14181d;
    --voile-opacite: 0.72;
    --sur-voile: #f5f7f9;
    --largeur-site: 80rem;
    --rythme: 2;
    --grille-min: 13rem;
    --galerie-min: 9rem;
    --titre-echelle: .85;
    --largeur: var(--largeur-site);
}

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

/* --- transitions entre pages, sans une ligne de script ---------------------
   Une maquette se parcourt : on clique « Ateliers », la page blanchit, puis
   réapparaît. Ce clignotement est ce qui la fait ressembler à un site de 2010,
   et c'est ce qui a été signalé à l'usage le 2026-08-03.

   La règle @view-transition demande au navigateur d'animer la navigation ENTRE DEUX
   DOCUMENTS. Il fait le travail lui-même : capture de l'ancienne page,
   fondu vers la nouvelle. Aucun script, donc rien qui contrevienne à ADR-020,
   et rien à réimplémenter — le clavier, le bouton « précédent » et le
   chargement direct d'une URL continuent de fonctionner.

   L'en-tête porte son propre nom de transition : il reste EN PLACE pendant que
   le contenu se croise, au lieu de disparaître avec la page. C'est ce détail
   qui fait la différence entre « une page se recharge » et « on navigue ».

   Là où le navigateur ne sait pas le faire, il n'y a simplement pas
   d'animation : la navigation reste celle d'avant. */
@view-transition { navigation: auto; }

header { view-transition-name: entete; }
main { view-transition-name: contenu; }

::view-transition-old(contenu) {
    animation: sortie-page var(--duree-vive) var(--courbe) both;
}
::view-transition-new(contenu) {
    animation: entree-page var(--duree) var(--courbe) both;
}
@keyframes sortie-page { to { opacity: 0; transform: translateY(-8px); } }
@keyframes entree-page { from { opacity: 0; transform: translateY(12px); } }
/* --- le rognage horizontal, et pourquoi il faut **deux** règles -------------
   Défaut mesuré le 2026-08-03, invisible jusque-là faute de navigateur : toutes
   les compositions pleine largeur emploient « margin-inline: calc(50% - 50vw) ».
   Or « 50% » se calcule sur la colonne — sans la barre de défilement — et
   « 50vw » sur la fenêtre — barre comprise. L'écart vaut la moitié d'une barre,
   et **chaque page de chaque maquette portait une barre de défilement
   horizontale** de 8 px.

   Quatre écritures mesurées sur la page réelle, trois sont des pièges :

     html { overflow-x: clip }               défile encore — sans effet
     body { overflow-x: clip }   seul        défile encore — sans effet
     body { overflow-x: hidden } + html      ne défile plus, **casse le collant**
     html + body { overflow-x: clip }        ne défile plus, en-tête intact

   La raison tient en une règle du modèle de mise en forme : tant que la racine
   est en « visible », **le débordement du corps est propagé à la fenêtre** et
   ne s'applique pas au corps. Poser la règle sur la racine sert donc uniquement
   à couper cette propagation ; c'est celle sur le corps qui rogne. Et « hidden »
   ferait du corps un conteneur de défilement, ce qui redéfinit ce à quoi
   « position: sticky » s'accroche — l'en-tête cesserait de suivre. Voir G-23. */
html { scroll-behavior: smooth; overflow-x: clip; }
body { overflow-x: clip; }
body {
    margin: 0;
    background: var(--fond);
    color: var(--texte);
    font: var(--poids-corps) var(--corps)/var(--interligne-corps) var(--police-corps);
    -webkit-font-smoothing: antialiased;
}
/* Les titres portent trois choses que la feuille ne disait pas : leur approche,
   qui appartient au dessin de la police ; leur interligne, qui doit se resserrer
   quand le corps grossit ; et « text-wrap: balance », qui empêche le dernier mot
   d'un titre de trois mots de tomber seul sur sa ligne. Ce dernier point est le
   défaut le plus visible d'une maquette, et le moins cher à corriger. */
h1, h2, h3 {
    font-family: var(--police-titres); font-weight: var(--poids-titre);
    line-height: var(--interligne-titre); letter-spacing: var(--lettrage-titre);
    text-wrap: balance; margin: 0 0 .45em;
}
h1 { font-size: var(--h1); line-height: var(--interligne-h1); }
h2 { font-size: var(--h2); }
/* Un h3 est trop petit pour qu'un serrage se voie autrement que comme un défaut. */
h3 { font-size: var(--h3); letter-spacing: normal; }
p { text-wrap: pretty; }
a { color: var(--primaire-texte); text-underline-offset: .18em; text-decoration-thickness: .07em;
    transition: color var(--duree-vive) var(--courbe); }
a:hover { color: var(--primaire-texte-appui); }

/* La bannière dit ce qu'est cette page. Elle le disait en tête, sur toute la
   largeur et en couleur d'accent : c'était la **première chose vue du site**, et
   elle annonçait « maquette » avant que la maquette ait rien montré.
   Elle est désormais posée en bas, discrète et permanente — donc plus honnête
   qu'un bandeau qu'on dépasse au premier défilement et qu'on ne revoit jamais. */
.banniere {
    position: fixed; z-index: var(--plan-flottant);
    left: var(--espace-s); bottom: var(--espace-s);
    max-width: min(32rem, calc(100vw - var(--espace-l)));
    margin: 0; padding: var(--espace-2xs) var(--espace-s);
    border: 1px solid var(--bordure-douce); border-radius: var(--rayon-rond);
    background: color-mix(in srgb, var(--surface) 88%, transparent);
    backdrop-filter: blur(10px);
    box-shadow: var(--ombre-2);
    color: var(--texte-faible); font-size: var(--surtitre); line-height: 1.45;
}

/* L'en-tête **suit le défilement**. C'est ce que fait tout site de production
   depuis dix ans, et son absence se remarque immédiatement : sur une page de
   deux mille pixels, la navigation disparaissait au premier écran. */
header {
    position: sticky; top: 0; z-index: var(--plan-entete);
    background: color-mix(in srgb, var(--fond) 82%, transparent);
    backdrop-filter: blur(14px) saturate(140%);
    border-bottom: 1px solid var(--bordure-douce);
}
/* La largeur du **site**, pas celle de la page. Les modes resserrent la colonne
   de lecture — 46 rem en « campagne » — et l'en-tête la suivait : la marque
   passait sur sa propre ligne, la navigation sur deux, le bouton sur une
   troisième, le tout entassé au tiers gauche d'un écran de 1 440 px. Vu à la
   première capture, invisible à la lecture du CSS. */
/* **Une seule rangée, et c'est une règle de forme, pas un goût.** L'en-tête se
   repliait sur cinq rangées et 181 px de haut : marque, puis huit rubriques sur
   deux lignes, puis le bouton, puis la puce du document. Un en-tête
   contemporain tient sur une ligne de 64 a 80 px — mesure du 2026-08-05, G-49.

   « nowrap » sur le conteneur, « min-width: 0 » sur la navigation : sans le
   second, un enfant flex refuse de retrecir sous sa largeur de contenu et le
   « nowrap » deborde au lieu de comprimer. Le repli etroit, lui, rend le
   defilement horizontal a la navigation seule — deja pose plus bas. */
/* **L'en-tete est plus large que la colonne de lecture, et c'est voulu.** Elle
   suivait « --largeur-site » : sur un cadre de 68 rem, la navigation reclamait
   975 px et n'en recevait que 611 une fois la marque et l'appel a l'action
   servis — le menu se coupait en plein mot. Aucun reglage de taille ne comble
   364 px.

   Une barre plus large que le texte qu'elle surplombe est le patron courant :
   la lecture se borne, la navigation se deploie. Le « max » garde le cas des
   archetypes deja larges — « dense » a 80 rem ne retrecit pas. */
.entete {
    max-width: max(var(--largeur-site), min(88rem, 100%));
    margin: 0 auto;
    display: flex; flex-wrap: nowrap; align-items: center; gap: var(--espace-m);
    padding: var(--espace-s) max(var(--espace), env(safe-area-inset-left));
}
/* La marque ne se comprime pas : sous « nowrap », elle sortait sur deux lignes
   — « La » puis « Metairie » —, ce qu'un nom de marque ne doit jamais faire.
   L'appel a l'action non plus, sans quoi il defile hors du cadre avec la
   navigation. */
.entete > .marque { flex: none; white-space: nowrap; }
.entete > .nav-action { flex: none; }
/* Un lien de navigation se pose **sous** le corps de texte, en taille et en
   espacement : à la taille du corps et à un pas d'espacement plein, sept
   rubriques ne tenaient pas sur 72 rem et l'en-tête se repliait sur trois
   lignes. Mesuré à la capture, pas déduit du CSS. */
nav[aria-label="Navigation principale"] {
    display: flex; flex-wrap: nowrap; align-items: center;
    gap: var(--espace-2xs) var(--espace-s); margin-left: auto;
    min-width: 0; overflow-x: auto; scrollbar-width: none;
    font-size: var(--petit); white-space: nowrap;
}
nav[aria-label="Navigation principale"]::-webkit-scrollbar { display: none; }
/* **Aucun masque de degrade sur la navigation.** Il y en a eu un, pose pour dire
   qu'un menu trop long defile — et il estompait les derniers 2,5 rem *en toutes
   circonstances*, y compris quand rien ne debordait. Le commentaire qui
   l'accompagnait affirmait qu'il « s'efface quand tout tient » : c'etait faux,
   et le CSS n'a aucun moyen de detecter un debordement. Mesure : contenu de la
   navigation 871 px pour 871 px visibles — elle ne debordait pas, et le dernier
   lien paraissait pourtant tranche. Signale a l'usage.

   Un commentaire qui decrit un comportement que le code n'a pas est pire qu'un
   commentaire absent. */
/* **Un lien de menu n'est pas en gras.** Le socle posait « poids-appui » — 700 —
   sur toute la navigation : huit rubriques en gras se lisent comme huit appels a
   l'action, et c'est l'un des trois signaux qui datait l'en-tete. Le gras reste
   au seul lien promu, qui EST un appel a l'action. */
nav a { font-weight: var(--poids-corps); }
.nav-action { font-weight: var(--poids-appui); }
nav[aria-label="Navigation principale"] a {
    position: relative; color: var(--texte); padding-block: var(--espace-3xs);
    transition: color var(--duree-vive) var(--courbe);
}
/* Le trait se déploie depuis la gauche. Il dit « ce lien répond » sans déplacer
   une seule ligne de texte, ce qu'un soulignement apparaissant au survol fait. */
nav[aria-label="Navigation principale"] a::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
    border-radius: 2px; background: var(--primaire);
    transform: scaleX(0); transform-origin: left;
    transition: transform var(--duree) var(--courbe);
}
nav[aria-label="Navigation principale"] a:hover { color: var(--primaire-texte); }
nav[aria-label="Navigation principale"] a[aria-current="page"] { color: var(--primaire-texte); }
nav[aria-label="Navigation principale"] a:hover::after,
nav[aria-label="Navigation principale"] a[aria-current="page"]::after { transform: scaleX(1); }
/* Le dernier lien de la navigation **est** l'appel à l'action : il en prend
   l'aspect sans cesser d'être un lien, donc sans se disputer la largeur avec
   un bouton posé à côté. Le trait de survol ne s'y applique pas. */
.nav-action { padding: .45em 1.05em; border-radius: var(--rayon-l);
    background: var(--primaire); color: var(--sur-primaire) !important;
    box-shadow: var(--ombre-1);
    transition: background var(--duree-vive) var(--courbe),
        box-shadow var(--duree) var(--courbe); }
.nav-action::after { display: none; }
.nav-action:hover { background: var(--primaire-appui); box-shadow: var(--ombre-2); }

/* L'en-tête étant collant, une ancre atterrissait **dessous**. Personne ne le
   voit en relisant le CSS, tout le monde le voit en cliquant un lien interne. */
[id] { scroll-margin-top: 5.5rem; }

/* Largeur, rythme vertical et densité viennent de l'archétype : c'est par eux
   que deux variantes du même site cessent de se ressembler. */
/* **Le retrait d'un debord pleine largeur se mesure sur la colonne reelle.**
   Deux versions fausses avant celle-ci, et elles se corrigent l'une l'autre :

   1. « calc(50vw - --largeur/2) » place le bord de « main », pas celui de son
      contenu — « main » porte son propre padding, et toute section pleine
      largeur tombait --espace trop a gauche. Mesure : 73 px contre 89 px, un
      decrochement de 16 px entre deux sections successives.
   2. Y ajouter --espace corrige ce cas et pas le suivant : « --largeur » est un
      jeton, et une feuille de modele qui redefinit la largeur ou le padding de
      « main » desaccorde de nouveau les deux. Mesure sur l'accueil : colonne a
      313 px, debords a 89.

   La formule ci-dessous ne cite aucun jeton. Apres « margin-inline:
   calc(50% - 50vw) » la section fait 100vw, mais un pourcentage de padding se
   resout toujours sur le **bloc conteneur** — le contenu de « main ». « 50% »
   vaut donc la moitie de la colonne, quelle qu'elle soit, et « 50vw - 50% » est
   exactement la distance du bord de la fenetre au bord de la colonne. Le retrait
   suit la colonne sans que personne ait a les tenir accordes — G-38. */
:root { --retrait-colonne: max(var(--espace), calc(50vw - 50%)); }
main { max-width: var(--largeur); margin: 0 auto; padding: calc(var(--espace) * 2) var(--espace);
    /* Marges de zone sûre : sur un téléphone à encoche, le contenu passait
       sous l'arrondi de l'écran en orientation paysage — §8. */
    padding-left: max(var(--espace), env(safe-area-inset-left));
    padding-right: max(var(--espace), env(safe-area-inset-right)); }
/* Le rythme vertical se dit en padding, pas en marge — 2026-08-04, G-41.
   Il etait pose ici en margin-bottom, et le modele pose le sien en
   padding-block sur les classes de section. Deux proprietes differentes : elles
   s'ADDITIONNENT au lieu de se substituer. Mesure sur l'accueil de metairie —
   72 px de padding bas, 96 px de marge, 72 px de padding haut, soit 240 px de
   vide entre deux sections, et une section « Autres contenus » faite de 144 px
   de padding pour 98 px de contenu.
   Sur la meme propriete, la couche du modele remplace la notre au lieu de s'y
   ajouter. La moitie de la valeur par section rend exactement le meme ecart
   qu'avant sur une page deterministe — les maquettes sans feuille de modele ne
   bougent pas d'un pixel. */
section { padding-block: calc(var(--espace) * var(--rythme) / 2); }
/* **L'approche d'un titre se resserre avec sa taille.** La valeur du couple est
   celle du dessin — propriete de la police, pas du site —, mais elle est calibree
   pour un titre courant : a 49 px, le -0,008 em de Merriweather ne se voit pas,
   la ou le display contemporain se serre a -0,02 / -0,04 em. Mesure le
   2026-08-05.

   On multiplie donc l'approche de la famille au lieu d'en poser une nouvelle :
   une didone qui declare 0 em garde son air — multiplier zero rend zero, et
   c'est exactement ce que Cormorant Garamond demande. Le max() borne le serrage
   pour les familles deja serrees : Inter declare -0,022 em, que 2,5 porterait a
   -0,055 em, illisible. */
h1 { font-size: calc(var(--h1) * var(--titre-echelle));
    letter-spacing: max(-0.04em, calc(var(--lettrage-titre) * 2.5)); }
h2 { font-size: calc(var(--h2) * var(--titre-echelle));
    letter-spacing: max(-0.03em, calc(var(--lettrage-titre) * 1.6)); }
.fil { font-size: var(--petit); color: var(--texte-faible); }
.fil a { color: var(--texte-faible); }
.fil a:hover { color: var(--primaire-texte); }
/* La ligne reste lisible quelle que soit la largeur du cadre : au-delà d'une
   soixantaine de caractères, l'œil perd le début de la ligne suivante. */
.chapeau { font-size: calc(var(--corps) * 1.2); line-height: 1.5;
    color: var(--texte-faible); max-width: var(--mesure); }
p { max-width: min(var(--mesure), 100%); }

/* Les images sont **cadrées par le CSS**, jamais redimensionnées à la
   génération : recompresser une photographie du prospect abîmerait la seule
   chose de la maquette qui soit vraiment de lui. */
img { max-width: 100%; height: auto; }

/* La marque n'était écrite que si le relevé avait trouvé un logo. Sans logo,
   l'en-tête n'affichait donc **aucun nom de site** — huit liens de navigation
   nus, et rien pour dire chez qui l'on est. Elle est désormais toujours là, le
   nom prenant la place du logo quand il n'y en a pas. */
.marque { display: inline-flex; align-items: center; gap: .55em; margin: 0;
    text-decoration: none; color: var(--texte); font-family: var(--police-titres);
    font-weight: var(--poids-titre); font-size: calc(var(--corps) * 1.15);
    letter-spacing: var(--lettrage-titre); line-height: 1.2;
    transition: opacity var(--duree-vive) var(--courbe); }
.marque:hover { opacity: .72; }
.marque img { max-height: 2.4rem; width: auto; }

/* Le hero est un **conteneur de calques**, quelle que soit sa disposition : les
   archétypes qui le veulent en couverture y superposent l'image, un voile et le
   texte sans que le balisage change d'une ligne. */
.hero { position: relative; isolation: isolate;
    padding: calc(var(--espace) * 2) 0; border-bottom: 1px solid var(--bordure);
    display: grid; gap: calc(var(--espace) * 1.5); align-items: center; }
.hero:has(.hero-image) { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.hero-texte { position: relative; z-index: var(--plan-contenu); }
.hero-image { position: relative; z-index: var(--plan-media);
    width: 100%; border-radius: var(--rayon-xl); object-fit: cover;
    box-shadow: var(--ombre-2); }

/* --- composition en couverture -------------------------------------------
   L'image occupe toute la largeur de la fenêtre, le texte se pose dessus. Le
   voile n'est pas décoratif : c'est lui qui **borne** le contraste d'un texte
   posé sur une photographie qu'aucun code n'a regardée. Sa couleur et son
   opacité sont des jetons, et « --sur-voile » est calculée sur le pire cas.

   Aucun texte n'est jamais incrusté dans le bitmap : c'est du HTML par-dessus
   une image, donc sélectionnable, traduisible et lisible au lecteur d'écran. */
.hero-couverture { grid-template-columns: 1fr !important;
    margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw);
    padding: calc(var(--espace) * 6) var(--retrait-colonne);
    border-bottom: 0; min-height: 60svh; align-content: end; color: var(--sur-voile); }
.hero-couverture .hero-image { position: absolute; inset: 0; z-index: var(--plan-media);
    width: 100%; height: 100%; border-radius: 0; object-fit: cover; }
/* Le dégradé se pose **par-dessus** l'aplat, jamais à sa place : il ne peut donc
   qu'assombrir là où le texte tombe, et le pire cas calculé reste le pire cas.
   Un voile uniforme sur toute la hauteur écrase la photographie ; un voile qui
   se creuse vers le bas la laisse respirer en haut, là où il n'y a pas de texte. */
.hero-couverture::before { content: ""; position: absolute; inset: 0;
    z-index: var(--plan-voile); opacity: var(--voile-opacite);
    background: linear-gradient(to top, var(--voile) 30%, transparent 100%), var(--voile); }
/* Le troisième calque : un aplat d'accent décalé derrière le texte, sous le
   voile. C'est lui qui donne de la profondeur à la composition. */
.hero-couverture::after { content: ""; position: absolute; z-index: var(--plan-voile);
    left: 0; bottom: 0; width: min(28rem, 45%); height: 45%;
    background: var(--primaire); opacity: .35;
    clip-path: polygon(0 0, 100% 22%, 100% 100%, 0 100%); }
/* Une couverture pleine largeur commence **sous l'en-tête**, sans bande de fond
   entre les deux. Le remplissage haut de « main » en laissait une, large de deux
   pas d'espacement : à l'écran, une lisière beige qui donne l'impression que
   l'image n'a pas fini de charger. */
main:has(> .hero-couverture:first-child) { padding-top: 0; }
.hero-couverture .hero-texte { color: var(--sur-voile); }
.hero-couverture .chapeau { color: var(--sur-voile); opacity: .92; }
.hero-couverture .bouton { background: var(--sur-voile); color: var(--voile); }
.hero-couverture .bouton:hover { background: var(--sur-voile); filter: brightness(.92); }
/* Le bouton portait « color: var(--fond) » sur « background: var(--primaire) » :
   deux couleurs dont **rien ne garantissait le contraste entre elles**, puisque
   le code vérifiait chaque rôle sur le fond de page et jamais cette paire-là.
   « --sur-primaire » est choisie par le calcul, comme la couleur du voile. */
.bouton {
    display: inline-flex; align-items: center; gap: .5em; margin-top: var(--espace);
    padding: .75em 1.5em; border-radius: var(--rayon-l);
    background: var(--primaire); color: var(--sur-primaire);
    text-decoration: none; font: inherit; font-weight: var(--poids-appui);
    border: 0; cursor: pointer; box-shadow: var(--ombre-1);
    transition: background var(--duree-vive) var(--courbe),
        box-shadow var(--duree) var(--courbe),
        transform var(--duree-vive) var(--courbe);
}
/* Un bouton qui ne réagit pas au survol est le signal numéro un d'une page qui
   n'est pas finie — avant la couleur, avant la typographie. La feuille n'avait
   pas une seule règle « :hover ». */
.bouton:hover { background: var(--primaire-appui); box-shadow: var(--ombre-2);
    transform: translateY(-1px); }
.bouton:active { transform: translateY(0); box-shadow: var(--ombre-1); }
/* Les sections sont des **conteneurs de requête** : une grille placée dans une
   colonne étroite doit se replier même si la fenêtre est large. « auto-fit » sur
   la fenêtre ne le voit pas, et c'est le cas qu'on rendait mal — une grille de
   trois cartes dans un archétype « colonne » de 46 rem. */
section { container-type: inline-size; }

/* **Une grille de cartes ne laisse pas une carte seule sur sa derniere rangee.**
   Le plafond vient de colonnesDeGrille() ; la piste minimale est la plus large
   des deux exigences — la densite voulue par l'archetype, et la part qui revient
   a une colonne quand on en veut au plus « --colonnes-grille ». Le plafond ne
   peut donc jamais RESSERRER la grille : c'est un plafond, pas un compte, et une
   variable a lui, jamais --grille-min, que les modes resserrent.

   L'espacement retire du calcul est celui que cette regle declare. Une feuille
   de modele qui elargit la gouttiere sans toucher au reste fait tomber la grille
   d'une colonne : la degradation est du bon cote.

   Sans plafond, --colonnes-grille vaut 99 : la part d'une colonne devient
   negative, max() garde --grille-min, et la regle est exactement celle d'avant. */
.grille { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--espace);
    --colonnes-grille: 99;
    grid-template-columns: repeat(auto-fit, minmax(min(max(var(--grille-min),
        (100% - (var(--colonnes-grille) - 1) * var(--espace)) / var(--colonnes-grille)),
        100%), 1fr)); }
.grille:has(> :nth-child(2):last-child) { --colonnes-grille: 2; }
.grille:has(> :nth-child(3):last-child) { --colonnes-grille: 3; }
.grille:has(> :nth-child(4):last-child) { --colonnes-grille: 4; }
.grille:has(> :nth-child(5):last-child) { --colonnes-grille: 3; }
.grille:has(> :nth-child(6):last-child) { --colonnes-grille: 3; }
.grille:has(> :nth-child(7):last-child) { --colonnes-grille: 4; }
.grille:has(> :nth-child(8):last-child) { --colonnes-grille: 4; }
.grille:has(> :nth-child(9):last-child) { --colonnes-grille: 3; }
.grille:has(> :nth-child(10):last-child) { --colonnes-grille: 4; }
.grille:has(> :nth-child(11):last-child) { --colonnes-grille: 4; }
.grille:has(> :nth-child(12):last-child) { --colonnes-grille: 4; }
/* La galerie n'est plus de la partie : elle porte ses propres bandes, et elles
   interrogent **sa** largeur au lieu de celle de sa section. Lui poser ici un
   gabarit de colonnes annulerait ses six pistes fixes, donc le pas de ses
   vignettes. */
@container (max-width: 32rem) {
    .grille { grid-template-columns: 1fr; }
    .chiffres { grid-template-columns: repeat(2, 1fr); }
    .citations { grid-template-columns: 1fr; }
}
@container (max-width: 20rem) {
    .chiffres { grid-template-columns: 1fr; }
}
/* **Trois cartes ne se rangent jamais par deux.** Le repli vient apres le repli
   general : il est plus specifique, et les deux disent la meme chose la ou ils
   se recouvrent. */
@container (max-width: 43rem) { .grille:has(> :nth-child(3):last-child) { grid-template-columns: 1fr; } }
@container (max-width: 43rem) { .grille:has(> :nth-child(5):last-child) { grid-template-columns: 1fr; } }
@container (max-width: 43rem) { .grille:has(> :nth-child(9):last-child) { grid-template-columns: 1fr; } }
@container (max-width: 43rem) { .grille:has(> :nth-child(11):last-child) { grid-template-columns: 1fr; } }
/* **Une carte ne porte plus de bordure par defaut.** Un filet d'un pixel autour
   d'un fond deja distinct de la page, avec un rayon moyen et aucune ombre, c'est
   la carte de Bootstrap 3 — et c'est le second signal, apres l'echelle
   typographique, qui datait les maquettes de dix ans. Ce qui detache une carte
   aujourd'hui est son fond, son espace, ou rien.

   Le filet n'est pas perdu : il devient la variante « bordee » du composant, que
   l'agent choisit quand il veut ce registre-la. Ce que le socle proposait sans
   qu'on le lui demande, il faut desormais le demander. */
.carte { padding: var(--espace); border: 0;
    border-radius: var(--rayon-l); background: var(--surface); overflow: hidden;
    transition: transform var(--duree) var(--courbe),
        box-shadow var(--duree) var(--courbe),
        border-color var(--duree) var(--courbe); }
.carte:hover { transform: translateY(-3px); box-shadow: var(--ombre-2);
    border-color: var(--bordure); }
/* **Le texte d'une carte est borné, pour que la grille tienne.** Mesuré le
   2026-08-05 sur « ateliers-stages » : six entrees de 13 a 69 mots dans la meme
   grille, soit un rapport de un a cinq entre les hauteurs. Une carte est une
   amorce, pas un article : passe quatre lignes elle cesse d'etre lisible d'un
   coup d'oeil et deregle ses voisines.

   Couper en CSS plutot qu'a la source : la matiere n'est jamais tronquee au
   milieu d'une phrase — regle de enParagraphes — et le texte entier reste dans
   le document, donc lisible par un lecteur d'ecran et par un moteur.

   **Six lignes d'abord, quatre depuis que le plafond de colonnes a elargi les
   cartes.** La borne se lit en lignes et la ligne s'allonge : la meme grille de
   six entrees passait de cinq pistes de 227 px a trois de 395 px, et six lignes
   y portaient une fois et demie le texte pour lequel elles avaient ete
   calibrees. Ce qui compte n'est pas la borne mais **l'ecart** qu'elle laisse :
   200 px de texte a cote de 67 px, c'etait 184 px de vide au bas des cartes
   courtes ; a quatre lignes l'ecart tombe des deux tiers, et l'amorce garde
   autant de mots qu'a six lignes sur une carte etroite.

   En couche socle : une feuille de modele peut relever la borne. */
.carte p { display: -webkit-box; -webkit-box-orient: vertical;
    -webkit-line-clamp: 4; line-clamp: 4; overflow: hidden; }
/* Une carte s'etire sur la hauteur de sa rangee, et son texte occupe ce qui
   reste : sans quoi deux cartes voisines alignent leur haut et rien d'autre. */
.carte { display: flex; flex-direction: column; }
.carte > :last-child { margin-bottom: 0; }
/* La marge négative fait affleurer l'image au bord de la carte : sans elle,
   la photo flotte dans un liseré de fond qui la fait paraître mal placée. */
/* --- emplacement d'une image à produire -------------------------------------
   Il occupe la place, garde le rapport de forme, et se **voit** : une maquette
   dont les images manquent doit le dire, sinon une capture d'écran la fait
   passer pour un site fini. Même intention que la mention « à rédiger » du
   remplissage textuel. */
/* La matiere reprise en fin de page : ce que les sections du plan n'ont pas
   montre. Un article par entree, en flux — ce sont des paragraphes de plusieurs
   lignes, une grille en ferait des rubans. Le filet dit qu'on change de registre
   sans crier a la rubrique. */
.reprise-matiere { display: grid; gap: var(--espace-l);
    padding-top: var(--espace-l); border-top: 1px solid var(--bordure-douce); }
.reprise-entree { max-width: var(--mesure); }
.reprise-entree h3 { margin: 0 0 var(--espace-2xs); font-size: var(--h3); }
.reprise-entree p { margin: 0; }
/* Un lien de document annonce ce qu'il ouvre. Le format en pastille, le poids
   a cote : un PDF de 4 Mo qui s'ouvre sans prevenir est un piege, surtout sur un
   telephone. Meme exigence que la mention du remplissage — ce qui va se passer
   se dit avant le clic. */
.document { display: inline-flex; align-items: center; gap: var(--espace-2xs);
    text-decoration: none; font-weight: var(--poids-appui); }
.document-format { padding: .12em .5em; border-radius: var(--rayon-xs);
    background: var(--primaire-voile); color: var(--primaire-texte);
    font-size: var(--surtitre); letter-spacing: var(--lettrage-surtitre);
    text-transform: uppercase; }
.document-poids { color: var(--texte-faible); font-weight: var(--poids-corps);
    font-size: var(--petit); }
.nav-document .document-format { background: var(--surface-creuse); }
.liste-documents { list-style: none; padding: 0; display: grid;
    gap: var(--espace-s); }
.image-absente {
    display: grid; place-items: center;
    width: 100%; margin: 0 0 var(--espace);
    border: 1px dashed var(--bordure-forte); border-radius: var(--rayon-l);
    background:
        repeating-linear-gradient(45deg, transparent 0 10px,
            color-mix(in srgb, var(--texte) 5%, transparent) 10px 20px),
        var(--surface-creuse);
    color: var(--texte-faible);
}
.image-absente--4-3 { aspect-ratio: 4 / 3; }
.image-absente--16-10 { aspect-ratio: 16 / 10; }
.image-absente--1-1 { aspect-ratio: 1 / 1; }
.image-absente--3-4 { aspect-ratio: 3 / 4; }
.carte .image-absente { width: calc(100% + 2 * var(--espace));
    margin: calc(-1 * var(--espace)) calc(-1 * var(--espace)) var(--espace); }
.image-absente-mention {
    padding: .3em .8em; border-radius: var(--rayon-rond);
    background: var(--fond);
    font-size: var(--petit); font-weight: var(--poids-appui);
    letter-spacing: var(--lettrage-surtitre); text-transform: uppercase;
}
/* **L'image d'une carte est plafonnee en hauteur, pas seulement en rapport.**
   Trois raisons, et la troisieme est la seule qui compte vraiment :

     · un rapport se lit sur la largeur, et la largeur d'une carte varie du
       simple au double dans la meme grille — la carte de une de l'archetype
       « magazine » tient deux colonnes. A rapport egal son image faisait 375 px
       contre 175 aux voisines, et **toute la rangee suivait** : 672 px contre
       437 pour la rangee d'apres, 327 px de vide au bas d'une carte ;
     · une feuille de modele qui repose « .carte img { aspect-ratio: 4/3 } » en
       couche posterieure defait le bandeau de la carte de une sans defaire son
       etendue — une invariante en deux regles dont une seule tient. Le plafond,
       lui, ne se dit qu'une fois et aucun modele ne l'a jamais ecrit ;
     · le plafond est **le meme pour toutes les cartes**, donc il ne peut pas
       introduire l'ecart qu'il corrige. Un plafond pose sur la seule carte de
       une rendait son image plus courte que celle de ses voisines des qu'une
       variante annulait l'etendue — 224 px contre 241, mesure sur l'accueil de
       la Metairie.

   Meme remede que pour l'image du hero, et pour la meme raison : une hauteur
   plafonnee dit l'intention sans dependre de la largeur — G-41.

   Le rapport reste un jeton pour que la variante « decalees » puisse le
   retablir sans le recopier : elle annule l'etendue de la carte de une, donc le
   bandeau qui allait avec. */
.carte { --carte-rapport: 4 / 3; }
.carte img { display: block; width: calc(100% + 2 * var(--espace));
    margin: calc(-1 * var(--espace)) calc(-1 * var(--espace)) var(--espace);
    aspect-ratio: var(--carte-rapport); max-height: 14rem; object-fit: cover;
    transition: transform var(--duree-ample) var(--courbe); }
.carte:hover img { transform: scale(1.035); }
/* **Une galerie compte toujours six vignettes**, et six se divise. L'emetteur
   complete les images relevees par des cadres jusqu'a six : le compte est une
   invariante, pas une circonstance, et c'est ce qui permet de garantir ici ce
   qu'une grille de cartes ne peut que plafonner.

   Six pistes fixes, et chaque vignette en occupe un pas : 6, 3, 2 ou 1 — donc
   1, 2, 3 ou 6 colonnes, **tous diviseurs de six**. Aucune largeur ne peut plus
   produire une rangee incomplete. Mesure du 2026-08-05 : « auto-fit » en rendait
   4, 5 ou 7 selon la page, et sept galeries sur douze laissaient de 366 a 906 px
   de vide sur leur derniere rangee.

   **La galerie est son propre conteneur de requete.** C'est indispensable et
   non decoratif : sa largeur n'est pas celle de sa section — mesure du meme
   jour, 1 440 px de galerie dans une section de 1 232, et 784 dans une section
   de 1 440. Interroger la section reviendrait a decider du nombre de colonnes
   d'apres une largeur qui n'est pas celle de la grille. Les regles qui visent
   « .galerie » elle-meme continuent d'interroger la section, comme avant : un
   element n'est jamais son propre conteneur. */
.galerie { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--espace);
    grid-template-columns: repeat(6, minmax(0, 1fr));
    container-type: inline-size; }
.galerie > li { grid-column: span 6; }
@container (min-width: 19.5rem) { .galerie > li { grid-column: span 3; grid-row: auto; } }
@container (min-width: 30rem) { .galerie > li { grid-column: span 2; grid-row: auto; } }
@container (min-width: 61.5rem) { .galerie > li { grid-column: span 1; grid-row: auto; } }
/* Le recadrage vit sur le « li » et non sur l'image : c'est ce qui permet de
   faire glisser la photographie sous son propre cadre au survol. */
.galerie > li { overflow: hidden; border-radius: var(--rayon-l); }
.galerie img { display: block; width: 100%; aspect-ratio: 4 / 3;
    object-fit: cover; border-radius: var(--rayon-l);
    transition: transform var(--duree-ample) var(--courbe); }
.galerie > li:hover img { transform: scale(1.05); }

/* Une galerie peut déborder la colonne : c'est la seule façon de donner à des
   photographies la place qu'elles méritent dans une colonne éditoriale. */
.pleine-largeur { margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw);
    padding-inline: var(--retrait-colonne); }
/* Un cadre en pointillés se lit comme une image **qui n'a pas chargé** — le
   contraire exact de ce qu'il veut dire. Un aplat doux et un pictogramme centré
   se lisent comme un emplacement réservé, ce qu'il est. */
.cadre { aspect-ratio: 4 / 3; border: 0; border-radius: var(--rayon-l);
    background: linear-gradient(135deg, var(--primaire-voile), var(--surface-creuse)); }
.appel { padding: var(--espace-xl); border-radius: var(--rayon-xl);
    border: 1px solid var(--bordure-douce); background: var(--primaire-voile); }

/* Sections métier : ce qui les distingue est leur **forme**. Un tableau
   d'horaires ne se lit pas comme un paragraphe, et c'est tout l'intérêt de leur
   avoir donné un type propre. */
.fiche { padding: var(--espace-l); border-left: 3px solid var(--primaire);
    background: var(--surface-creuse); border-radius: 0 var(--rayon-l) var(--rayon-l) 0; }
.fiche h2 { display: flex; align-items: center; gap: .5em; }
.coordonnees { list-style: none; margin: var(--espace) 0 0; padding: 0;
    display: flex; flex-wrap: wrap; gap: var(--espace); font-weight: var(--poids-appui); }
.coordonnees a { text-decoration: none; }
.coordonnees a:hover { text-decoration: underline; }

.chiffres { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--espace);
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); text-align: center; }
.chiffres li { padding: var(--espace-l) var(--espace); border: 0;
    border-radius: var(--rayon-l); background: var(--primaire-voile);
    transition: transform var(--duree) var(--courbe); }
.chiffres li:hover { transform: translateY(-2px); }
.chiffres strong { display: block; font-family: var(--police-titres);
    font-size: calc(var(--h2) * var(--titre-echelle)); line-height: 1.05;
    letter-spacing: var(--lettrage-titre); color: var(--primaire); }
/* Le libellé passe en capitales espacées : c'est ce qui fait qu'un chiffre se
   lit comme un chiffre-clé et non comme une légende de tableau. */
.chiffres span { display: block; margin-top: .3em; font-size: var(--surtitre);
    text-transform: uppercase; letter-spacing: var(--lettrage-surtitre);
    font-weight: var(--poids-appui); color: var(--texte-faible); }

.citations { display: grid; gap: var(--espace);
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); }
.citations figure { margin: 0; }
.citations blockquote { margin: 0; padding: var(--espace-l);
    border: 0; border-radius: var(--rayon-l); background: var(--surface);
    box-shadow: var(--ombre-1); font-size: calc(var(--corps) * 1.05);
    line-height: 1.55; font-style: italic; }

.partenaires { list-style: none; margin: 0; padding: 0; display: flex;
    flex-wrap: wrap; align-items: center; gap: calc(var(--espace) * 1.5); }
/* Les logos d'un tiers gardent leurs couleurs : les désaturer serait modifier
   une marque qui n'est pas celle du prospect. On borne seulement la hauteur. */
.partenaires img { max-height: 3.5rem; width: auto;
    transition: transform var(--duree) var(--courbe); }
.partenaires li:hover img { transform: scale(1.06); }

.formulaire { display: grid; gap: var(--espace); max-width: var(--mesure-courte); }
.formulaire label { display: grid; gap: .35em; font-size: var(--petit);
    font-weight: var(--poids-appui); }
.formulaire input, .formulaire textarea {
    padding: .7em .8em; border: 1px solid var(--bordure); border-radius: var(--rayon);
    background: var(--surface); color: var(--texte); font: inherit;
    transition: border-color var(--duree-vive) var(--courbe),
        box-shadow var(--duree-vive) var(--courbe);
}
.formulaire input:hover, .formulaire textarea:hover { border-color: var(--bordure-forte); }
/* L'anneau remplace le contour du navigateur, qui disparaît dès qu'on touche à
   une couleur de fond. Il reste visible au clavier — c'est sa seule raison d'être. */
.formulaire input:focus, .formulaire textarea:focus {
    outline: 0; border-color: var(--primaire);
    box-shadow: 0 0 0 3px var(--primaire-voile-fort);
}
/* Le formulaire est une grille : sans cela le bouton s'étire sur toute la
   colonne, et un « Envoyer » large de cinq cents pixels ne ressemble à rien. */
.formulaire .bouton { justify-self: start; }
details { border-bottom: 1px solid var(--bordure-douce); padding: var(--espace-s) 0; }
summary { cursor: pointer; font-weight: var(--poids-appui); padding: var(--espace-2xs) 0;
    transition: color var(--duree-vive) var(--courbe); }
summary:hover, details[open] summary { color: var(--primaire-texte); }

/* « À voir aussi » était une liste à puces des liens de la page : la forme la
   plus pauvre qu'on puisse donner à une invitation à continuer. */
.suite ul { list-style: none; margin: 0; padding: 0;
    display: flex; flex-wrap: wrap; gap: var(--espace-xs); }
.suite a { display: inline-flex; align-items: center;
    padding: var(--espace-2xs) var(--espace-s);
    border: 1px solid var(--bordure-douce); border-radius: var(--rayon-rond);
    background: var(--surface); color: var(--texte); text-decoration: none;
    font-size: var(--petit); font-weight: var(--poids-appui);
    transition: background var(--duree-vive) var(--courbe),
        border-color var(--duree-vive) var(--courbe),
        color var(--duree-vive) var(--courbe); }
.suite a:hover { background: var(--primaire-voile); border-color: var(--primaire);
    color: var(--primaire-texte); }

/* Le pied de page recopiait la navigation principale, mot pour mot et sur une
   seule ligne. Il porte maintenant la marque à gauche et les rubriques en
   colonne : la même information, mais lue comme un pied de page. */
footer { border-top: 1px solid var(--bordure-douce); background: var(--surface-creuse);
    padding: var(--espace-2xl) var(--espace);
    /* La bannière est posée en bas de la fenêtre : sans cette réserve, elle
       recouvrirait la dernière ligne du pied de page. */
    padding-bottom: calc(var(--espace-2xl) + 2.5rem); }
.pied { max-width: var(--largeur-site); margin: 0 auto; display: grid;
    gap: var(--espace-l); grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
    align-items: start; }
.pied-marque { display: grid; gap: var(--espace-xs); justify-items: start; }
footer nav { display: grid; gap: var(--espace-2xs); margin: 0; }
footer nav a { color: var(--texte-faible); font-weight: var(--poids-corps); }
footer nav a:hover { color: var(--primaire-texte); }
.pied-titre { margin: 0 0 var(--espace-xs); font-size: var(--surtitre);
    text-transform: uppercase; letter-spacing: var(--lettrage-surtitre);
    font-weight: var(--poids-appui); color: var(--texte-faible); }
.pied-bas { max-width: var(--largeur-site); margin: var(--espace-l) auto 0;
    padding-top: var(--espace); border-top: 1px solid var(--bordure-douce); }
/* Requête de **fenêtre** et non de conteneur : le pied de page n'a aucun
   ancêtre déclaré « container-type », et une requête de conteneur sans
   conteneur ne se déclenche jamais — en silence. */
@media (max-width: 46rem) { .pied { grid-template-columns: 1fr; } }
.mention { color: var(--texte-faible); font-size: var(--petit); }

/* Le remplissage se voit — mais il n'a pas à **abîmer** la page pour se voir.
   Une opacité de 0,82 sur le paragraphe entier délavait la maquette, et faisait
   passer une carence de contenu pour un défaut de mise en forme. L'étiquette dit
   la même chose, et le texte reste net. */
[data-remplissage]::after {
    content: "à rédiger"; display: inline-block; margin-left: .5em;
    padding: .1em .65em; border-radius: var(--rayon-rond);
    background: var(--accent-voile); color: var(--accent-appui);
    font-family: var(--police-corps); font-size: var(--surtitre);
    font-weight: var(--poids-appui); font-style: normal;
    letter-spacing: .02em; vertical-align: .1em;
}

/* --- accessibilité --------------------------------------------------------
   Le focus doit se voir. Le défaut du navigateur disparaît dès qu'on touche à
   une couleur de fond, et personne ne s'en aperçoit à la souris. */
:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; border-radius: 2px; }

.evitement { position: absolute; left: -9999px; top: 0;
    z-index: var(--plan-entete); padding: .6em 1em; border-radius: 0 0 var(--rayon) 0;
    background: var(--primaire); color: var(--sur-primaire);
    text-decoration: none; font-weight: var(--poids-appui); }
.evitement:focus { left: 0; }

/* --- apparition au défilement ---------------------------------------------
   **Sans une ligne de script** : le minutage vient de la position de l'élément
   dans la fenêtre, que le navigateur connaît déjà. Là où il ne sait pas le
   faire, il n'y a simplement pas d'animation — rien n'est masqué au départ,
   donc rien ne peut rester masqué. C'est la seule forme d'apparition acceptable
   sur une page que nginx sert telle quelle.

   Le hero en est exclu : il est visible au chargement, et le faire apparaître
   ferait clignoter la première chose que le prospect voit. Les cartes aussi,
   parce qu'une animation « both » fige leur transformation et empêcherait le
   soulèvement au survol de se produire. */
@supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
        main > section:not(.hero) {
            animation: apparait linear both;
            animation-timeline: view();
            animation-range: entry 0% entry 40%;
        }
    }
}
@keyframes apparait {
    from { opacity: 0; transform: translateY(16px); }
    to { opacity: 1; transform: none; }
}

/* « prefers-reduced-motion » est un **vrai mode**, pas un interrupteur
   cosmétique : plus aucune transition, et le défilement redevient instantané.
   Toute l'information reste disponible — rien n'est masqué par ce mode. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important; animation-iteration-count: 1 !important;
        transition-duration: .01ms !important; scroll-behavior: auto !important;
    }
    /* Le sélecteur universel **n'atteint pas** les pseudo-éléments de
       transition de vue : ils vivent dans un arbre à part. Sans cette règle,
       une préférence de mouvement réduit laisserait la page glisser quand
       même. */
    ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) {
        animation: none !important;
    }
}


/* --- Variantes de composants ---------------------------------------------
   Déclarées dans lib/composants.mjs avec leur usage et leurs anti-patrons.
   Une variante ne change que la forme : le balisage du composant est le même
   quelle qu'elle soit. */
/* hero — chevauchement : le texte mord sur l'image — une ouverture qui a du relief */
.v-hero-chevauchement .hero-image { max-height: none; aspect-ratio: 4 / 5;
    border-radius: var(--rayon-l); }
@media (min-width: 60rem) {
    .v-hero-chevauchement { align-items: center; }
    /* Le fond et sa couleur de texte vont ensemble : la carte quitte le fond de
       page, donc elle redit les deux. Sans quoi un p { color } venu d'ailleurs
       peut casser le couple — la faute de G-27, G-34 et G-36. */
    .v-hero-chevauchement .hero-texte { position: relative; z-index: var(--plan-contenu);
        margin-inline-start: calc(var(--espace-2xl) * -1);
        padding: var(--espace-xl); border-radius: var(--rayon-xl);
        background: var(--fond); color: var(--texte); box-shadow: var(--ombre-2); }
}

/* texte — colonnes : un texte long qu'on veut aérer sur grand écran */
.v-texte-colonnes p { max-width: none; }
@media (min-width: 60rem) {
    .v-texte-colonnes { columns: 2; column-gap: calc(var(--espace) * 2); }
    .v-texte-colonnes h2 { column-span: all; }
}

/* texte — encadre : une mise en garde, un rappel, une précision qui doit se détacher */
.v-texte-encadre { padding: var(--espace); background: var(--surface);
    border-left: 3px solid var(--accent); border-radius: 0 var(--rayon) var(--rayon) 0; }

/* cartes — bordee : des entrées à comparer une à une, que le cadre sépare nettement */
.v-cartes-bordee .carte { border: 1px solid var(--bordure-douce); }
.v-cartes-bordee .carte:hover { border-color: var(--bordure); }

/* cartes — decalees : des entrées de même nature, posées en quinconce */
@media (min-width: 60rem) {
    /* Pas d align-items: start. Le decalage se fait par la marge seule ; « start »
       y ajoutait des cartes de hauteurs libres, et trois textes de longueurs
       differentes donnaient trois boites sans rapport. En etirement — le defaut
       de la grille — les hauts restent decales et les bas s alignent : le
       decalage se lit alors comme une intention. */
    .v-cartes-decalees .grille > .carte:nth-child(even) { margin-block-start: var(--espace-3xl); }
    /* **Le decalage et la carte etendue ne vont pas ensemble.** L archetype
       « magazine » etend la premiere carte sur deux colonnes ; combine au
       decalage, on obtenait une carte double large et haute, une deuxieme
       poussee de trois pas vers le bas, et une troisieme ailleurs — trois
       accidents plutot qu une composition.

       La variante gagne parce qu elle est plus specifique, pas parce qu elle
       vient apres : dans @layer socle, les regles d archetype sont ecrites
       APRES les variantes. */
    .v-cartes-decalees .grille > .carte:first-child { grid-column: auto; }
    /* **Retirer l etendue, c est retirer le bandeau qui allait avec.** Un
       archetype peut donner a la carte de une un rapport d image plus large que
       celui des autres — c est ce que fait « magazine » —, et il le fait parce
       qu elle est deux fois plus large. Ramenee a une colonne, elle gardait ce
       rapport : 138 px d image contre 241 a ses voisines, dans la meme rangee.
       Le rapport se retablit par son jeton, jamais par sa valeur recopiee. */
    .v-cartes-decalees .grille > .carte:first-child img { aspect-ratio: var(--carte-rapport); }
}

/* cartes — liste : des entrées à lire dans l'ordre, image à gauche */
.v-cartes-liste .grille { grid-template-columns: 1fr; }
@media (min-width: 40rem) {
    /* **Un flottement, pas une grille.** La grille a été essayée et mesurée le
       2026-08-03 : avec trois enfants — image, titre, paragraphe — et deux
       colonnes sans placement, le paragraphe retombait dans la colonne de
       12 rem et se lisait sur vingt lignes de dix mots. Le placement explicite
       corrigeait la colonne, mais grid-row 1/-1 ne s'étend pas sur une grille
       implicite : un blanc de la hauteur de l'image s'installait entre le titre
       et son texte.

       Un flottement fait exactement ce qu'on demande — le texte coule autour de
       l'image, quel que soit le nombre d'enfants — et ne connaît aucun de ces
       deux défauts. */
    /* La carte se borne à ce qu'elle contient : l'image, plus la mesure de
       lecture. Sans cette borne, un texte bridé à 65 caractères — ce qui est
       juste — laissait le tiers droit de la carte vide, et la composition
       paraissait mal alignée alors que seule la carte l'était. */
    .v-cartes-liste .carte { display: block; overflow: hidden;
        max-width: calc(12rem + var(--espace) + var(--mesure)); }
    .v-cartes-liste .carte img { float: left; width: 12rem; aspect-ratio: 4 / 3;
        margin: 0 var(--espace) var(--espace-2xs) 0; }
    .v-cartes-liste .carte .image-absente { float: left; width: 12rem;
        margin: 0 var(--espace) var(--espace-2xs) 0; }
    .v-cartes-liste .carte > h3 { margin-top: 0; }
}

/* cartes — mosaique : beaucoup d'entrées courtes, vues d'un coup */
.v-cartes-mosaique .grille { grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }
.v-cartes-mosaique .carte { padding: calc(var(--espace) * .6); }
.v-cartes-mosaique .carte h3 { font-size: var(--petit); }

/* galerie — bande : un défilement horizontal, quand la page est déjà longue */
.v-galerie-bande .galerie { grid-auto-flow: column;
    grid-auto-columns: minmax(var(--galerie-min), 18rem); grid-template-columns: none;
    overflow-x: auto; scroll-snap-type: x mandatory; padding-bottom: var(--espace); }
/* **Une variante qui reprend le gabarit reprend le pas des vignettes avec.** Le
   socle donne six pistes et fait occuper a chaque vignette un pas qui divise
   six ; ici il n'y a plus de pistes du tout, et un « span 2 » herite ferait
   occuper deux colonnes IMPLICITES a chaque vignette — une bande a trous. */
.v-galerie-bande .galerie > li { scroll-snap-align: start; grid-column: auto; }

/* galerie — mosaique : beaucoup d'images, format serré */
.v-galerie-mosaique .galerie > li { grid-column: span 6; }
@container (min-width: 15.5rem) { .v-galerie-mosaique .galerie > li { grid-column: span 3; } }
@container (min-width: 24rem) { .v-galerie-mosaique .galerie > li { grid-column: span 2; } }
@container (min-width: 49.5rem) { .v-galerie-mosaique .galerie > li { grid-column: span 1; } }

/* listage — liste : des entrées à lire dans l'ordre, image à gauche */
.v-listage-liste .grille { grid-template-columns: 1fr; }
@media (min-width: 40rem) {
    /* **Un flottement, pas une grille.** La grille a été essayée et mesurée le
       2026-08-03 : avec trois enfants — image, titre, paragraphe — et deux
       colonnes sans placement, le paragraphe retombait dans la colonne de
       12 rem et se lisait sur vingt lignes de dix mots. Le placement explicite
       corrigeait la colonne, mais grid-row 1/-1 ne s'étend pas sur une grille
       implicite : un blanc de la hauteur de l'image s'installait entre le titre
       et son texte.

       Un flottement fait exactement ce qu'on demande — le texte coule autour de
       l'image, quel que soit le nombre d'enfants — et ne connaît aucun de ces
       deux défauts. */
    /* La carte se borne à ce qu'elle contient : l'image, plus la mesure de
       lecture. Sans cette borne, un texte bridé à 65 caractères — ce qui est
       juste — laissait le tiers droit de la carte vide, et la composition
       paraissait mal alignée alors que seule la carte l'était. */
    .v-listage-liste .carte { display: block; overflow: hidden;
        max-width: calc(12rem + var(--espace) + var(--mesure)); }
    .v-listage-liste .carte img { float: left; width: 12rem; aspect-ratio: 4 / 3;
        margin: 0 var(--espace) var(--espace-2xs) 0; }
    .v-listage-liste .carte .image-absente { float: left; width: 12rem;
        margin: 0 var(--espace) var(--espace-2xs) 0; }
    .v-listage-liste .carte > h3 { margin-top: 0; }
}

/* cta — bandeau : une coupure pleine largeur entre deux sections */
.v-cta-bandeau { margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw);
    padding: calc(var(--espace) * 3) var(--retrait-colonne);
    /* --sur-primaire et non --fond : le premier est VÉRIFIÉ contre l'aplat par
       texteSur(), le second s'y trouvait à 3,82:1 seulement — G-27. */
    border-radius: 0; background: var(--primaire); color: var(--sur-primaire); }
.v-cta-bandeau .bouton { background: var(--fond); color: var(--primaire-texte); }

/* horaires — simple : dans le fil, sans encadré */
.v-horaires-simple { border: 0; background: transparent; padding: 0; }

/* tarifs — simple : dans le fil */
.v-tarifs-simple { border: 0; background: transparent; padding: 0; }

/* acces — simple : dans le fil */
.v-acces-simple { border: 0; background: transparent; padding: 0; }

/* chiffres — ligne : une ligne sans cadre, plus discrète */
.v-chiffres-ligne .chiffres li { border: 0; padding: 0; }
.v-chiffres-ligne .chiffres { gap: calc(var(--espace) * 2); }

/* agenda — liste : une suite chronologique, la forme d'un programme */
.agenda { list-style: none; padding: 0; display: grid; gap: 0; }
.agenda li { display: grid; grid-template-columns: minmax(6rem, auto) 1fr;
    gap: var(--espace); align-items: baseline;
    padding: var(--espace-s) 0; border-top: 1px solid var(--bordure-douce); }
.agenda li:last-child { border-bottom: 1px solid var(--bordure-douce); }
.agenda time { font-weight: var(--poids-appui); color: var(--primaire-texte);
    font-variant-numeric: tabular-nums; }
@media (max-width: 30em) { .agenda li { grid-template-columns: 1fr; gap: var(--espace-3xs); } }

/* agenda — fiches : des cases, quand les dates sont peu nombreuses et à mettre en avant */
.v-agenda-fiches .agenda { grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: var(--espace); }
.v-agenda-fiches .agenda li { display: block; border: 1px solid var(--bordure);
    border-radius: var(--rayon-l); padding: var(--espace); background: var(--surface); }
.v-agenda-fiches .agenda li:last-child { border-bottom-width: 1px; }
.v-agenda-fiches .agenda time { display: block; margin-bottom: var(--espace-3xs); }

/* annonce — filet : discrète, un filet coloré à gauche */
.annonce { display: grid; gap: var(--espace-3xs);
    padding: var(--espace-s) var(--espace); border-left: 3px solid var(--accent);
    background: var(--accent-voile); }
.annonce .annonce-date { font-size: var(--petit); font-weight: var(--poids-appui);
    letter-spacing: var(--lettrage-surtitre); text-transform: uppercase; }

/* annonce — bandeau : pleine largeur, pour ce qui ne doit pas être manqué */
.v-annonce-bandeau { margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw);
    padding: var(--espace-l) var(--retrait-colonne);
    background: var(--accent-voile); color: var(--texte); }
.v-annonce-bandeau .annonce { border-left: 0; padding: 0; background: transparent; }

/* temoignages — bandeau : une seule citation, en pleine largeur */
.v-temoignages-bandeau .citations { grid-template-columns: 1fr; }
.v-temoignages-bandeau blockquote { font-size: calc(var(--corps) * 1.25);
    border: 0; background: transparent; text-align: center; padding-inline: 0; }

/* partenaires — bandeau : une bande pleine largeur, fond teinté */
.v-partenaires-bandeau { margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw);
    padding: calc(var(--espace) * 2) var(--retrait-colonne);
    background: var(--surface); }
.v-partenaires-bandeau .partenaires { justify-content: center; }

/* --- Modes d'expérience ---------------------------------------------------
   Une modulation de **page** à l'intérieur de la disposition du site. Les
   jetons sont portés par « body » : une propriété personnalisée y est héritée par
   tout le document, ce qui suffit à moduler une page sans dupliquer la feuille. */
/* editorial — une page qui se lit — présentation, histoire, dossier */
body.mode-editorial {
    --rythme: 4;
}

/* institutionnel — une page de référence, sobre — mentions, organisation, gouvernance */
body.mode-institutionnel {
    --rythme: 3;
}
/* Sobriété : des filets plutôt que des fonds, aucun effet. */
body.mode-institutionnel section + section {
    border-top: 1px solid var(--bordure); padding-top: calc(var(--espace) * 2); }
body.mode-institutionnel .carte { box-shadow: none; }

/* listage — un inventaire — beaucoup d'entrées, peu de texte par entrée */
body.mode-listage {
    --rythme: 2;
    --grille-min: 13rem;
    --galerie-min: 10rem;
}
/* On resserre pour montrer davantage d'entrées d'un coup. */
body.mode-listage .hero { padding-block: var(--espace); }
body.mode-listage .carte h3 { font-size: var(--corps); }

/* conversion — une page dont le but est une action — contact, réservation, inscription */
body.mode-conversion {
    --rythme: 3;
}
/* L'appel à l'action domine, et le formulaire occupe la largeur utile. */
body.mode-conversion .appel { padding: calc(var(--espace) * 3); text-align: center; }
body.mode-conversion .appel .bouton { font-size: calc(var(--corps) * 1.1); }
body.mode-conversion .formulaire { max-width: none; }
body.mode-conversion .bouton { padding: .85em 1.8em; }

/* produit — une fiche — un objet, une œuvre, une prestation détaillée */
body.mode-produit {
    --rythme: 3;
}
/* La fiche met l'image et le texte au même niveau, côte à côte. */
@media (min-width: 52rem) {
    body.mode-produit .hero:has(.hero-image) {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); }
}
body.mode-produit .fiche { border-left-width: 4px; }

/* documentation — une page consultée par fragments — informations pratiques, aide, FAQ */
body.mode-documentation {
    --rythme: 2;
    --titre-echelle: .9;
}
/* On consulte, on ne lit pas d'un trait : les repères comptent plus que le
   souffle. Chaque section garde une ancre atteignable. */
body.mode-documentation section[id] { scroll-margin-top: var(--espace); }
body.mode-documentation h2 { border-bottom: 1px solid var(--bordure);
    padding-bottom: .3em; }
body.mode-documentation details { padding-block: calc(var(--espace) * .5); }

/* recit — une page qui raconte — visite, collection, patrimoine */
body.mode-recit {
    --rythme: 5;
}
/* Le récit respire : images larges, chapeau généreux, beaucoup de vide. */
body.mode-recit .chapeau { font-size: calc(var(--corps) * 1.3); max-width: 34rem; }
body.mode-recit .galerie, body.mode-recit .hero-image { border-radius: var(--rayon); }
body.mode-recit .galerie { margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw);
    padding-inline: var(--retrait-colonne); }

/* campagne — une page à message unique — annonce, événement, opération */
body.mode-campagne {
    --rythme: 5;
    --titre-echelle: 1.3;
}
/* Une idée par écran, tout centré. */
body.mode-campagne main { text-align: center; }
body.mode-campagne p, body.mode-campagne .chapeau { margin-inline: auto; }
body.mode-campagne .grille, body.mode-campagne .formulaire { text-align: left; }


/* --- États vides et états d'erreur ----------------------------------------
   Un état vide n'est jamais une page blanche : il dit ce qui manque et ce
   qu'on peut faire — rebuild-rules.md §18. */
.etat-vide, .etat-erreur, .etat-succes, .etat-substitut {
    padding: var(--espace-xl); border-radius: var(--rayon-xl);
    border: 1px solid var(--bordure-douce); background: var(--surface);
}
/* Un état vide encadré d'un filet gris se lit comme une **erreur de rendu**.
   Un aplat doux, centré, sans bordure, se lit comme une place réservée — ce
   qu'il est. C'est la même information, et ce n'est pas le même aveu. */
.etat-vide { border: 0; background: var(--primaire-voile); text-align: center; }
.etat-vide .mention { max-width: var(--mesure-courte); margin-inline: auto; }
.etat-titre { font-family: var(--police-titres); font-weight: var(--poids-titre);
    font-size: calc(var(--corps) * 1.1); letter-spacing: var(--lettrage-titre);
    margin: 0 0 .4em; }
.etat-erreur { border-left: 4px solid var(--accent); background: var(--accent-voile); }
.etat-succes { border-left: 4px solid var(--primaire); background: var(--primaire-voile); }
.etat-substitut { border-style: dashed; text-align: center; background: var(--surface-creuse); }
.champ-erreur input[aria-invalid="true"] { border-color: var(--accent); border-width: 2px; }
.etat-squelette { display: grid; gap: .6em; }
.etat-squelette span { display: block; height: 1em; border-radius: var(--rayon-xs);
    background: var(--bordure); }
.etat-squelette span:nth-child(2) { width: 80%; }
.etat-squelette span:nth-child(3) { width: 55%; }

/* La page de référence : elle se distingue du site qu'elle documente. */
.etats-liste { display: grid; gap: var(--espace-xl); list-style: none;
    margin: 0; padding: 0; }
.etats-liste > li { border-top: 1px solid var(--bordure-douce); padding-top: var(--espace); }
.etats-quand { color: var(--texte-faible); font-size: var(--petit); margin: 0 0 var(--espace); }


/* --- Pictogrammes ---------------------------------------------------------
   Sprite en ligne, un symbole par role employe. La couleur suit le texte :
   aucune valeur codee en dur, donc aucun picto a reprendre quand le systeme
   de design change. */
.sprite { display: none; }
.ico { width: 1.15em; height: 1.15em; flex: none; vertical-align: -.17em;
    fill: currentColor; }
/* Le pictogramme d'un etat vide prend la couleur primaire, en retrait : gris sur
   gris, il disparaissait dans son cadre et la section paraissait vide au sens
   propre — c'est-a-dire ratee, et non reservee. */
.ico-etat { width: 4rem; height: 4rem; display: block; margin: 0 auto var(--espace-s);
    fill: currentColor; color: var(--primaire); opacity: .5; }
.ico-vignette { width: 2.4rem; height: 2.4rem; display: block; margin: 0 auto .6em;
    fill: currentColor; color: var(--primaire); opacity: .35; }
.coordonnees li { display: flex; align-items: baseline; gap: .5em; }
.coordonnees .ico { color: var(--primaire); }
.cadre { display: grid; place-content: center; }
.cadre .ico-vignette { margin: 0; }


/* --- Forme par défaut, et négociable ---------------------------------------
   Ces règles étaient hors couche jusqu'au 2026-08-03, donc intouchables par une
   feuille de modèle. Elles n'ont pourtant rien de structurel : centrer une
   colonne, coller un en-tête, poser une couverture à 60svh sont des PARTIS
   PRIS. Les tenir pour des garanties, c'est ce qui faisait sortir cinq
   maquettes de cinq secteurs différents identiques à la police près.

   Elles vivent donc ici : elles s'appliquent tant que rien ne les reprend, et
   la couche « modele » gagne dessus. Ce que le modèle ne peut toujours pas
   défaire est resté dans garantiesStructurelles(). */
main { max-width: var(--largeur); margin-inline: auto;
    padding-inline: max(var(--espace), env(safe-area-inset-left)); }
header { position: sticky; top: 0; z-index: var(--plan-entete); }
.entete { max-width: max(var(--largeur-site), min(88rem, 100%)); margin-inline: auto;
    padding-inline: max(var(--espace), env(safe-area-inset-left)); }
.hero-couverture { grid-template-columns: 1fr;
    margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw);
    padding: calc(var(--espace) * 6) var(--retrait-colonne);
    min-height: 60svh; align-content: end; }
.pleine-largeur { margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw);
    padding-inline: var(--retrait-colonne); }

/* --- Disposition de l'archétype --------------------------------------------
   Descendue elle aussi dans le socle. C'est une proposition de rythme, pas une
   garantie — et tant qu'elle était hors couche, elle reprenait son fond au
   bandeau d'appel à l'action sans lui reprendre sa couleur de texte : G-27. */

/* --- Archétype « dense » ----------------------------------------- */
/* Dense : on privilégie le nombre d'entrées visibles sur le confort de
   lecture. C'est ce que fait un site de collectivité, et c'est assumé. */
.hero { padding: var(--espace) 0; }
.hero:has(.hero-image) { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
.hero-image { aspect-ratio: 3 / 2; }
.carte { padding: calc(var(--espace) * .7); }
.carte img { width: calc(100% + 1.4 * var(--espace));
    margin: calc(-.7 * var(--espace)) calc(-.7 * var(--espace)) calc(var(--espace) * .7); }
.carte h3 { font-size: var(--corps); }
nav[aria-label="Navigation principale"] { gap: calc(var(--espace) * .7); font-size: var(--petit); }
details { padding: calc(var(--espace) * .6) 0; }

/* Le repli étroit garde ici ce qui relève de l'habillage ; sa géométrie, elle,
   est répétée hors couche dans les garanties structurelles. */
@media (max-width: 40rem) {
    .marque { flex: none; }
    .marque span { white-space: nowrap; }
    nav[aria-label="Navigation principale"] a { white-space: nowrap; }
    /* La pastille d'avertissement recouvre le contenu sur un petit écran : elle
       s'y fait plus courte, et lâche la moitié droite de la fenêtre. */
    .banniere { max-width: calc(100vw - var(--espace-l)); font-size: .68rem;
        padding: var(--espace-3xs) var(--espace-xs); }
    /* La vignette de une, elle, n'est plus reprise ici : ses bandes savent a
       quelle largeur elle tient, et « grid-column: auto » lui donnerait une seule
       piste sur six pendant que ses voisines en occupent deux. */
    .grille > .carte:first-child { grid-column: auto; grid-row: auto; }
    h1 { font-size: calc(var(--h1) * var(--titre-echelle)); }
}

}

/* --- garanties structurelles, hors couche ---------------------------------
   Ce bloc gagne sur le socle **et** sur la feuille du modèle. Il ne contient
   que de la géométrie : ce qui rend le site faux si on le défait. */
html { overflow-x: clip; }
body { overflow-x: clip; margin: 0; padding: 0; max-width: none; width: auto; }
.hero-couverture .hero-image { position: absolute; inset: 0;
    width: 100%; height: 100%; border-radius: 0; object-fit: cover; }
/* Le voile et la couleur qui se pose dessus sont une **garantie de contraste**,
   pas une décoration. « --sur-voile » est calculée par texteSurVoile() sur le
   pire cas : un voile d'opacité connue composé sur un gris moyen. C'est ce qui
   permet d'écrire sur une photographie qu'aucun code n'a regardée.

   Le modèle a écrit « .hero::before { … } » — même spécificité que
   « .hero-couverture::before », et plus tard dans la feuille. Le voile a
   disparu, le chapeau est repassé en couleur primaire, et le texte s'est
   retrouvé illisible sur l'image. Un calcul de contraste ne se laisse pas
   renégocier par une feuille. */
.hero-texte { position: relative; z-index: var(--plan-contenu); }
.hero-couverture::before { content: ""; position: absolute; inset: 0;
    z-index: var(--plan-voile); opacity: var(--voile-opacite);
    background: linear-gradient(to top, var(--voile) 30%, transparent 100%), var(--voile); }
.hero-couverture, .hero-couverture .hero-texte, .hero-couverture .hero-texte *,
.hero-couverture .chapeau { color: var(--sur-voile); }
.hero-couverture .bouton { background: var(--sur-voile); color: var(--voile); }
.pleine-largeur { margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw);
    padding-inline: var(--retrait-colonne); }
[id] { scroll-margin-top: 5.5rem; }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
/* Le couple fond + couleur des sections qui peignent leur propre fond. Dérivé
   de composants.mjs. Hors couche parce que c'est un contraste, pas un parti
   pris : la feuille du modèle gagne sur le socle, et un simple « p { color } »
   de sa part suffisait à poser du gris sur du brun — 1,03:1 mesuré. */
.v-cta-bandeau, .v-cta-bandeau :where(p, h2, h3, h4, li, dt, dd, blockquote, strong, em, span) { color: var(--sur-primaire); }
.v-annonce-bandeau, .v-annonce-bandeau :where(p, h2, h3, h4, li, dt, dd, blockquote, strong, em, span) { color: var(--texte); }
.v-cta-bandeau, .v-annonce-bandeau { padding-inline: var(--retrait-colonne); }
/* **Un lien de navigation ne porte jamais deux traits.** Le socle propose un
   indicateur de page courante — une barre dessinee par ::after, animee, que le
   modele peut restyler entierement. Il proposait aussi « text-decoration: none »
   sur les liens de nav ; une couche posterieure bat n'importe quelle
   specificite, et un « a { text-decoration: underline } » du modele suffisait a
   souligner toute la barre de menu **et a doubler le trait de l'item actif**.
   Ce n'est pas un parti pris qu'on ecrase : c'est une convention dont la
   violation se lit comme une erreur. La moitie negative du couple est donc
   garantie, la moitie visible reste au modele — G-36.

   **La classe nav-action y est nommee depuis le 2026-08-05.** L'appel a l'action a
   quitte la navigation ce jour-la — il defilait hors du cadre avec elle — et il
   est sorti du meme coup de cette garantie : le bouton s'est retrouve souligne
   a la premiere recompilation. Une garantie qui tient a un selecteur de
   structure se perd quand la structure bouge. */
nav[aria-label="Navigation principale"] a, .nav-action { text-decoration: none; }
/* **Un aplat porte toujours une respiration.** Le socle donnait « .45em 1.05em »
   a l'action promue du menu ; le modele a repose « var(--espace-2xs) 0 » et le
   fond brun s'est colle au texte — 0 px de part et d'autre, et un pave deux fois
   plus haut que ses voisins. Le padding d'un element peint n'est pas une
   decoration : sans lui, l'aplat n'est pas un bouton, c'est un accident. Sa
   valeur reste au modele par les jetons ; son existence, non — G-38. */
.nav-action { padding-inline: max(1.05em, var(--espace-s)); padding-block: .45em; }
/* **Une grille ne declare pas plus de colonnes qu'elle n'a de contenu.** Le
   socle ne donne deux colonnes au hero que s'il porte une image ; une feuille de
   modele qui les pose sans condition laisse la moitie de l'ecran vide quand
   l'image manque — 272 px perdus, mesure sur /etats/. Le cas ne se dit pas en
   CSS pour une grille quelconque ; il se dit pour celle-ci, et c'est la seule
   qui occupe un ecran entier — G-38. */
.hero:not(:has(.hero-image, .image-absente)) { grid-template-columns: 1fr; }
.evitement { position: absolute; left: -9999px; top: 0; z-index: var(--plan-entete); }
.evitement:focus { left: 0; }
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important; animation-iteration-count: 1 !important;
        transition-duration: .01ms !important; scroll-behavior: auto !important;
    }
    /* Le sélecteur universel **n'atteint pas** les pseudo-éléments de
       transition de vue : ils vivent dans un arbre à part. Sans cette règle,
       une préférence de mouvement réduit laisserait la page glisser quand
       même. */
    ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) {
        animation: none !important;
    }
}

/* --- comportements : menu mobile ------------------------------------------
   Conditionné par le drapeau que le script pose. Sans exécution, la navigation
   reste celle qui défile horizontalement — ADR-020. */
.menu-bouton { display: none; }
@media (max-width: 40rem) {
    html[data-comportements] .menu-bouton {
        display: inline-flex; align-items: center; gap: .5em; flex: none;
        margin-left: auto; padding: .5em .9em;
        border: 1px solid var(--bordure); border-radius: var(--rayon-l);
        background: var(--surface); color: var(--texte);
        font: inherit; font-size: var(--petit); font-weight: var(--poids-appui);
        cursor: pointer;
    }
    /* Trois barres dessinées en dégradé : pas d'image, pas de pictogramme à
       rapatrier, et la couleur suit le texte. */
    html[data-comportements] .menu-barres {
        width: 1.1em; height: .7em;
        background: linear-gradient(currentColor 0 2px, transparent 2px calc(50% - 1px),
            currentColor calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px) calc(100% - 2px),
            currentColor calc(100% - 2px) 100%);
    }
    /* Le tiroir. La navigation cesse de défiler et se déploie sous l'en-tête. */
    html[data-comportements] nav[aria-label="Navigation principale"] {
        position: absolute; left: 0; right: 0; top: 100%;
        flex-direction: column; align-items: stretch; overflow: visible;
        gap: 0; margin: 0; padding: var(--espace-xs);
        background: var(--fond); border-bottom: 1px solid var(--bordure-douce);
        box-shadow: var(--ombre-2);
        /* Fermé, il ne prend **aucune place et aucun focus** : masquer par la
           hauteur ne suffirait pas, un lien invisible reste tabulable. */
        display: none;
    }
    html[data-comportements] nav[aria-label="Navigation principale"][data-ouvert] {
        display: flex;
    }
    html[data-comportements] nav[aria-label="Navigation principale"] a {
        padding: var(--espace-xs) var(--espace-2xs); white-space: normal;
    }
    html[data-comportements] nav[aria-label="Navigation principale"] a::after { display: none; }
    html[data-comportements] .nav-action { text-align: center; margin-top: var(--espace-2xs); }
    /* L'en-tête devient le repère du tiroir. */
    html[data-comportements] .entete { position: relative; }
}

/* --- rail : les contrôles, sous la bande ---------------------------------- */
.rail-controles { display: none; }
html[data-comportements] .rail-controles {
    display: flex; gap: var(--espace-2xs); justify-content: flex-end;
    margin-top: var(--espace-xs);
}
html[data-comportements] .rail-bouton {
    width: 2.4em; height: 2.4em; display: grid; place-content: center;
    border: 1px solid var(--bordure); border-radius: var(--rayon-rond);
    background: var(--surface); color: var(--texte);
    font: inherit; cursor: pointer;
    transition: background var(--duree-vive) var(--courbe),
        border-color var(--duree-vive) var(--courbe);
}
html[data-comportements] .rail-bouton:hover:not(:disabled) {
    background: var(--primaire-voile); border-color: var(--primaire);
}
/* Un bouton désactivé **doit se voir désactivé** : sans cela, le visiteur
   clique et rien ne se passe, ce qui est un contrôle mort au sens de §24. */
html[data-comportements] .rail-bouton:disabled { opacity: .38; cursor: default; }

/* --- consentement ---------------------------------------------------------
   Posée en bas, au-dessus de la pastille d'avertissement. Les deux actions ont
   **le même poids visuel** : c'est une exigence de conformité, pas un choix
   graphique. */
.consentement, .consentement-rappel { display: none; }
html[data-comportements] .consentement {
    display: grid; gap: var(--espace-xs);
    position: fixed; z-index: var(--plan-flottant);
    right: var(--espace-s); bottom: var(--espace-s);
    max-width: min(26rem, calc(100vw - var(--espace-l)));
    padding: var(--espace); border: 1px solid var(--bordure);
    border-radius: var(--rayon-xl); background: var(--surface);
    box-shadow: var(--ombre-3);
}
html[data-comportements] .consentement-texte {
    margin: 0; font-size: var(--petit); color: var(--texte-faible); max-width: none;
}
html[data-comportements] .consentement-actions { display: flex; gap: var(--espace-xs); }
html[data-comportements] .consentement .bouton { margin-top: 0; flex: 1; justify-content: center; }
/* « Tout refuser » n'est pas un lien discret sous un bouton coloré. */
html[data-comportements] .consentement .bouton[data-choix="rien"] {
    background: var(--surface); color: var(--texte); border: 1px solid var(--bordure-forte);
    box-shadow: none;
}
html[data-comportements] .consentement-rappel {
    display: inline-block; position: fixed; z-index: var(--plan-flottant);
    right: var(--espace-s); bottom: var(--espace-s);
    padding: var(--espace-3xs) var(--espace-xs);
    border: 1px solid var(--bordure-douce); border-radius: var(--rayon-rond);
    background: color-mix(in srgb, var(--surface) 88%, transparent);
    backdrop-filter: blur(10px);
    color: var(--texte-faible); font: inherit; font-size: var(--surtitre); cursor: pointer;
}

/* --- substitut autorisé ---------------------------------------------------- */
html[data-comportements] .etat-substitut[data-autorise] { border-style: solid; }

/* --- effets, quand le script les a posés ---------------------------------- */
/* **Le masque doit laisser passer les jambages.** Un titre est composé avec un
   interligne serré — 1,05 —, et la boîte de ligne s'arrête donc au-dessus du bas
   des « g », « p » et « q ». Un « overflow: hidden » posé dessus les tranche :
   « Bienvenue au centre d'art graphique » perdait le bas de ses lettres, signalé
   à l'usage. Le remplissage rend la place, la marge négative la reprend au flux :
   le masque cache toujours la ligne qui monte, sans rogner celle qui est en
   place. */
.masque-ligne { display: block; overflow: hidden;
    padding-bottom: .2em; margin-bottom: -.2em; }
.masque-ligne > .ligne-titre { display: block; }

/* Le repli étroit vient **en dernier**, hors couche : les grilles à deux
   colonnes n'ont pas de sens sur un téléphone, quoi qu'en dise une feuille. */
@media (max-width: 40rem) {
    main { padding-inline: var(--espace); }
    .entete { flex-wrap: nowrap; gap: var(--espace-xs); }
    nav[aria-label="Navigation principale"] {
        flex: 1 1 auto; min-width: 0; flex-wrap: nowrap;
        overflow-x: auto; scrollbar-width: thin; }
    .hero, .hero:has(.hero-image) { grid-template-columns: 1fr; }
    main > section:nth-of-type(even) { margin-inline: 0; padding-inline: var(--espace); }
}
