/* Adaptations WordPress seulement (composants.css reste la copie exacte de la maquette) : barre d'administration,
   classes posées par WordPress. Aucune couleur en dur : variables de tokens.css. */

/* Barre flottante sous la barre d'administration (32 px, 46 px sous 783 px) */
body.admin-bar .entete-flottante { top: 32px; }
@media (max-width: 782px) { body.admin-bar .entete-flottante { top: 46px; } }

/* Icônes à taille fixe dans une boîte flex : jamais rétrécies (ticket 033 : à 390 px, la rangée d'actions serrée
   écrasait l'icône de « Mon devis » en un point ; même règle pour toutes les icônes des composants). */
.bouton svg, .bouton-icone svg, .bouton-menu svg, .bouton-whatsapp svg, .entete .nav-icone svg, .entete .recherche-envoi svg,
.menu > li > a svg, .entete .mega-menu .mega-brochures svg, .pagination svg, .etiquette svg, .popup-pub .fermer svg { flex: none; }

/* En-tête fixe au défilement (ticket 033) : navigation.js pose est-fixe sur l'en-tête quand le repère .entete-repere
   (haut de --hauteur-entete-flottante, avant l'en-tête) est sorti par le haut ; la même barre blanche arrondie que la
   maquette se fixe alors en haut de la fenêtre, dégagée de --espace-2, et descend par entete-descend (coupée par la
   règle globale de mouvement réduit de composants.css). top n'est pas redéclaré : les règles body.admin-bar ci-dessus
   gardent la barre sous celle de l'administration. --hauteur-entete-fixe vit ici, tokens.css étant la copie verrouillée
   du design system (tests/design-system.sh) : hauteurs de .nav (76 px, 64 px sous 900 px) + deux --espace-2. */
:root { --hauteur-entete-fixe: calc(76px + 2 * var(--espace-2)); }
@media (max-width: 899px) { :root { --hauteur-entete-fixe: calc(64px + 2 * var(--espace-2)); } }
.entete-repere { position: absolute; top: 0; left: 0; width: 1px; height: var(--hauteur-entete-flottante); visibility: hidden; pointer-events: none; }
.entete-flottante.est-fixe { position: fixed; padding-top: var(--espace-2); padding-bottom: var(--espace-2); animation: entete-descend var(--duree-moyenne) var(--courbe) both; }
@keyframes entete-descend { from { transform: translateY(-100%); } to { transform: none; } }
/* Rien sous la barre fixe : ancres, scrollIntoView et déplacements de focus s'arrêtent sous elle et son dégagement
   (scroll-padding-top, à côté du scroll-padding-bottom du bouton WhatsApp plus bas, qui reste) ; le menu replié ouvert
   dans l'état fixe tient dans la fenêtre et défile seul, ses entrées et ses deux actions restent atteignables. */
html { scroll-padding-top: calc(var(--hauteur-entete-fixe) + var(--espace-2)); }
.entete-flottante.est-fixe .menu-mobile { max-height: calc(100dvh - var(--hauteur-entete-fixe)); overflow-y: auto; }

/* Barre d'onglets de la description de la fiche flottante (ticket 038) : nav.fiche-onglets colle sous l'en-tête fixe
   (ticket 033), dégagée de --espace-2, tant que son bloc conteneur (.contenu de la section sur fond pâle, celui de la
   description) est à l'écran ; elle remonte avec lui, sans jamais recouvrir « Équipements similaires ». Ici et non dans
   composants.css, copie verrouillée de la maquette (tests/design-system.sh), comme --hauteur-sommaire-fiche, hors de
   tokens.css : une ligne d'onglets (--cible-tactile) et le filet. Toujours une seule ligne, défilante quand les onglets
   ne tiennent pas (écart voulu avec la maquette sous 900 px, qui les passe à la ligne : une barre flottante de deux ou
   trois lignes mangerait un quart d'un écran de téléphone) : la hauteur de la barre est fixe, ce qu'exige le
   scroll-margin-top des titres ; overflow-y: hidden absorbe le margin-bottom: -1.5px des liens (filet vert sur le filet
   gris) sans ascenseur vertical. Aimantation (scroll-snap, bord droit des onglets, bord gauche du premier) : sans elle,
   Chromium ne fait pas défiler la barre vers un onglet atteint par Tab déjà en partie visible, qui restait coupé de
   moitié à 390 px ; aimantée, la barre montre l'onglet focalisé en entier. Ce scroll-margin-top s'ajoute au scroll-padding-top de html (en-tête fixe) : un titre
   atteint par un onglet s'arrête sous la barre, elle-même sous l'en-tête. */
:root { --hauteur-sommaire-fiche: calc(var(--cible-tactile) + var(--epaisseur-bordure)); }
.fiche-onglets { position: sticky; top: calc(var(--hauteur-entete-fixe) + var(--espace-2)); z-index: 10; background: var(--couleur-surface-pale); flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; overscroll-behavior-x: contain; scroll-padding-inline: var(--espace-2); scroll-snap-type: x mandatory; }
.fiche-onglets a { flex: 0 0 auto; white-space: nowrap; scroll-snap-align: end; }
.fiche-onglets a:first-child { scroll-snap-align: start; }
.fiche-description :is(h2, h3)[id] { scroll-margin-top: calc(var(--hauteur-sommaire-fiche) + var(--espace-2)); }

/* Texte réservé aux lecteurs d'écran : même règle que .visuellement-cache */
.screen-reader-text { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* Cibles tactiles de 24 px au moins (ticket 011, WCAG 2.5.8) : liens sans classe des paragraphes et adresses
   (téléphone, e-mail, « Mon devis ») et liens de la demande de devis ; le remplissage vertical d'un lien en ligne
   agrandit sa zone cliquable sans déplacer les lignes. */
:is(p, address) a:not([class]), .barre-liste a, .devis-vide a { padding-block: 0.25em; }

/* Bouton WhatsApp flottant (ticket 011) : la colonne « Détails » des lignes d'événement s'arrête avant son couloir
   (marge droite + bouton + espace-2) quand la gouttière de la page ne suffit pas ; à 768 et 1280 px, le premier
   « Détails » de /evenements/ était à moitié sous le bouton. 140 px : largeur du bouton avec libellé, dès 900 px. */
@media (min-width: 768px) {
  .ligne-evenement { --couloir-flottant: calc(var(--espace-4) + var(--taille-flottant) + var(--espace-2)); padding-inline-end: max(0px, calc(var(--couloir-flottant) - (100vw - min(100vw - 2 * var(--marge-page), var(--largeur-contenu))) / 2)); }
}
@media (min-width: 900px) { .ligne-evenement { --couloir-flottant: calc(var(--espace-5) + 140px + var(--espace-2)); } }

/* Popup « Mise en avant » (ticket 011) : dès 1800 px, il tient dans la gouttière gauche de la page ; à 1920 px, il
   couvrait l'angle du bouton « Demander un devis » et les chiffres du héros de l'accueil. */
@media (min-width: 1800px) {
  .popup-pub { width: min(var(--largeur-popup), calc((100% - var(--largeur-contenu)) / 2 - 2 * var(--espace-5))); }
}

/* Images dimensionnées (ticket 011) : width et height réservent la place pendant le chargement, la hauteur suit la
   largeur rendue comme sans attributs ; sans poids (:where) pour laisser gagner les hauteurs de composants.css
   (.heros-photo img, .carte-photo img…). Sans elle, .fiche-photo img passait de 464 × 464 à 464 × 560 px. */
:where(img[width][height]) { height: auto; }

/* Alignements et légendes des images de l'éditeur */
img.alignleft, .wp-caption.alignleft { float: left; margin: 0 var(--espace-5) var(--espace-4) 0; }
img.alignright, .wp-caption.alignright { float: right; margin: 0 0 var(--espace-4) var(--espace-5); }
img.aligncenter, .wp-caption.aligncenter { display: block; margin-inline: auto; }
.wp-caption { max-width: 100%; }
.wp-caption img { display: block; max-width: 100%; height: auto; }
.wp-caption-text { margin-top: var(--espace-2); color: var(--couleur-texte-2); font-size: var(--taille-s); }

/* Contenus injectés depuis contenus/pages/ (ticket 005) : texte courant */
.prose > * { max-width: var(--largeur-texte); }
.prose h2 { margin-top: var(--espace-7); }
.prose h3 { margin-top: var(--espace-6); }
.prose > :first-child { margin-top: 0; }
.prose ul, .prose ol { margin: 0 0 var(--espace-5); }
.prose li + li { margin-top: var(--espace-2); }

/* Agences des pages légales (inc/legal.php ; Contact : bloc « ticket 046 ») */
.carte-agence { padding: var(--espace-5); background: var(--couleur-surface); border-radius: var(--rayon-m); box-shadow: var(--ombre-douce); }
.carte-agence address { font-style: normal; }
.carte-agence p:last-child, .contact-email { margin-bottom: 0; }
.contact-email { margin-top: var(--espace-5); }

/* Navigation (ticket 006) : lien d'évitement visible au focus, menu replié absent de la barre complète, appels à
   l'action du menu replié, anneau de focus lisible sur le fond nuit */
.lien-evitement { position: fixed; top: var(--espace-2); left: var(--espace-2); z-index: 100; display: inline-flex; align-items: center; min-height: var(--cible-tactile); padding: 0 var(--espace-4); background: var(--couleur-bleu); color: var(--couleur-surface); font-weight: var(--graisse-forte); text-decoration: none; border-radius: var(--rayon-s); transform: translateY(calc(-100% - var(--espace-4))); }
.lien-evitement:focus { transform: none; }
main:focus { outline: none; }
/* Cibles tactiles de la barre : 44 px même à 390 px (sans flex: none, l'icône téléphone y tombe à 40 px) */
.nav-actions .nav-icone, .nav-actions .bouton-menu { flex: none; }
/* Ticket 023 : la loupe ajoutée aux actions ne comprime pas le logo (107 px de la maquette à 390 px) ; la place vient
   des écarts, resserrés sous 600 px. Sous 375 px, le logo peut encore rétrécir plutôt que déborder. */
@media (max-width: 599px) {
  .entete .nav { gap: var(--espace-2); }
  .entete .nav-actions { gap: var(--espace-1); }
}
@media (min-width: 375px) { .entete .logo { flex: none; } }
@media (min-width: 900px) { .menu-mobile { display: none; } }
.menu-mobile :focus-visible { box-shadow: 0 0 0 3px var(--couleur-nuit), 0 0 0 6px var(--couleur-focus); }
.menu-mobile .menu-mobile-actions { display: grid; gap: var(--espace-3); margin: 0; }
.menu-mobile .menu-mobile-actions .bouton { justify-content: center; width: 100%; min-height: var(--cible-tactile); padding: 0 var(--espace-5); border: var(--epaisseur-bordure) solid transparent; font-family: var(--police-texte); font-weight: var(--graisse-forte); font-size: var(--taille-base); }
/* Bas de page : le bandeau légal se termine au-dessus du bouton WhatsApp flottant (sinon il recouvre « Politique de
   confidentialité » à 768 et 1280 px) */
.pied-bas .contenu { padding-bottom: calc(var(--taille-flottant) + 2 * var(--espace-5)); }
/* Méga-menu « Catalogue » et menu déroulant « À propos » (tickets 034 et 052) : panneau toujours opaque (un fondu laisserait transparaître le héros sous
   ses textes), révélé de haut en bas par clip-path et descendu de --espace-2 ; --duree-moyenne à l'ouverture (durée
   déclarée sur l'état ouvert), --duree-courte à la fermeture (sur l'état fermé). Ouvert, la découpe déborde de
   --espace-9 pour laisser l'ombre (--ombre-haute) entière. display reste affiché pendant la fermeture
   (allow-discrete), le panneau y est inert (navigation.js). @starting-style donne le départ de l'ouverture depuis
   display: none. Navigateur sans ces deux règles : apparition sèche, comme avant. Mouvement réduit : coupé plus bas.
   est-maintenue (navigation.js, ticket 052) : entrée quittée par la souris dans sa zone d'accès, gardée ouverte.
   « À propos » (ticket 052) : même découpe, même descente, mêmes durées et même inertie que le méga-menu. */
.mega-menu, .entete .menu-rubrique > .sub-menu { clip-path: inset(0 0 100% 0); transform: translateY(calc(-1 * var(--espace-2))); transition: clip-path var(--duree-courte) var(--courbe), transform var(--duree-courte) var(--courbe), display var(--duree-courte) allow-discrete; }
.menu-catalogue:hover .mega-menu, .menu-catalogue:focus-within .mega-menu, .menu-catalogue.est-maintenue .mega-menu, .mega-menu.est-ouvert, .entete .menu-rubrique:hover > .sub-menu, .entete .menu-rubrique:focus-within > .sub-menu, .entete .menu-rubrique.est-maintenue > .sub-menu { clip-path: inset(calc(-1 * var(--espace-9))); transform: none; transition: clip-path var(--duree-moyenne) var(--courbe), transform var(--duree-moyenne) var(--courbe), display var(--duree-moyenne) allow-discrete; }
@starting-style {
  .menu-catalogue:hover .mega-menu, .menu-catalogue:focus-within .mega-menu, .menu-catalogue.est-maintenue .mega-menu, .mega-menu.est-ouvert, .entete .menu-rubrique:hover > .sub-menu, .entete .menu-rubrique:focus-within > .sub-menu, .entete .menu-rubrique.est-maintenue > .sub-menu { clip-path: inset(0 0 100% 0); transform: translateY(calc(-1 * var(--espace-2))); }
}
/* Refermé par Échap (navigation.js) malgré le survol ou le focus, en --duree-courte ; absent sous 900 px */
.menu-catalogue.est-replie .mega-menu, .entete .menu-rubrique.est-replie > .sub-menu { display: none; clip-path: inset(0 0 100% 0); transform: translateY(calc(-1 * var(--espace-2))); transition: clip-path var(--duree-courte) var(--courbe), transform var(--duree-courte) var(--courbe), display var(--duree-courte) allow-discrete; }
/* Mouvement réduit : aucune transition (celle de 0,01 ms de composants.css laisse le panneau découpé jusqu'à
   l'image suivante, un lien atteint au clavier aussitôt serait relevé masqué) */
@media (prefers-reduced-motion: reduce) {
  .mega-menu, .entete .menu-rubrique > .sub-menu, .menu-catalogue:hover .mega-menu, .menu-catalogue:focus-within .mega-menu, .menu-catalogue.est-maintenue .mega-menu, .mega-menu.est-ouvert, .entete .menu-rubrique:hover > .sub-menu, .entete .menu-rubrique:focus-within > .sub-menu, .entete .menu-rubrique.est-maintenue > .sub-menu, .menu-catalogue.est-replie .mega-menu, .entete .menu-rubrique.est-replie > .sub-menu { transition: none; }
}
/* Zone d'accès du méga-menu (ticket 052, corrige le ticket 034) : le lien « Catalogue » (--cible-tactile) est centré
   dans la barre (76 px) et le panneau commence --espace-2 au-dessus du bas de l'en-tête : 8 px vides sous le lien en
   en-tête absolu, 16 px en en-tête fixe (padding-bottom de est-fixe). Le pointeur y quittait l'entrée et le panneau se
   refermait. Un pont transparent sous le lien de l'entrée survolée couvre cet écart (--pont-menu : 16 px et 4 px de
   recouvrement, hors de tokens.css, copie verrouillée), sans JavaScript ; ::before, car ::after porte le soulignement
   de l'entrée courante (composants.css). Le panneau tient dans la fenêtre et défile seul : overscroll-behavior garde la
   molette dans le panneau arrivé en bas, la page ne remonte pas l'en-tête absolu sous le pointeur ; son bas s'arrête
   au-dessus du scroll-padding-bottom de html (bouton WhatsApp, même calcul plus bas) : un lien focalisé au Tab en bas du
   panneau n'y entre jamais, seul le panneau défile, pas la page sous l'en-tête absolu. Entrée maintenue
   (est-maintenue, navigation.js : souris sortie de l'entrée dans la zone d'accès, hors du pont) : pont gardé et panneau
   affiché malgré le display: none de composants.css, qui s'applique dès que :hover est perdu ; Échap (est-replie)
   l'emporte. « À propos » a le même pont, le même maintien et la même ligne de départ que le méga-menu : son li
   (--cible-tactile) est centré dans la barre, le sous-menu part --espace-2 sous le li en en-tête absolu (le méga-menu,
   8 px au-dessus du bas de la barre) et 2 × --espace-2 sous le li en en-tête fixe (le méga-menu, au bas de la barre). */
:root { --pont-menu: calc(2 * var(--espace-2) + var(--espace-1)); }
@media (min-width: 900px) {
  .entete :is(.menu-catalogue, .menu-rubrique):is(:hover, .est-maintenue):not(.est-replie) > a::before { content: ""; position: absolute; top: 100%; left: 0; right: 0; height: var(--pont-menu); }
  .entete .menu-catalogue.est-maintenue:not(.est-replie) .mega-menu, .entete .menu-rubrique.est-maintenue:not(.est-replie) > .sub-menu { display: block; }
  .entete .menu-rubrique > .sub-menu { top: calc(100% + var(--espace-2)); }
  .entete-flottante.est-fixe .menu-rubrique > .sub-menu { top: calc(100% + 2 * var(--espace-2)); }
  .entete .mega-menu { max-height: calc(100dvh - var(--hauteur-entete-fixe) - var(--taille-flottant) - 2 * var(--espace-5)); overflow-y: auto; overscroll-behavior: contain; }
}
@media (max-width: 899px) { .entete .mega-menu { display: none; } }

/* Accueil (ticket 007) : cadrage de la photo du héros, comme dans la maquette (style inline de accueil.html) */
.home .heros-plein .heros-photo img { object-position: 70% 50%; }

/* Accueil (ticket 007) : section « Marques » et lignes d'événements, à la place des styles inline de accueil.html */
.grille-marques { align-items: center; gap: var(--espace-8); }
.grille-marques .chiffres { margin-top: var(--espace-6); }
.ligne-evenement h3 { margin-top: var(--espace-2); }
.ligne-evenement .meta { margin: 0; }
.ligne-evenement.sans-date > div { grid-column: 2; }
/* Contenus venus de la copie : préréglages de couleur de l'ancien thème (« primary », « white ») absents de theme.json ;
   sans ces règles, un paragraphe blanc sur fond « primary » devient blanc sur blanc. */
.has-primary-color { color: var(--couleur-bleu); }
.has-primary-background-color { background-color: var(--couleur-bleu); }
.has-white-color { color: var(--couleur-texte-inverse); }
/* Bloc Bouton des contenus : aux couleurs et au rayon de la charte (.bouton-primaire), y compris contre les couleurs,
   dégradés et rayons posés en style en ligne dans l'ancien éditeur. */
main .wp-block-button__link { display: inline-flex; align-items: center; justify-content: center; min-height: var(--cible-tactile); padding: var(--espace-2) var(--espace-5); font-family: var(--police-texte); font-weight: var(--graisse-forte); font-size: var(--taille-base); line-height: 1.2; text-decoration: none; background: var(--couleur-bleu) !important; color: var(--couleur-texte-inverse) !important; border: var(--epaisseur-bordure) solid transparent; border-radius: var(--rayon-s) !important; }
main .wp-block-button__link:hover { background: var(--couleur-bleu-fonce) !important; }
/* Sous 900 px, logos sous le texte : à deux colonnes (maquette à 768 px), les cases des logos font 74 px et les logos
   larges tombent sous 12 px de haut (écart noté dans la fiche du ticket 007) */
@media (max-width: 899px) { .grille-marques { grid-template-columns: minmax(0, 1fr); } }

/* Accueil, section « Demande spécifique » (#devis) : cadrage de la photo de fond (style inline de la maquette) et
   message d'état après envoi (le message d'erreur commence par « Erreur : », tokens.css n'a pas de couleur d'erreur) */
#devis .appel-photo-fond img { object-position: 30% 50%; }
.encart-devis .message-etat { margin: 0 0 var(--espace-4); font-weight: var(--graisse-forte); }
.encart-devis .message-etat.est-envoyee { color: var(--couleur-vert-texte); }
.encart-devis .message-etat.est-erreur { color: var(--couleur-texte); }

/* Accueil (ticket 007) : popup fermé par accueil.js (le display: grid de composants.css l'emporte sinon sur hidden) */
.popup-pub[hidden] { display: none; }

/* Catalogue (ticket 008) : texte de la page dans la tête (comme le .texte-2 de equipements.html), total en couleur de
   texte (style inline de la maquette), groupes de filtres longs (59 marques) défilants à la hauteur du groupe Domaine
   (9 entrées), message d'une sélection vide, boutons « Hors liste » calés à droite (style inline de la maquette).
   Équipements, Mon devis et Événements rendent désormais une accroche .texte-2 et la suite sous la tête (ticket 041) :
   plus aucun .prose dans la tête */
.barre-liste strong { color: var(--couleur-texte); }
.filtres-groupe ul { max-height: calc(9 * var(--cible-tactile) + var(--espace-3)); overflow-y: auto; overscroll-behavior: contain; }
.catalogue-vide { margin: 0; padding: var(--espace-6); background: var(--couleur-surface); border-radius: var(--rayon-m); box-shadow: var(--ombre-douce); font-weight: var(--graisse-moyenne); }
.catalogue-vide a { margin-left: var(--espace-2); font-weight: var(--graisse-forte); }
section[aria-labelledby="titre-hors-liste"] .groupe-boutons { justify-self: end; }

/* Entêtes bleues (ticket 041) : le texte sous le titre de la bande nuit (accroche d'Équipements, de Mon devis et d'Événements,
   résumé de la fiche, méta d'un événement ou d'une actualité) tient en trois lignes au plus, pour que les bandes
   gardent une hauteur voisine ; au-delà, il finit par « … ». Le titre, le surtitre et le fil d'Ariane ne sont jamais
   tronqués. L'accroche réserve deux lignes : une accroche
   d'une ligne (dès 768 px) laissait sa bande plus de 1,25 fois plus basse que celle de la fiche, dont le résumé, à côté
   de la photo remontée, prend trois lignes. */
.tete-page.sur-nuit .texte-2 { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
.tete-page.sur-nuit .accroche { min-height: 2lh; }

/* Popup « Mise en avant » générique (ticket 042) : la photo d'un événement remplit sa case (cover, sans fond pâle ni
   multiply : ce n'est pas un détourage sur blanc) ; sans image, le texte prend toute la largeur ; le titre tient en
   trois lignes au plus (titres d'événements jusqu'à 131 caractères). Ici et non dans composants.css, copie conforme
   de styles.css (tests/design-system.sh). */
.popup-pub.popup-evenement .popup-photo { padding: 0; overflow: hidden; }
.popup-pub.popup-evenement .popup-photo img { object-fit: cover; mix-blend-mode: normal; }
.popup-pub.sans-image { grid-template-columns: minmax(0, 1fr); }
.popup-pub h2 { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }

/* Catalogue sans rechargement (ticket 022) : liste estompée pendant le chargement ; la liste ne reçoit le focus que par
   le script (après une page), sans anneau : les liens des filtres et de la pagination gardent le leur. */
.catalogue-liste[aria-busy="true"] .grille, .catalogue-liste[aria-busy="true"] .catalogue-vide { opacity: .6; }
.catalogue-liste:focus, .catalogue-liste:focus-visible { outline: none; box-shadow: none; }

/* Actions du devis sans rechargement (ticket 022) : bouton et lignes estompés pendant la requête ; message d'erreur de
   la fiche en couleur de texte (tokens.css n'a pas de couleur d'erreur, même convention que le récapitulatif) */
.formulaire-devis[aria-busy="true"] .bouton-primaire, .lignes-devis[aria-busy="true"] .ligne-devis { opacity: .6; }
.fiche-infos .message-etat.est-erreur { color: var(--couleur-texte); }

/* Fiche (ticket 008) : description en texte courant (titres de la maquette en taille xl), case sans photo, message
   d'état au-dessus du formulaire, notes sous les boutons (styles inline de equipement-microscope-primostar-3.html) */
.fiche-description h2 { font-size: var(--taille-xl); }
.fiche-description h3 { font-size: var(--taille-l); }
.fiche-description table { width: 100%; }
/* Ticket 021 : un tableau large de la description défile dans son conteneur, sans défilement de la page à 390 px */
.fiche-description .tableau-defilant { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.fiche-description .tableau-defilant table { min-width: 100%; }
/* Élément de liste de la description qui contient un tableau : pas de puce isolée devant lui */
.fiche-description li:has(> .tableau-defilant) { list-style: none; }
.fiche-description img { max-width: 100%; height: auto; }
.fiche-photo-vide { display: grid; place-items: center; width: 100%; min-height: 240px; color: var(--couleur-texte-2); }
.fiche-infos .message-etat { margin: 0 0 var(--espace-4); font-weight: var(--graisse-forte); }
.fiche-infos #fiche-pdf { font-size: var(--taille-s); margin-top: var(--espace-3); }
.fiche-infos .fiche-question { font-size: var(--taille-s); margin: 0; }
.fiche-miniatures { flex-wrap: wrap; }
/* Ticket 036 : galerie de la fiche (assets/js/galerie.js). Une vue cachée reste cachée malgré « img { display: block } » ;
   sans JavaScript (pas de data-galerie), première photo seule : miniatures et « Agrandir » cachés */
.fiche-photo img[hidden] { display: none; }
.fiche-miniatures:not([data-galerie]), .fiche-agrandir:not([data-galerie]) { display: none; }
/* Ticket 036 : visionneuse (<dialog> plein écran, fond nuit à 85 %) : image entière, Précédent et Suivant sur les bords,
   Fermer en haut à droite, compteur sous l'image ; sous 600 px, Précédent et Suivant passent sous l'image, de part et
   d'autre du compteur, pour ne pas couvrir la photo, et la photo prend 80svh de haut (90vw l'aurait laissée à la taille
   du cadre de la fiche) dans un cadre qui défile en largeur, centré par galerie.js, posé en bas de sa rangée pour
   dégager Fermer ; fondu d'ouverture coupé sous mouvement réduit */
.fiche-agrandir { position: absolute; right: var(--espace-3); bottom: var(--espace-3); display: grid; place-items: center; width: var(--cible-tactile); height: var(--cible-tactile); padding: 0; border: 0; border-radius: var(--rayon-s); background: var(--couleur-surface); color: var(--couleur-nuit); box-shadow: var(--ombre-douce); cursor: pointer; }
.fiche-photo img[data-vue] { cursor: zoom-in; }
.fiche-agrandir svg, .visionneuse button svg { width: 24px; height: 24px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.visionneuse { border: 0; padding: 0; background: transparent; max-width: 100vw; max-height: 100svh; }
.visionneuse[open] { box-sizing: border-box; width: 100vw; height: 100svh; margin: 0; padding: var(--espace-4); display: grid; grid-template-columns: auto minmax(0, 1fr) auto; grid-template-rows: minmax(0, 1fr) auto; align-items: center; justify-items: center; gap: var(--espace-3); }
.visionneuse::backdrop { background: color-mix(in srgb, var(--couleur-nuit) 85%, transparent); }
.visionneuse-cadre { grid-column: 2; grid-row: 1; }
.visionneuse-cadre img { max-width: 90vw; max-height: 80svh; width: auto; height: auto; object-fit: contain; }
.visionneuse img[hidden] { display: none; }
.visionneuse-precedent, .visionneuse-suivant, .visionneuse-fermer { display: grid; place-items: center; width: 48px; height: 48px; padding: 0; border: 0; border-radius: 50%; background: var(--couleur-nuit); color: var(--couleur-surface); cursor: pointer; }
.visionneuse-precedent { grid-column: 1; grid-row: 1; }
.visionneuse-suivant { grid-column: 3; grid-row: 1; }
.visionneuse-fermer { position: absolute; top: var(--espace-4); right: var(--espace-4); }
.visionneuse-compteur { grid-column: 2; grid-row: 2; margin: 0; color: var(--couleur-surface); font-size: var(--taille-s); }
@media (max-width: 599px) {
  .visionneuse-cadre { grid-column: 1 / -1; justify-self: stretch; align-self: end; display: flex; min-width: 0; overflow-x: auto; overscroll-behavior-x: contain; }
  .visionneuse-cadre img { max-width: none; height: 80svh; margin-inline: auto; flex: none; }
  .visionneuse-precedent, .visionneuse-suivant { grid-row: 2; }
}
@media (prefers-reduced-motion: no-preference) {
  .visionneuse[open] { animation: visionneuse-fondu var(--duree-courte) ease-out; }
}
@keyframes visionneuse-fondu { from { opacity: 0; } }
/* Ticket 037 : barre « plus disponible » en première rangée de la grille du cadre (l'image en seconde, jamais
   recouverte), collée en haut à droite à --espace-3 du bord malgré le padding du cadre (--espace-7, --espace-5 sous
   900 px) ; sous 600 px, pleine largeur du cadre à --espace-3 des deux bords. Icône à côté de l'état seulement : le
   lien vers le remplacement prend toute la largeur de la barre (titre long sur 3 lignes au plus) ; 60 % du cadre, padding
   compris (un pourcentage seul se rapporterait à sa zone de contenu) */
.fiche-photo.est-indisponible { grid-template-rows: auto minmax(0, 1fr); }
.fiche-indisponible { justify-self: end; align-self: start; z-index: 1; display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: var(--espace-3); align-items: center; max-width: calc((100% + 2 * var(--espace-7)) * 0.6); margin: calc(var(--espace-3) - var(--espace-7)) calc(var(--espace-3) - var(--espace-7)) var(--espace-4) 0; padding: var(--espace-3) var(--espace-4); border-radius: var(--rayon-m); background: var(--couleur-bleu); color: var(--couleur-texte-inverse); box-shadow: var(--ombre-douce); overflow-wrap: anywhere; }
.fiche-indisponible > div { display: contents; }
.fiche-indisponible svg { width: 24px; height: 24px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.fiche-indisponible p { margin: 0; }
.fiche-indisponible-etat { font-size: var(--taille-xs); font-weight: var(--graisse-forte); letter-spacing: var(--interlettrage-etiquette); text-transform: uppercase; }
.fiche-indisponible .fiche-indisponible-suite { grid-column: 1 / -1; font-size: var(--taille-s); margin-top: var(--espace-1); }
.fiche-indisponible a { color: inherit; font-weight: var(--graisse-forte); text-decoration: underline; text-underline-offset: 0.15em; }
@media (max-width: 899px) { .fiche-indisponible { max-width: calc((100% + 2 * var(--espace-5)) * 0.6); margin-top: calc(var(--espace-3) - var(--espace-5)); margin-right: calc(var(--espace-3) - var(--espace-5)); } }
@media (max-width: 599px) { .fiche-indisponible { justify-self: stretch; max-width: none; margin-left: calc(var(--espace-3) - var(--espace-5)); } }
/* Sous 900 px, le visuel suit les infos : sans remontée dans la bande (la règle de composants.css, placée après son
   media query, l'appliquait aussi et la photo recouvrait la note sous les boutons) */
@media (max-width: 899px) { .fiche-tete > .fiche-visuel { margin-top: 0; } }
/* Au-delà de 900 px, le texte de la tête s'arrête à la colonne de gauche : la photo remonte dans la bande à droite et
   recouvrait la fin d'un résumé long */
@media (min-width: 900px) { .tete-fiche .apparait-sequence { max-width: calc((100% - var(--espace-8)) / 2); } }
/* Ticket 024 : titre réduit par paliers selon sa longueur (classe posée par ams_labo_fiche_titre_classe, au-delà de 35
   puis de 70 caractères), replié au mot entier sans césure (la maquette ne coupe jamais un titre ; overflow-wrap en
   secours pour un mot plus large que la colonne) ; résumé long déplacé en tête de la colonne infos (taille et couleur
   de composants.css), au-dessus des données */
.tete-fiche h1 { overflow-wrap: break-word; }
.tete-fiche h1.titre-long { font-size: var(--taille-2xl); max-width: 28ch; }
.tete-fiche h1.titre-tres-long { font-size: var(--taille-xl); max-width: 40ch; }
.fiche-infos .resume { margin: 0 0 var(--espace-5); }

/* Mon devis (ticket 008) : message d'état au-dessus des colonnes, titre du récapitulatif en taille xl (style inline de
   mon-devis.html), bouton « Mettre à jour les quantités » sous les lignes (envoi sans JavaScript), demande vide */
.sur-pale > .contenu > .message-etat { margin: 0 0 var(--espace-5); font-weight: var(--graisse-forte); }
.devis-recap h2 { font-size: var(--taille-xl); }
.lignes-devis > .groupe-boutons { justify-content: flex-end; }
.devis-vide { padding: var(--espace-6); background: var(--couleur-surface); border-radius: var(--rayon-m); box-shadow: var(--ombre-douce); }
.devis-vide p { margin: 0 0 var(--espace-3); font-weight: var(--graisse-moyenne); }
.devis-vide a { font-weight: var(--graisse-forte); }

/* Pages intérieures (ticket 008) : bannière d'un événement, cartes d'articles, logos de la page Partenaires (lien
   autour du logo, anneau de focus sur nuit de composants.css), brochure sans PDF (carte sans téléchargement, même aspect, lien « Sur demande » vers la demande de devis souligné au survol et au focus) ;
   liste qui suit le texte de la page sans second espacement de section (hors bande colorée, comme les agences de Contact) */
.section.prose + .section:not([class*="sur-"]) { padding-top: 0; }
.evenement-banniere img { display: block; width: 100%; height: auto; border-radius: var(--rayon-m); margin-bottom: var(--espace-6); }
.evenement-liens { display: flex; flex-wrap: wrap; gap: var(--espace-3); margin-top: var(--espace-6); }
.evenement-inscription { margin-top: var(--espace-6); padding: var(--espace-5); background: var(--couleur-surface-bleutee); border-radius: var(--rayon-m); }
.evenement-inscription h2 { font-size: var(--taille-l); margin: 0 0 var(--espace-2); }
.evenement-inscription p { margin: 0 0 var(--espace-3); }
.evenement-inscription p:last-child { margin-bottom: 0; }
.liste-articles { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: var(--espace-5); }
.liste-articles article { padding: var(--espace-5); background: var(--couleur-surface); border-radius: var(--rayon-m); box-shadow: var(--ombre-douce); min-width: 0; }
.liste-articles h3 { font-size: var(--taille-m); margin: 0 0 var(--espace-2); overflow-wrap: anywhere; }
.liste-articles .meta { font-size: var(--taille-s); color: var(--couleur-texte-2); margin: 0 0 var(--espace-3); }
.marques-page .logos-marques li { padding: 0; }
.marques-page a, .marques-page figure { display: grid; place-items: center; width: 100%; height: 100%; min-height: 88px; margin: 0; padding: var(--espace-3) var(--espace-4); color: inherit; text-decoration: none; }
.marques-page a figure { padding: 0; min-height: 0; }
.marques-page a:hover img { opacity: 1; }
div.brochure.brochure-sans-pdf { cursor: default; }
div.brochure.brochure-sans-pdf:hover { transform: none; box-shadow: var(--ombre-douce); }
div.brochure.brochure-sans-pdf .lien { text-decoration: none; }
div.brochure.brochure-sans-pdf .lien:hover, div.brochure.brochure-sans-pdf .lien:focus-visible { text-decoration: underline; }

/* Formulaires (ticket 009) : état d'erreur absent de la maquette (résumé des erreurs focalisé, liste vers les champs),
   select en erreur comme les autres saisies ; Contact : formulaire « Nous écrire » à deux colonnes dès 768 px,
   message, bouton et téléphone sur toute la largeur ; messages d'état au-dessus du formulaire. */
.champ.champ-erreur select { border-color: var(--couleur-erreur, var(--couleur-bleu-fonce)); border-width: 2px; }
.resume-erreurs { border-left: 4px solid var(--couleur-bleu-fonce); background: var(--couleur-surface-pale); border-radius: var(--rayon-s); padding: var(--espace-4) var(--espace-5); margin: 0 0 var(--espace-5); }
.resume-erreurs:focus-visible { outline: 2px solid transparent; box-shadow: var(--anneau-focus); }
.resume-erreurs-titre { margin: 0 0 var(--espace-2); font-weight: var(--graisse-forte); }
.resume-erreurs ul { margin: 0; padding-left: var(--espace-5); display: grid; gap: var(--espace-1); }
.resume-erreurs a { color: var(--couleur-texte); font-weight: var(--graisse-forte); text-decoration: underline; }
.formulaire-contact { max-width: 880px; }
@media (min-width: 768px) {
  .formulaire-contact { grid-template-columns: 1fr 1fr; }
  .formulaire-contact .champ-message, .formulaire-contact .groupe-boutons, .formulaire-contact > .aide { grid-column: 1 / -1; }
}
.formulaire-contact > .aide { margin: 0; font-size: var(--taille-s); color: var(--couleur-texte-2); }
.section-contact .message-etat, .contact-etat .message-etat { margin: 0 0 var(--espace-5); font-weight: var(--graisse-forte); }
.contact-etat .message-etat.est-envoyee { color: var(--couleur-vert-texte); }

/* Pagination sous 600 px : la maquette masque « Précédent » et « Suivant » par display: none, ce qui laisse les liens
   fléchés sans nom accessible (svg aria-hidden). Le libellé n'est masqué qu'à l'écran et reste leur nom. */
@media (max-width: 599px) {
  .pagination .pagination-bord .libelle { display: inline; position: absolute; width: 1px; height: 1px; min-width: 0; min-height: 0; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}

/* Révélation au défilement et clavier (ticket 011) : un élément focalisé dans un .apparait est montré sans attendre la
   translation ni son décalage de séquence (jusqu'à 330 ms + --duree-longue), sinon le focus se pose sur un bloc encore
   en mouvement. */
.apparait:focus-within { transform: none; transition: none; }

/* Défilement doux de la maquette (html, composants.css) gardé pour la souris ; au clavier, le défilement vers l'élément
   focalisé est immédiat : sinon un champ du bas de page reste hors de la fenêtre jusqu'à 1,5 s après le focus. */
html:has(:focus-visible) { scroll-behavior: auto; }

/* Vidéo du héros (ticket 012) : masquée tant que accueil.js n'a pas posé data-video="lecture" (événement playing), la
   photo dessous sert de repli ; jamais visible sous 900 px ni sous mouvement réduit. Le filtre (opacité, gris) vient de
   la règle « .heros-photo video » de composants.css. */
.heros-video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; display: none; }
.heros-photo[data-video="lecture"] .heros-video { display: block; }
.heros-photo[data-video="lecture"] img { visibility: hidden; }
@media (max-width: 899px), (prefers-reduced-motion: reduce) { .heros-photo[data-video] .heros-video { display: none; } .heros-photo[data-video] img { visibility: visible; } }
/* Ticket 051 : la vidéo est plus claire que la photo sous le texte (surtitre, accroche, partie verte du titre sous les seuils) :
   pendant sa lecture seulement, voile de gauche renforcé (nuit pleine au bord, --couleur-voile-nuit jusqu'à la fin de
   l'accroche) ; la photo garde le voile de composants.css, la moitié droite reste sans voile. */
@media (min-width: 900px) and (prefers-reduced-motion: no-preference) {
  .heros-plein .heros-photo[data-video="lecture"] + .heros-voile { background: linear-gradient(90deg, var(--couleur-nuit) 0%, var(--couleur-voile-nuit) 48%, var(--couleur-voile-heros) 60%, transparent 72%); }
}

/* Recherche de l'entête (ticket 023, absente des maquettes, composants du design system) : dès 1280 px, champ en ligne
   dans la barre, suggestions en carte sous le champ ; en dessous, panneau blanc sous la barre (même calage que le
   méga-menu), ouvert par la loupe (navigation.js) ; la hauteur de l'entête ne change pas. Dès 1280 px, la barre
   (1 200 px au plus) est serrée pour garder un champ d'au moins 120 px sans renvoyer « À propos » ni « Mon devis » à
   la ligne : écarts et marges des entrées du menu réduits, loupe d'envoi et attente posées dans le champ. */
.recherche-entete { display: flex; align-items: center; gap: var(--espace-2); position: relative; }
.entete .bouton-recherche, .entete .recherche-envoi { flex: none; padding: 0; border: 0; background: transparent; cursor: pointer; }
.entete .recherche-envoi svg { width: 20px; height: 20px; }
.recherche-champ { width: 100%; min-width: 0; min-height: 40px; padding-inline: var(--espace-3); font-family: var(--police-texte); font-size: var(--taille-s); color: var(--couleur-texte); background: var(--couleur-surface); border: var(--epaisseur-bordure) solid var(--couleur-bordure); border-radius: var(--rayon-s); }
.recherche-champ::placeholder { color: var(--couleur-texte-2); opacity: 1; }
.recherche-champ:focus-visible { outline: none; box-shadow: var(--anneau-focus); }
.recherche-champ::-webkit-search-cancel-button { display: none; }
.recherche-attente { flex: none; width: 16px; height: 16px; border: 2px solid var(--couleur-bordure); border-top-color: var(--couleur-bleu); border-radius: 50%; animation: rotation 0.8s linear infinite; }
.recherche-attente[hidden] { display: none; }
@keyframes rotation { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .recherche-attente { animation: none; } }
.recherche-suggestions { background: var(--couleur-surface); border-radius: var(--rayon-m); box-shadow: var(--ombre-haute); max-height: min(70vh, 560px); overflow-y: auto; padding: var(--espace-2); z-index: 30; }
.recherche-suggestions[hidden] { display: none; }
.suggestion { display: flex; align-items: center; gap: var(--espace-3); min-height: 52px; padding: var(--espace-2) var(--espace-3); border-radius: var(--rayon-s); color: var(--couleur-texte); text-decoration: none; }
.suggestion:hover, .suggestion.est-active { background: var(--couleur-surface-pale); }
.suggestion.est-active { box-shadow: var(--anneau-focus); }
.suggestion-photo { flex: none; display: block; width: 48px; height: 48px; overflow: hidden; background: var(--couleur-surface-pale); border-radius: var(--rayon-s); }
.suggestion-photo img { width: 100%; height: 100%; object-fit: contain; }
.suggestion-corps { display: flex; flex-direction: column; min-width: 0; }
.suggestion-titre { font-weight: var(--graisse-forte); font-size: var(--taille-s); display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
.suggestion-meta { font-size: var(--taille-xs); color: var(--couleur-texte-2); }
.suggestion mark { background: transparent; color: var(--couleur-bleu); font-weight: var(--graisse-forte); }
.suggestion-tout { margin-top: var(--espace-2); border-top: var(--epaisseur-bordure) solid var(--couleur-filet); border-radius: 0; color: var(--couleur-bleu); font-weight: var(--graisse-forte); }
.recherche-vide { margin: 0; padding: var(--espace-3); font-size: var(--taille-s); }
@media (min-width: 1280px) {
  .entete .nav { gap: var(--espace-4); }
  .entete .menu { gap: 0; }
  .entete .menu a { padding-inline: var(--espace-2); white-space: nowrap; }
  .entete .menu a[aria-current="page"]::after { left: var(--espace-2); right: var(--espace-2); }
  .entete .bouton-devis { white-space: nowrap; }
  .recherche-entete { flex: 1 1 140px; max-width: 260px; }
  .recherche-champ { padding-inline-end: var(--cible-tactile); }
  .entete .recherche-envoi { position: absolute; right: 0; inset-block: 0; margin-block: auto; }
  .recherche-attente { position: absolute; right: var(--cible-tactile); top: 50%; margin-top: -8px; }
  .entete .bouton-recherche { display: none; }
  .recherche-suggestions { position: absolute; top: calc(100% + var(--espace-2)); right: 0; width: min(480px, calc(100vw - 2 * var(--marge-page))); }
}
@media (max-width: 1279px) {
  .recherche-entete { position: absolute; top: calc(100% + var(--espace-2)); left: max(var(--marge-page), calc((100% - var(--largeur-contenu)) / 2)); right: max(var(--marge-page), calc((100% - var(--largeur-contenu)) / 2)); flex-wrap: wrap; max-width: none; padding: var(--espace-3) var(--espace-4); background: var(--couleur-surface); border-radius: var(--rayon-m); box-shadow: var(--ombre-haute); z-index: 30; display: none; }
  .recherche-entete.est-ouverte { display: flex; }
  .recherche-champ { flex: 1 1 0; }
  .recherche-suggestions { position: static; flex-basis: 100%; width: 100%; margin-top: var(--espace-3); box-shadow: none; padding: 0; }
}

/* Bouton WhatsApp flottant (ticket 027) : anneau de focus du design system (l'ombre du bouton, déclarée après
   :focus-visible dans composants.css, l'effaçait) ; défilement au clavier qui arrête l'élément focalisé au-dessus du
   bouton (scroll-padding-bottom : bouton et ses deux marges) ; sous 900 px, derniers blocs de main dégagés du rond par
   un couloir à droite, comme les lignes d'événement, sans agrandir main (la maquette garde 70 px avant la lettre
   d'information) : e-mail de Contact et des pages légales, boutons de « Hors liste » du catalogue à 768 px, lecteurs
   vidéo et audio des articles (barre de commandes native en bas à droite du cadre, sous le rond une fois le lecteur
   amené entier au bas de la fenêtre). */
.bouton-whatsapp:focus-visible { box-shadow: var(--anneau-focus); }
html { scroll-padding-bottom: calc(var(--taille-flottant) + 2 * var(--espace-5)); }
@media (max-width: 899px) {
  html { scroll-padding-bottom: calc(var(--taille-flottant) + 2 * var(--espace-4)); }
  .contact-email { --couloir-flottant: calc(var(--espace-4) + var(--taille-flottant) + var(--espace-2)); margin-inline-end: max(0px, calc(var(--couloir-flottant) - (100vw - min(100vw - 2 * var(--marge-page), var(--largeur-contenu))) / 2)); }
  main :is(video[controls], audio[controls], iframe) { --couloir-flottant: calc(var(--espace-4) + var(--taille-flottant) + var(--espace-2)); max-width: calc(100% - max(0px, calc(var(--couloir-flottant) - (100vw - min(100vw - 2 * var(--marge-page), var(--largeur-contenu))) / 2))); }
}
@media (min-width: 768px) and (max-width: 899px) {
  .appel .groupe-boutons { --couloir-flottant: calc(var(--espace-4) + var(--taille-flottant) + var(--espace-2)); padding-inline-end: max(0px, calc(var(--couloir-flottant) - (100vw - min(100vw - 2 * var(--marge-page), var(--largeur-contenu))) / 2)); }
}

/* Slider à la une (ticket 031) : aside.heros-une, frère du héros dans .heros-cadre (hauteur, photo et voile du héros
   inchangés). Sous 900 px, bande nuit en flux sous le héros, dans la gouttière de page ; dès 900 px, panneau superposé
   au héros, collé au bord droit de la fenêtre, sous la barre flottante, le texte du héros resserré pour ne jamais passer
   dessous. Diapositives empilées dans une même case de grille, une seule non hidden ; la nouvelle entre depuis
   l'extérieur du bord droit (100vw : sous 900 px la carte, à gauche, ne quitte l'écran que d'une largeur de fenêtre),
   plus petite et derrière la sortante (z-index), sans défilement horizontal (overflow-x: clip) ; aucune animation sur
   une carte dont le lien a le focus, ni sous mouvement réduit. .sur-nuit a de composants.css passerait le titre de la
   carte en blanc sur blanc : couleur de texte rétablie. Commandes hidden sans JavaScript (display: flex de
   .carrousel-commandes l'emporte sinon). Titre h2#titre-une réservé aux lecteurs d'écran (ticket 053) : aucune pastille
   au-dessus de la carte. */
.heros-cadre { position: relative; overflow-x: clip; --largeur-une: clamp(260px, 24vw, 340px); }
.heros-une { padding: var(--espace-6) var(--marge-page); }
.heros-une-diapos { display: grid; max-width: 360px; }
.heros-une-diapo { grid-area: 1 / 1; }
.heros-une-commandes { margin-top: var(--espace-4); }
.heros-une-commandes[hidden] { display: none; }
.heros-une .bouton-secondaire { background: var(--couleur-plancher-heros); }
.heros-une .carte-photo { aspect-ratio: 4 / 3; }
.heros-une .carte-equipement, .heros-une .carte-equipement:hover { color: var(--couleur-texte); }
@media (min-width: 900px) {
  .heros-une { position: absolute; top: calc(var(--hauteur-entete-flottante) + var(--espace-4)); bottom: var(--espace-6); right: 0; width: var(--largeur-une); padding: 0; background: transparent; display: grid; align-content: center; z-index: 1; }
  .heros-une-commandes { margin-inline-start: var(--espace-4); }
  .heros-une-diapos { max-width: none; }
  .heros-une .carte-equipement { border-radius: var(--rayon-m) 0 0 var(--rayon-m); }
  .heros-cadre .heros-plein .heros-texte { max-width: min(760px, calc(100% - var(--largeur-une) - var(--espace-6) + var(--marge-page))); }
}
@keyframes une-entree { from { transform: translateX(100vw) scale(0.9); opacity: 0; } 60% { opacity: 1; } to { transform: none; opacity: 1; } }
@keyframes une-sortie { to { transform: translateX(-10%) scale(0.96); opacity: 0; } }
.heros-une-diapo.entre { animation: une-entree var(--duree-longue) var(--courbe) both; }
.heros-une-diapo.sort { animation: une-sortie var(--duree-moyenne) var(--courbe) both; z-index: 1; pointer-events: none; }
.heros-une-diapo:focus-within { animation: none; }
@media (prefers-reduced-motion: reduce) { .heros-une-diapo.entre, .heros-une-diapo.sort { animation: none; } }

/* Pastille éditoriale (ticket 032) : span.etiquette-pastille.etiquette-sur-photo, rendu après l'étiquette de stock pour
   les seuls produits du slider qui en ont une. En bas à gauche de la photo (le stock reste en haut), sans en déborder ;
   vert foncé et texte blanc (paire 5,09:1). Ombre, rayon, taille et z-index de .etiquette et .etiquette-sur-photo. */
.etiquette-sur-photo.etiquette-pastille { top: auto; bottom: var(--espace-3); max-width: calc(100% - 2 * var(--espace-3)); background: var(--couleur-vert-fonce); color: var(--couleur-texte-inverse); }

/* Filtres des documents de la page Brochures (ticket 039) et de la page Événements (ticket 040) : nav.filtres-documents
   et nav.filtres-evenements, puces .filtres au-dessus des cartes ou des lignes, chacune avec le nombre d'éléments
   (.nombre) ; message .documents-vide ou .evenements-vide pour un filtre sans élément. Ici et non dans composants.css,
   copie conforme de styles.css (tests/design-system.sh). */
.filtres-documents, .filtres-evenements { margin-bottom: var(--espace-6); }
.filtres-documents .nombre, .filtres-evenements .nombre { margin-left: var(--espace-2); font-size: var(--taille-xs); font-weight: var(--graisse-forte); color: var(--couleur-texte-2); }
.filtres-documents a:hover .nombre, .filtres-evenements a:hover .nombre { color: inherit; }
.filtres-documents a[aria-current="true"] .nombre, .filtres-evenements a[aria-current="true"] .nombre { color: inherit; }
.documents-vide a, .evenements-vide a { font-weight: var(--graisse-forte); }

/* Événements à la une (ticket 040) : section.evenements-une juste sous la tête de page de /evenements/, trois
   article.carte-evenement-une.sur-nuit sur trois aplats (accent-1 nuit, accent-2 bleu, accent-3 vert foncé), texte,
   étiquette, bouton et anneau de focus blancs (paires AA de tokens.css) ; jamais --couleur-texte-muet-inverse, à
   2,9:1 sur le vert foncé. La tuile .date-bloc garde son fond bleuté (7,9:1). */
.evenements-une { padding-top: var(--espace-6); }
.evenements-une-grille { display: grid; grid-template-columns: 1fr; gap: var(--espace-4); }
@media (min-width: 768px) { .evenements-une-grille { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.carte-evenement-une { display: flex; flex-direction: column; align-items: flex-start; gap: var(--espace-3); padding: var(--espace-5); border-radius: var(--rayon-m); color: var(--couleur-texte-inverse); }
.carte-evenement-une.accent-1 { background: var(--couleur-nuit); }
.carte-evenement-une.accent-2 { background: var(--couleur-bleu); }
.carte-evenement-une.accent-3 { background: var(--couleur-vert-fonce); }
.carte-evenement-une h3 { font-size: var(--taille-m); margin: 0; color: var(--couleur-texte-inverse); }
.carte-evenement-une .meta { margin: 0; font-size: var(--taille-s); color: var(--couleur-texte-inverse); }
.carte-evenement-une .etiquette { background: transparent; color: inherit; border: 1px solid currentColor; }
.carte-evenement-une .date-bloc { min-width: 72px; }
.carte-evenement-une .bouton-secondaire { margin-top: auto; color: var(--couleur-texte-inverse); border-color: var(--couleur-texte-inverse); }
.carte-evenement-une :focus-visible { outline: 3px solid var(--couleur-surface); outline-offset: 2px; box-shadow: none; }

/* Champ obligatoire (ticket 043) : mention « (obligatoire) » du libellé, sur le modèle de « (facultatif) »
   (.optionnel de composants.css, copie conforme de styles.css) ; seul le téléphone des demandes de devis la porte. */
.champ label .obligatoire { font-weight: var(--graisse-texte); color: var(--couleur-texte-2); }

/* Filtre de disponibilité à droite de la barre de la liste (ticket 045) : nav.filtre-disponibilite après le comptage
   de .barre-liste, titre au style des groupes de la colonne (.filtres-groupe summary), puces .filtres côte à côte ;
   marges, retrait et marqueurs du ul remis à zéro, padding-block de « .barre-liste a » (plus haut) neutralisé sur les
   puces. Sous 600 px, le filtre prend sa propre ligne sous le comptage, aligné à gauche. */
.filtre-disponibilite { display: flex; flex-wrap: wrap; align-items: center; gap: var(--espace-3); margin-left: auto; }
.filtre-disponibilite-titre { font-size: var(--taille-xs); font-weight: var(--graisse-forte); letter-spacing: var(--interlettrage-etiquette); text-transform: uppercase; color: var(--couleur-vert-texte); }
.filtre-disponibilite .filtres { margin: 0; padding: 0; list-style: none; flex-wrap: nowrap; }
.barre-liste .filtres a { padding-block: 0; }
@media (max-width: 599px) { .filtre-disponibilite { width: 100%; margin-left: 0; } }

/* Page Contact (blocs ams/agences et ams/ecrire, ticket 046) : agences juste sous la bande nuit, sur fond blanc, en colonnes égales dès
   768 px (adresse, téléphone à la cible tactile, carte de localisation) ; façade de la carte (épingle, texte, bouton
   « Afficher la carte » caché sans JavaScript : le display de .bouton de composants.css l'emporterait sur [hidden]),
   cadre Google Maps chargé au clic (assets/js/contact.js) à pleine taille, au-dessus de la règle du couloir WhatsApp des
   iframe de main (largeur fixée : sans elle, aspect-ratio reporterait min-height sur la largeur) ; section « Nous écrire » : titre visible, formulaire et autres moyens de nous joindre côte à côte dès
   900 px (2/3, 1/3), empilés en dessous ; adresses et e-mail coupés au besoin, jamais de débordement horizontal. */
@media (min-width: 768px) { .agences .grille-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.agences .carte-agence { display: grid; gap: var(--espace-3); align-content: start; min-width: 0; padding: var(--espace-5); background: var(--couleur-surface); border-radius: var(--rayon-m); box-shadow: var(--ombre-douce); overflow-wrap: anywhere; }
.agences .carte-agence .etiquette { justify-self: start; margin: 0; }
.agences .carte-agence h3 { margin: 0; font-size: var(--taille-l); }
.agences .carte-agence address { display: grid; gap: var(--espace-2); font-style: normal; }
.agences .carte-agence address p { margin: 0; }
.carte-agence-telephone a { display: inline-flex; align-items: center; min-height: var(--cible-tactile); }
.carte-localisation { position: relative; width: 100%; aspect-ratio: 16 / 10; min-height: 220px; overflow: hidden; background: var(--couleur-surface-pale); border: 1px solid var(--couleur-filet); border-radius: var(--rayon-m); }
.carte-localisation-facade { display: grid; place-content: center; justify-items: center; gap: var(--espace-3); height: 100%; padding: var(--espace-5); text-align: center; }
.carte-localisation-facade svg { width: 40px; height: 40px; fill: none; stroke: var(--couleur-bleu); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.carte-localisation-texte { margin: 0; font-size: var(--taille-s); color: var(--couleur-texte-2); }
.carte-localisation-bouton[hidden] { display: none; }
.carte-localisation.est-chargee iframe { display: block; width: 100%; height: 100%; max-width: none; border: 0; }
.section-contact h2#titre-formulaire { margin: 0 0 var(--espace-4); }
.section-contact .prose { margin-bottom: var(--espace-6); }
.contact-colonnes { display: grid; gap: var(--espace-6); }
@media (min-width: 900px) {
  .contact-colonnes { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); align-items: start; }
  .contact-formulaire .formulaire-contact { max-width: none; }
}
.contact-voies { display: grid; gap: var(--espace-4); min-width: 0; }
.carte-voie { min-width: 0; padding: var(--espace-5); background: var(--couleur-surface); border-radius: var(--rayon-m); border-left: 4px solid var(--couleur-bleu); overflow-wrap: anywhere; }
.carte-voie h3 { margin: 0 0 var(--espace-2); font-size: var(--taille-m); }
.carte-voie p:last-child { margin-bottom: 0; }
.carte-voie .contact-email { margin: 0; }
.carte-voie .contact-email a { display: inline-flex; align-items: center; min-height: var(--cible-tactile); }

/* Logos des marques en couleur (ticket 047) : accueil (section « Marques ») et page Partenaires (/marques/), même
   ul.logos-marques. Le filtre de composants.css (logos blanchis, opacité réduite) est neutralisé ici, sans toucher à la
   copie du design system : logos dans leurs couleurs d'origine, sans filtre, opacité ni fusion, sur une tuile blanche
   (sur nuit, Hitachi, Sartorius, Brand et le bleu de ZEISS ne se liraient pas) ; noms seuls en texte secondaire sur
   blanc. Hauteur, grille et espacements inchangés ; écart voulu avec la maquette (logos blancs sur cases nuit). */
.logos-marques li { background: var(--couleur-surface); border-color: var(--couleur-surface); }
.logos-marques img, .logos-marques li:hover img, .marques-page a:hover img { filter: none; opacity: 1; mix-blend-mode: normal; transition: none; }
.logos-marques .nom-marque { color: var(--couleur-texte-2); }

/* Note sous les lignes de Mon devis (ticket 050) : p.devis-note, petit texte de la maquette mon-devis.html, après les
   lignes ou le message de devis vide, dans la colonne des lignes. */
.devis-note { font-size: var(--taille-s); margin-top: var(--espace-5); }

/* Sélection de l'accueil (ticket 054) : les titres des fiches tiennent sur une à trois lignes ; le height: 100 % de
   .carte-equipement (composants.css, maquette) vaut auto dans la piste flex sans hauteur définie et empêche
   l'étirement : height auto rend la main à align-items: stretch, cartes de même hauteur et pieds alignés (margin-top:
   auto de .carte-pied), comme dans la maquette. */
.carrousel > .carte-equipement { height: auto; }

/* Filtres de Brochures et Événements sans rechargement (ticket 057, assets/js/filtres.js) : le focus programmatique
   rendu à la liste ne dessine pas de cadre (comme .catalogue-liste) ; liste estompée pendant le chargement. */
[data-filtres-liste]:focus, [data-filtres-liste]:focus-visible { outline: none; box-shadow: none; }
[data-filtres-liste][aria-busy="true"] .brochures,
[data-filtres-liste][aria-busy="true"] .documents-vide,
[data-filtres-liste][aria-busy="true"] .evenements-vide,
[data-filtres-liste][aria-busy="true"] .contenu > div { opacity: .6; }

/* Ticket 064 : réseaux sociaux du pied (Réglages AMS Labo), rendus seulement s'il y en a ; liens du pied gardés. */
.pied .pied-reseaux { display: flex; flex-wrap: wrap; gap: var(--espace-2); margin-top: var(--espace-2); }
.pied .pied-reseaux li { margin-bottom: 0; }

/* Captcha Turnstile des formulaires (ticket 066, assets/js/captcha.js) : le conteneur reste replié tant que Turnstile n'y
   a rien posé (rendu des pages inchangé au chargement ; aucun déplacement du bouton entre l'appui et le relâchement
   d'un clic qui déclenche le chargement) ; il se déplie à l'arrivée du widget, ou avec un message d'erreur ; sans
   JavaScript (aucun état), le message du noscript reste visible. */
.champ-captcha:not(.champ-erreur):has(> .captcha-turnstile[data-captcha-etat]:empty) { display: none; }
.captcha-turnstile:not(:empty) { min-height: 65px; }
.captcha-attente { margin-top: var(--espace-2); }

/* Lettre d'information du pied (ticket 066, inc/newsletter.php) : message d'état, case de consentement, captcha et
   message d'attente du captcha en pleine largeur de la ligne du formulaire ; case de 24 px alignée sur la première ligne
   de son libellé, à la couleur d'accent de l'interface ; lien du libellé en bloc en ligne de 24 px (cible tactile de responsive.spec.mjs) ; focus de composants.css (.bande-newsletter :focus-visible). */
.bande-newsletter .message-etat, .bande-newsletter .champ-case, .bande-newsletter .champ-captcha, .bande-newsletter .captcha-attente { flex: 1 1 100%; }
.bande-newsletter .message-etat { color: var(--couleur-texte); font-weight: var(--graisse-forte); }
.bande-newsletter .champ-case { flex-direction: row; align-items: flex-start; gap: var(--espace-3); }
.bande-newsletter .champ-case input { flex: none; width: 24px; height: 24px; min-height: 0; margin: 0; padding: 0; accent-color: var(--couleur-bleu); }
.bande-newsletter .champ-case label { font-weight: var(--graisse-texte); line-height: 24px; }
.bande-newsletter .champ-case label a { display: inline-block; min-height: 24px; color: var(--couleur-bleu); text-decoration: underline; }
