/* Classes sémantiques globales — cf. D_004 (couche 2/2).
   Thème sombre repris cette nuit des 7 PDFs de Max (resources/*.pdf, night-shift
   17-18 août 2026) — à confirmer, cf. note dans tokens.css et le handoff. h6 et
   bouton secondaire restent en brouillon et ne sont pas ici. */

html {
  scrollbar-gutter: stable; /* espace de la scrollbar réservé en permanence :
  évite le flicker de largeur quand body.style.overflow passe à hidden
  (popup News, cf. assets/js/news-popup.js) */
}

body {
  margin: 0;
  font-family: var(--tok-font-degular);
  background: var(--tok-bg);
  color: var(--tok-text);
}

/* --- typographie --- */
/* Échelle responsive mobile-first (B-009) : taille de base = mobile, paliers tablette/desktop
   alignés sur les breakpoints déjà utilisés pour --tok-page-padding-inline (tokens.css:59-65).
   Desktop (min-width: 1200px) reprend les valeurs validées, inchangées. */

h1, h2, h3, h4, h5,
.h1, .h2, .h3, .h4, .h5 {
  font-family: var(--tok-font-degular);
  font-weight: 700;
  font-style: italic;
  line-height: 1.15;
  margin: 0;
  color: var(--tok-color-beige);
  max-width: 620px;
}
h1, .h1 { font-size: 34px; }
h2, .h2 { font-size: 26px; }
h3, .h3 { font-size: 20px; }
h4, .h4 { font-size: 18px; }
h5, .h5 { font-size: 16px; }

@media (min-width: 768px) {
  h1, .h1 { font-size: 44px; }
  h2, .h2 { font-size: 30px; }
  h3, .h3 { font-size: 22px; }
  h4, .h4 { font-size: 20px; }
  h5, .h5 { font-size: 18px; }
}

@media (min-width: 1200px) {
  h1, .h1 { font-size: 55px; }
  h2, .h2 { font-size: 34px; }
  h3, .h3 { font-size: 24px; }
  h4, .h4 { font-size: 21px; }
  h5, .h5 { font-size: 18px; }
}

p, .text {
  font-family: var(--tok-font-degular);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.5;
  margin: 0;
}

.text-sm {
  font-family: var(--tok-font-degular);
  font-weight: 400;
  font-size: 14px;
  line-height: 1.5;
}

/* Paragraphe agrandi — s'applique aussi aux <p> d'un bloc KirbyText (.prose) */
.text-lg,
.text-lg p { font-size: 20px; }

.text-muted { color: var(--tok-text-muted); }

.caption {
  font-family: var(--tok-font-degular);
  font-weight: 700;
  font-size: 13px;
  line-height: 1.4;
}
@media (min-width: 768px) {
  .caption { font-size: 14px; }
}

/* Étiquette d'introduction de section (ex. « — événements », hero Accueil), vue sur tous les
   PDFs. Pas de marge intégrée — l'appelant gère l'espacement via les utilitaires mt/mb comme
   le reste du système. Trait en élément réel (.eyebrow-line), pas en ::before. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: var(--tok-space-lg);
  font-family: var(--tok-font-degular);
  font-weight: 900;
  font-size: 16px;
  line-height: 1.15;
  color: var(--tok-color-rouge-vif);
}
.eyebrow-line { display: block; flex-shrink: 0; width: var(--tok-space-4xl); height: 1px; background: var(--tok-color-rouge-vif); }

/* --- boutons --- */

.btn-primary,
.btn-secondary {
  display: inline-block;
  font-family: var(--tok-font-degular);
  font-size: .95rem;
  font-weight: 700;
  text-transform: uppercase;
  text-decoration: none;
  padding: var(--tok-space-sm) var(--tok-space-lg);
  border: 1px solid transparent;
  border-radius: 0;
  background: var(--tok-color-rouge);
  color: var(--tok-color-beige);
  cursor: pointer;
}
/* Secondaire : fond transparent, bordure de la couleur de sa police (beige, comme le primaire). */
.btn-secondary {
  background: transparent;
  border-color: currentColor;
}

/* Carré 50×50px, icône 24×24px centrée. Couleur par nom de fichier de l'icône
   (suffixe -black -> fond beige, défaut -> fond rouge). Forme en négatif de son
   contexte : .sheared hors bloc à bords de travers (combiner avec .shear-both de
   utilities.css), .straight dans un bloc droit — jamais les deux. */
/* Taille responsive (B-009 même logique) : 24×24 mobile/tablette, 50×50 desktop
   (≥1200px) — icône interne toujours ~50% du carré (12px / 24px). */
.btn-icon {
  height: 24px;
  width: 24px;
  flex-shrink: 0; /* évite l'écrasement quand le parent flex manque de place
  (ex. .news-slide-content sur mobile, h3 + bouton dans une largeur de slide réduite). */
  border: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--tok-color-beige);
  font-size: 12px;
  line-height: 1;
  text-decoration: none;
}
.btn-icon svg { width: 12px; height: 12px; display: block; }
.btn-icon.bg-beige { background: var(--tok-color-beige-bg); }
.btn-icon.bg-rouge { background: var(--tok-color-rouge); }
.btn-icon.straight { border-radius: 0; }
.btn-icon.sheared {
  --tok-shear: calc(var(--tok-shear-ratio) * 24px);
  width: calc(24px + var(--tok-shear));
}
@media (min-width: 1200px) {
  .btn-icon { height: 50px; width: 50px; font-size: 24px; }
  .btn-icon svg { width: 24px; height: 24px; }
  .btn-icon.sheared {
    --tok-shear: calc(var(--tok-shear-ratio) * 50px);
    width: calc(50px + var(--tok-shear));
  }
}

/* --- navbar (bloc unique sur fond dédié, cf. D_007 / hero Accueil PDF) --- */

/* Sticky : fixe en haut de viewport sur toute page, masquée au scroll down et
   réaffichée au scroll up (cf. assets/js/navbar.js). Barre continue, sans cisaillement
   ni espace entre les 3 segments (brand/links/cta restent 3 blocs de grille pour le
   layout, mais partagent le même fond, posé une seule fois ici). */
.navbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 20;
  display: grid;
  grid-template-columns: 1fr 3fr 1fr;
  background: var(--tok-bg-navbar);
  transition: transform .3s ease;
}
.navbar--hidden { transform: translateY(-100%); }
.navbar-brand,
.navbar-links,
.navbar-cta {
  display: flex;
  align-items: center;
  padding: var(--tok-space-lg) var(--tok-page-padding-inline);
}
.navbar-brand { gap: var(--tok-space-sm); text-decoration: none; }
.navbar-brand img { height: 48px; width: 48px; display: block; }
.navbar-brand .wordmark {
  font-family: var(--tok-font-degular);
  font-weight: 900;
  font-size: 18px;
  color: var(--tok-color-beige);
  text-transform: uppercase;
}
.navbar-links { justify-content: center; gap: 16px; }
.navbar-links a {
  font-family: var(--tok-font-degular);
  font-weight: 700;
  font-size: 18px;
  color: var(--tok-color-beige);
  text-decoration: none;
  padding-bottom: 4px;
  border-bottom: 2px solid transparent; /* espace toujours réservé, même state de
  repos, pour que l'apparition d'un soulignement (hover/actif) ne décale rien */
  transition: color .2s ease, border-color .2s ease;
}
.navbar-links a:hover { color: color-mix(in srgb, var(--tok-color-beige) 80%, black); }
.navbar-links a:active { color: color-mix(in srgb, var(--tok-color-beige) 60%, black); transition: none; }
/* Page courante exacte (ex. Accueil, Location, ou Expositions lui-même) : soulignée. */
.navbar-links a.is-active { border-bottom-color: var(--tok-color-beige); }
/* Section ouverte mais pas la page exacte (Expositions actif sur une sous-page
   /expositions/[slug]) : en rouge, pas de soulignement. */
.navbar-links a.is-active-section { color: var(--tok-color-rouge-vif); }
.navbar-cta { justify-content: flex-end; }
.btn-navbar {
  display: inline-block;
  font-family: var(--tok-font-degular);
  font-weight: 700;
  font-size: 16px;
  text-transform: uppercase;
  text-decoration: none;
  background: transparent;
  color: var(--tok-color-beige);
  border: none;
  border-radius: 0;
  padding: 0;
  cursor: pointer;
}

/* Wrapper autour de navbar-links/navbar-cta : display:contents en desktop, les 2
   segments restent les enfants directs de .navbar (grille 1fr 3fr 1fr intacte).
   En mobile (cf. media query ci-dessous), le wrapper redevient un vrai conteneur
   (le panneau du menu) et n'est plus display:contents. */
.navbar-menu { display: contents; }

/* Bouton hamburger : masqué en desktop, seule .navbar-menu gère la navigation.
   Icône burger/croix togglées en CSS via aria-expanded (posé par assets/js/navbar.js) —
   même principe que .news-popup-close (croix = icon-plus tourné à 45deg). */
.navbar-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: var(--tok-space-lg) var(--tok-page-padding-inline);
  cursor: pointer;
}
.navbar-toggle-icon--close { display: none; }
.navbar-toggle-icon--close svg { width: 24px; height: 24px; transform: rotate(45deg); }
.navbar-toggle[aria-expanded="true"] .navbar-toggle-icon--open { display: none; }
.navbar-toggle[aria-expanded="true"] .navbar-toggle-icon--close { display: block; }

/* --- navbar responsive (<= 900px, seuil propre à la navbar : au-delà de 768px les 6 liens +
   CTA ne tiennent plus sur une ligne — dupliqué en dur dans navbar.js) ---
   Le panneau (.navbar-menu) sort du flux normal et se substitue à un vrai off-canvas :
   position fixed sous la barre, plein cadre. Le fond de .navbar (posé une seule fois,
   règle de base) couvre déjà brand + toggle, plus besoin de fond par segment ici. */
@media (max-width: 900px) {
  .navbar {
    display: flex;
    align-items: stretch;
    justify-content: space-between;
  }
  .navbar-toggle {
    display: flex;
  }
  .navbar-menu {
    display: flex;
    flex-direction: column;
    position: fixed;
    top: var(--tok-navbar-height);
    left: 0;
    right: 0;
    max-height: calc(100vh - var(--tok-navbar-height));
    overflow-y: auto;
    background: var(--tok-bg-navbar);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    transition: opacity .2s ease, transform .2s ease, visibility .2s ease;
  }
  .navbar--menu-open .navbar-menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
  }
  .navbar-links,
  .navbar-cta {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--tok-space-lg);
    padding: var(--tok-space-lg) var(--tok-page-padding-inline);
  }
  .navbar-cta { border-top: 1px solid var(--tok-gray-6); }

  /* Stagger : chaque lien entre avec un délai croissant, seulement à l'ouverture
     (état de repos = invisible, de toute façon masqué par .navbar-menu tant que
     .navbar--menu-open n'est pas posée — pas de flash au chargement). */
  .navbar-links a,
  .navbar-cta a {
    opacity: 0;
    transform: translateY(8px);
  }
  .navbar--menu-open .navbar-links a,
  .navbar--menu-open .navbar-cta a {
    animation: navbar-link-in .3s ease forwards;
  }
  .navbar-links a:nth-child(1) { animation-delay: .05s; }
  .navbar-links a:nth-child(2) { animation-delay: .1s; }
  .navbar-links a:nth-child(3) { animation-delay: .15s; }
  .navbar-links a:nth-child(4) { animation-delay: .2s; }
  .navbar-links a:nth-child(5) { animation-delay: .25s; }
  .navbar-links a:nth-child(6) { animation-delay: .3s; }
  .navbar-cta a { animation-delay: .35s; }
}

@keyframes navbar-link-in {
  to { opacity: 1; transform: translateY(0); }
}

/* --- liens de contenu --- */

.link {
  font-family: var(--tok-font-degular);
  font-weight: 700;
  color: var(--tok-color-rouge-vif);
  text-decoration: underline;
}

/* --- hero (structure Accueil B-004, statique — cf. D_006) --- */

.hero {
  position: relative;
  min-height: 640px;
  display: flex;
  align-items: flex-end;
  background-size: cover;
  background-position: center;
}
.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(rgba(0, 0, 0, 0) 40%, rgba(0, 0, 0, .55) 100%);
}
.hero-content {
  position: relative;
  z-index: 1;
  padding: var(--tok-space-4xl) var(--tok-page-padding-inline);
  max-width: 640px;
}

/* Variante plein écran, contenu centré (cf. hero Accueil PDF) — cf. D_007. Modificateurs
   plutôt que classes contextuelles (D_004) : réutilisables sur tout hero qui doit suivre
   le même traitement une fois revu contre son propre PDF.
   Colonne (hero-content + .scroll-hint) plutôt que positionnement absolute de la flèche :
   le gap entre les deux devient une valeur directe (--tok-space-4xl), pas un calcul à
   rebours entre le padding-bottom de .hero-content et un bottom en px (fragile — cassait
   dès que l'un des deux changeait). padding-bottom de .hero-content neutralisé ici pour
   que ce gap soit la SEULE source d'espacement avant la flèche. */
.hero--full {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: var(--tok-space-4xl);
  background-position: left center;
}
.hero--full { padding-bottom: var(--tok-space-lg); }
.hero--full .hero-content { padding-bottom: 0; }
.hero--full .scroll-hint { margin-top: 0; }
.hero--full .scroll-hint svg { width: 24px; height: 24px; }
.hero-content--centered {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  max-width: none;
}
@media (min-width: 1200px) {
  .hero-content--centered {
    justify-content: center;
    align-items: center;
    text-align: center;
  }
}

/* Variante page Exposition (détail) : plein écran, contenu aligné à droite / centré
   verticalement, texte du bloc aligné à gauche à l'intérieur (cf. Expositions.pdf).
   Desktop (≥1200px) : bloc et texte centrés, cf. media query plus bas. */
.hero--right { min-height: 100svh; align-items: center; justify-content: flex-end; }
.hero-content--wide { min-width: 720px; max-width: 820px; }
/* Sous 768px : le min-width: 720px ci-dessus dépasse le viewport (overflow horizontal) —
   contenu recalé en bas/gauche (padding latéral déjà à 16px via --tok-page-padding-inline,
   cf. tokens.css) plutôt qu'au centre/droite comme en desktop (cf. Expositions.pdf). */
@media (max-width: 768px) {
  .hero--right { align-items: flex-end; justify-content: flex-start; }
  .hero-content--wide { min-width: 0; max-width: 100%; padding-bottom: var(--tok-space-2xl); }
}
/* .prose force text-align: left — surchargé ici pour suivre le centrage du bloc. */
@media (min-width: 1200px) {
  .hero--right { justify-content: center; }
  .hero--right .hero-content,
  .hero--right .prose { text-align: center; }
}

/* Variante sans image (ex. hero catalogue Expositions) : pas de min-height forcé ni de
   flex-end — contenu en flux normal, padding vertical explicite propre à la section (la
   navbar overlay reste en position absolute par-dessus, le padding-top lui laisse la place).
   Le dégradé ::after n'a plus de raison d'être sans image à assombrir. */
.hero--flow {
  min-height: 0;
  display: block;
  padding-top: 64px;
}
.hero--flow::after { display: none; }
.hero--flow .hero-content { padding: 0 var(--tok-page-padding-inline); }

/* Variante page Notre histoire : pas d'image plein fond, mais une image glissée à
   gauche sur 50% de la largeur (bord droit incliné) + texte à droite (cf. Notre
   histoire.pdf). Hauteur de rangée fixe pour un cisaillement constant (même logique
   que --panel-shear sur .gallery-card) — évite de recalculer l'angle si le texte
   varie légèrement en hauteur. */
.hero-story { padding-top: var(--tok-navbar-height); padding-bottom: 0; }
.hero-story-row {
  display: flex;
  align-items: stretch;
  min-height: 480px;
  margin-top: var(--tok-space-lg);
}
.hero-story-media {
  --tok-shear: calc(var(--tok-shear-ratio) * 480px);
  flex: 0 0 50%;
  background-size: cover;
  background-position: center;
  background-color: var(--tok-surface);
}
.hero-story-content {
  flex: 1 1 50%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--tok-space-4xl);
  padding: var(--tok-space-lg);
}
@media (min-width: 768px) {
  .hero-story-content { padding: var(--tok-space-2xl); }
}
.hero-story-content .scroll-hint { margin-top: 0; }
@media (max-width: 768px) {
  /* Plein écran mobile comme les autres heros (100svh) : l'image prend la hauteur
     restante sous la navbar et au-dessus du texte (280px minimum). */
  .hero-story { min-height: 100svh; display: flex; flex-direction: column; }
  .hero-story-row { flex: 1; flex-direction: column; min-height: 0; }
  .hero-story-media { flex: 1 1 280px; clip-path: none; }
}

/* Lien retour vers la page parente (ex. fiche Expo -> catalogue) : carré flèche
   (même .btn-icon que les flèches de slider, icône tournée vers la gauche) + libellé.
   Dans un .hero : ancré en haut à gauche, sous la navbar overlay, au-dessus du scrim. */
.back-link {
  display: inline-flex;
  align-items: center;
  gap: var(--tok-space-sm);
  font-family: var(--tok-font-degular);
  font-size: 16px;
  color: var(--tok-color-beige);
  text-decoration: none;
}
.back-link svg { transform: rotate(90deg); }
.back-link:hover { text-decoration: underline; }
.hero > .back-link {
  position: absolute;
  z-index: 1;
  top: calc(var(--tok-navbar-height) + var(--tok-space-lg));
  left: var(--tok-page-padding-inline);
}

/* Indicateur de scroll (cf. home.pdf / Notre histoire.pdf) — icône SVG dédiée,
   cf. resources/assets/Icones/→.svg (fill fixe, même logique que .btn-icon). */
.scroll-hint { display: inline-block; margin-top: var(--tok-space-lg); }
.scroll-hint svg { display: block; width: 40px; height: auto; }

/* Grand bloc citation sur image (cf. home.pdf) — même logique que .hero en plus court. */
.quote-block {
  position: relative;
  min-height: 320px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  background-size: cover;
  background-position: center;
  padding: var(--tok-space-4xl) var(--tok-page-padding-inline);
}
.quote-block::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, .5);
}
.quote-block blockquote {
  position: relative;
  z-index: 1;
  margin: 0;
  max-width: 720px;
}

/* --- sliders (News Accueil, Nos prestations, présentation Exposition) : un seul
   carrousel Swiper (CDN, cf. site/snippets/swiper-assets.php + assets/js/slider.js) —
   boucle infinie et centrage gérés par Swiper (loop + centeredSlides). La slide active
   est centrée, la précédente et la suivante restent visibles de part et d'autre, voilées
   (opacité) ; le voile s'appuie sur la classe que Swiper pose lui-même
   (swiper-slide-active) — aucun data-attribute custom à maintenir. --- */

/* --tok-shear en % de largeur (pas de calc(px), hauteur non fixe — aspect-ratio 16/9 :
   0.12 × 9/16 = 6.75%, même angle constant que .expo-card-media).
   Sélecteurs à 2 classes (.slider-track .slider-slide) plutôt que .slider-slide seul :
   le <link> Swiper (CDN) est chargé dans le <body> via le snippet, donc après semantic.css
   dans la cascade — à spécificité égale (.swiper-slide vs .slider-slide), c'est lui qui
   gagnerait sur width sinon (son width: 100% par défaut écrase notre 62%). */
.slider-track .slider-slide {
  width: 62%;
  aspect-ratio: 16 / 9;
  background-size: cover;
  background-position: center;
  background-color: var(--tok-surface);
  --tok-shear: 6.75%;
  transition: opacity .3s ease;
}
.slider-track.swiper-initialized .slider-slide { opacity: .5; }
.slider-track .slider-slide.swiper-slide-active { opacity: 1; }
/* Une seule slide (image-slider.php / news-slider.php) : pas de Swiper, image fixe centrée. */
.slider-track:not(.swiper-initialized) .slider-slide { margin-inline: auto; }
/* Mobile : slide active à 76 %, un bout des voisines dépasse de chaque côté (voilé). */
@media (max-width: 768px) {
  .slider-track .slider-slide { width: 76%; }
  .slider-track:not(.swiper-initialized) .slider-slide { width: 100%; }
}

.news-slider { padding-top: 128px; padding-bottom: 64px; } /* asymétrique : 128px hérité du
gap voulu avec le hero au-dessus, 64px vers la section suivante — au-delà de l'échelle de
tokens (max --tok-space-4xl: 64px), valeur en dur comme .hero--flow et .cta-banner juste plus
bas dans le fichier. */
.news-slider h2 { text-align: center; }
.news-slider-track-wrap { margin-top: var(--tok-space-2xl); }
/* Slide News = .slider-slide (cf. plus haut) + dégradé bas et contenu (titre, bouton +). */
.news-slide { position: relative; }
.news-slide::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(rgba(0, 0, 0, 0) 45%, rgba(0, 0, 0, .6) 100%);
}
.news-slide-content {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--tok-space-lg);
  padding: var(--tok-space-2xl);
  /* Coin bas-droit coupé vers l'intérieur par le clip-path .shear-both du parent
     (--tok-shear) : compenser le padding droit pour retomber à 32px du coin visible,
     pas du rectangle non coupé. */
  padding-right: calc(var(--tok-space-2xl) + var(--tok-shear));
}
.news-slide-content h3 { color: var(--tok-color-beige); max-width: 60%; }
/* Nav sous le carrousel — commune à tous les sliders (cf. site/snippets/slider-nav.php) :
   flèches alignées à droite, dots centrés sous le slider (sortis du flux pour que la
   largeur des flèches ne décale pas leur centre). */
.slider-nav {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--tok-space-lg);
  margin-top: var(--tok-space-lg);
}
.slider-nav .slider-dots { position: absolute; left: 50%; transform: translateX(-50%); }
/* Mobile : navigation au swipe uniquement (Swiper) — flèches masquées, les dots
   reviennent dans le flux (sinon la nav, vide, s'effondre), seuls et centrés. */
@media (max-width: 768px) {
  .slider-nav { justify-content: center; }
  .slider-nav .slider-dots { position: static; transform: none; }
  .slider-arrow { display: none; }
}
/* Dots entre les flèches, tous breakpoints : parallélogrammes (même angle constant
   que le reste du site, --tok-shear calculé sur leur hauteur fixe de 8px), le dot
   actif s'allonge. Générés en JS, un par slide d'origine (cf. assets/js/slider.js). */
.slider-dots { display: flex; align-items: center; gap: var(--tok-space-xs); }
.slider-dot {
  --tok-shear: calc(var(--tok-shear-ratio) * 8px);
  width: calc(12px + var(--tok-shear));
  height: 8px;
  padding: 0;
  border: none;
  background: var(--tok-color-beige-bg);
  opacity: .35;
  cursor: pointer;
  transition: width .3s ease, opacity .3s ease;
}
.slider-dot.is-active { width: calc(28px + var(--tok-shear)); opacity: 1; }
/* Icône flèche de base (icon-arrow.php) orientée vers le bas (même tracé que
   .scroll-hint) — rotation pour obtenir gauche/droite. */
.slider-arrow.prev svg { transform: rotate(90deg); }
.slider-arrow.next svg { transform: rotate(-90deg); }

/* Popup déclenchée par le bouton "+" d'une slide (cf. assets/js/news-popup.js).
   Croix de fermeture = icon-plus.php tournée à 45°, pas une icône dédiée. */
.news-popup {
  position: fixed;
  inset: 0;
  z-index: 30;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--tok-space-lg);
}
.news-popup:not([hidden]) { display: flex; }
.news-popup-overlay {
  position: absolute;
  inset: 0;
  background: rgba(15, 15, 14, .85);
}
.news-popup-panel {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 1440px;
  padding: var(--tok-space-2xl);
}
/* Wrapper dédié au scroll interne — séparé de .news-popup-panel pour que le
   overflow-y ne s'applique jamais à la croix (cf. .news-popup-close, positionnée
   hors du panneau : sur .news-popup-panel, overflow-y:auto forçait aussi
   overflow-x:auto d'après la règle CSS "un axe non-visible rend l'autre auto",
   ce qui créait un scroll latéral). Centré comme unité (image + texte). */
.news-popup-content {
  max-width: 720px;
  margin-inline: auto;
  max-height: calc(100vh - var(--tok-space-4xl) * 2);
  overflow-y: auto;
  text-align: left;
}
/* --tok-shear en % de largeur, même formule que .news-slide (aspect-ratio 16/9 identique).
   max-height plafonne l'image sur les viewports bas (fenêtre non maximisée, petit laptop) :
   sans ça, l'image seule (16/9 sur 720px de large ≈ 405px) pouvait déjà remplir tout le
   budget vertical de .news-popup-content et déclencher le scroll interne même pour un
   paragraphe très court. object-fit: cover recadre l'image quand max-height l'emporte sur
   le ratio, sans jamais toucher au texte. */
.news-popup-image {
  width: 100%;
  aspect-ratio: 16 / 9;
  max-height: 45vh;
  object-fit: cover;
  display: block;
  --tok-shear: 6.75%;
}
.news-popup-content h2 { text-align: left; margin-top: var(--tok-space-2xl); }
.news-popup-content p { margin-top: var(--tok-space-lg); }
/* background: none — le bouton reste un <button> (hit-box a11y), mais ne doit montrer
   que la croix, pas de fond UA par défaut ni de couleur de marque. Décalée hors du
   panneau (offsets négatifs) pour rester au-dessus de l'image, jamais dessous — sans
   provoquer de scroll latéral puisque .news-popup-panel n'a plus d'overflow. En dessous
   de 768px, ce décalage négatif (-16px) s'ajoute au padding de .news-popup (16px) pour
   ramener la croix pile au bord du viewport (0 de marge réelle) — casse la règle "même
   padding latéral partout" sur mobile. Repos = insérée dans le padding du panneau
   (positif, cf. --tok-space-sm) ; le décalage négatif ne s'active qu'à partir de 768px,
   où le panneau a assez de marge autour pour l'accueillir sans toucher le bord. */
.news-popup-close {
  position: absolute;
  top: var(--tok-space-sm);
  right: var(--tok-space-sm);
  height: 32px;
  width: 32px;
  background: none;
}
@media (min-width: 768px) {
  .news-popup-close {
    top: calc(-1 * var(--tok-space-lg));
    right: calc(-1 * var(--tok-space-lg));
  }
}
/* Sans fond de couleur (background: none ci-dessus), l'icône seule doit rester lisible :
   pas de rétrécissement à 12px comme .btn-icon standard sous 1200px (cf. règle B-009,
   pensée pour une icône posée sur un carré plein), fixe à 32×32 sur toutes les tailles
   (agrandie depuis 24×24 — trop discrète à côté du reste du popup). */
.news-popup-close svg { width: 32px; height: 32px; transform: rotate(45deg); }

/* --- section générique (teasers Accueil, réutilisable ailleurs) --- */

.section { padding-block: var(--tok-space-4xl); }

/* --- bandeau CTA final (cf. location.pdf / événement.pdf) --- */

.cta-banner {
  padding: 128px var(--tok-space-lg);
  text-align: center;
  background: linear-gradient(to bottom, var(--tok-bg), var(--tok-bg-footer));
}
.cta-banner .h1 { margin: 0 auto var(--tok-space-2xl); }

/* --- liste numérotée (« marche à suivre », cf. location.pdf) --- */

.step-list { display: flex; flex-direction: column; gap: var(--tok-space-lg); }
.step { display: flex; gap: var(--tok-space-lg); }
.step .step-number {
  font-family: var(--tok-font-degular);
  font-weight: 700;
  font-style: italic;
  font-size: 21px;
  color: var(--tok-color-rouge-vif);
}
.step h4 { margin-bottom: var(--tok-space-xs); }
.step p { color: var(--tok-text-muted); }

/* --- liste numérotée H2/H3 (variante « notre histoire » du step-list, cf. location.php) --- */

.numbered-list { display: flex; flex-direction: column; gap: var(--tok-space-lg); list-style: none; margin: 0; padding: 0; }
.numbered-list-item { display: flex; gap: var(--tok-space-lg); cursor: pointer; }
.numbered-list-index {
  font-family: var(--tok-font-degular);
  font-weight: 700;
  font-style: italic;
  font-size: 21px;
  margin: 0;
}
.numbered-list-item h3 { font-weight: 400; }
/* Groupe texte (h3 titre + p description) à côté du numéro : pas de classe dédiée dans
   numbered-teaser.php, juste un <div> — ciblé via l'enfant direct. */
.numbered-list-item > div { display: flex; flex-direction: column; gap: var(--tok-space-xs); }

/* Slider « Marche à suivre » (cf. numbered-teaser.php + numbered-teaser.js) : un seul item
   actif à la fois — inactif = beige à 50% d'opacité, actif = numéro rouge + texte plein. */
.numbered-list-index,
.numbered-list-item h2,
.numbered-list-item h3,
.numbered-list-item p {
  color: var(--tok-color-beige);
  opacity: .5;
  transition: color .3s ease, opacity .3s ease;
}
.numbered-list-item h2 { opacity: .35; }
.numbered-list-item.is-active .numbered-list-index { color: var(--tok-color-rouge-vif); opacity: 1; }
.numbered-list-item.is-active h2,
.numbered-list-item.is-active h3,
.numbered-list-item.is-active p { color: var(--tok-text); opacity: 1; }

/* --- média + texte côte à côte (spotlight Accueil, Notre histoire...) --- */

.media-text {
  display: flex;
  align-items: center;
  gap: var(--tok-space-lg);
  flex-wrap: wrap;
}
.media-text .card-media { flex: 1 1 280px; aspect-ratio: 16 / 10; }
/* Colonne texte, quel que soit l'élément (div, ou <ol> du numbered-teaser) : sans flex-basis,
   un texte long prendrait sa largeur naturelle et passerait sous l'image. */
.media-text > :last-child { flex: 2 1 400px; min-width: 0; }
.media-text--wide { gap: var(--tok-space-2xl); }
/* Inverse l'ordre visuel image/texte (desktop uniquement — le sens du DOM ne change
   pas, donc l'empilement mobile reste identique quelle que soit l'alternance). */
.media-text--reverse { flex-direction: row-reverse; }
.card-media.card-media--square { aspect-ratio: 1 / 1; --tok-shear: 12%; }

/* Slider « Marche à suivre » (cf. numbered-teaser.php + numbered-teaser.js) : le conteneur
   réserve l'espace (aspect-ratio), les deux calques y sont en position absolute — le calque
   overlay reçoit la nouvelle image et fade in par-dessus le calque base, qui est ensuite mis
   à jour pour le prochain changement, sans flash entre deux crossfades successifs. */
.numbered-teaser-media { position: relative; flex: 1 1 280px; max-width: 520px; aspect-ratio: 1 / 1; }
.card-media.numbered-teaser-image-base,
.card-media.numbered-teaser-image-overlay { position: absolute; inset: 0; }
.card-media.numbered-teaser-image-overlay { opacity: 0; transition: opacity .6s ease; }
.card-media.numbered-teaser-image-overlay.is-fading { opacity: 1; }

/* --- carte (grille Expositions / Location / Événements) --- */

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--tok-space-lg);
}
.card { display: flex; flex-direction: column; gap: var(--tok-space-sm); }
.card-media {
  position: relative;
  aspect-ratio: 4 / 3;
  background-size: cover;
  background-position: center;
  background-color: var(--tok-surface);
}
.card-media .btn-icon { position: absolute; right: var(--tok-space-sm); bottom: var(--tok-space-sm); }
/* Bords inclinés sur un card-media 4/3 (cf. page Location) : angle constant --tok-shear-ratio
   (0.12) appliqué à la hauteur réelle du bloc, converti en % de la largeur pour le clip-path
   (%x = %y du ratio d'aspect) — 0.12 × 3/4 = 9%, cf. formule #formes dans design-system.html. */
.card-media.shear-both { --tok-shear: 9%; }
.card h3, .card h4 { margin: 0; }
.card p { color: var(--tok-text-muted); }
.card-actions { display: flex; align-items: center; gap: var(--tok-space-lg); margin-top: var(--tok-space-xs); }

/* Gap réel 32px entre bords inclinés (Location + 1re section Événements) : ici les deux
   bords d'une paire de cartes penchent dans le MÊME sens (shear-both = parallélogramme),
   donc contrairement à .expo-showcase le cisaillement s'AJOUTE au gap de grille au lieu
   d'en manger une partie — écart réel = gap grille + shear. Colonnes en auto-fit (largeur
   variable), donc shear en % non figeable en px comme .gallery-row : chaque media absorbe
   la moitié de son propre cisaillement de chaque côté (-shear/2 gauche + -shear/2 droite),
   ce qui retire pile le shear ajouté entre deux cartes adjacentes, quelle que soit leur
   largeur — le gap de grille (32px) devient alors l'écart réel. Les bords extérieurs de la
   rangée débordent aussi légèrement dans le padding du container (même choix assumé que
   .gallery-row) : pas de sélecteur fiable pour un "dernier de la rangée" en auto-fit. */
.card-grid--gap-2xl { column-gap: var(--tok-space-2xl); }
.card-grid--gap-4xl { column-gap: var(--tok-space-4xl); }
.card-grid--gap-2xl .card-media.shear-both,
.card-grid--gap-4xl .card-media.shear-both { margin-inline: calc(var(--tok-shear) * -0.5); }

/* Variante grille à colonnes fixes (au lieu d'auto-fit) : garantit un nombre de cartes
   par ligne stable quel que soit l'écran — nécessaire dès qu'on veut un nombre de lignes
   précis (ex. 6 cartes en 2 lignes de 3 sur Événements), ce qu'auto-fit ne garantit pas. */
.card-grid--3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 1199px) { .card-grid--3 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 767px) { .card-grid--3 { grid-template-columns: 1fr; } }
/* 4 colonnes desktop / 2 tablette / 1 mobile (bandeau Atouts depuis D_013 : deux items
   partis clore la section Expérience, il n'en reste que 4). */
.card-grid--4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 1199px) { .card-grid--4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 767px) { .card-grid--4 { grid-template-columns: 1fr; } }

/* --- catalogue Expositions (hub, cf. Expositions - hub.pdf) : contenu dans le
   .container standard (max-width 1720px, cf. tokens.css), regroupé en lignes de 3
   (.expo-row, cf. exposition-grid.php) plutôt qu'une grille plate — nécessaire pour que
   le hover d'une carte (cf. plus bas) ne redimensionne que sa propre ligne, une grille
   CSS partageant ses pistes de colonnes entre toutes les lignes. Médias portrait à bords
   inclinés — TOUS les bords, pas seulement les bords intérieurs de rangée (écart
   volontaire avec la règle générale #formes design-system.html, propre à ce composant).
   Contenu en overlay bas-gauche révélé au survol. Gap 4px entre cartes / 8px entre lignes.
   --tok-shear = 16% (ratio 3/4 fixe : 0.12 × 4/3, cf. angle constant #formes) — en %,
   donc reste géométriquement correct même quand une carte change de largeur au hover
   (aspect-ratio fixe -> le ratio hauteur/largeur ne bouge pas). Un bord incliné "mange"
   visuellement du gap en plus du gap de ligne (cf. "piège évité" #formes) — chaque carte
   qui n'est pas la première d'une ligne (nth-child(2), nth-child(3)) compense avec
   margin-left: -var(--tok-shear), qui annule exactement cet excès (démontré : écart réel
   = gap_ligne + shear sans compensation, gap_ligne pile avec). Compensation pensée pour
   3/ligne — désactivée sous 1200px avec le reflow en flex-wrap (cf. media query plus bas),
   mais le bord incliné lui-même reste sur toutes les tailles d'écran. */
.expo-showcase { display: flex; flex-direction: column; gap: var(--tok-space-sm); }
/* aspect-ratio sur la LIGNE (pas sur chaque carte) : la largeur d'une .expo-row ne bouge
   jamais (seule la répartition interne — flex-basis des cartes — change au hover), donc
   une hauteur dérivée de cette largeur reste fixe même quand une carte grandit/rétrécit.
   9/4 = 3 cartes de ratio 3:4 (largeur:hauteur) posées côte à côte à largeur égale :
   largeur totale 3×3=9, hauteur commune 4 — reproduit exactement la hauteur d'origine
   (carte à 1/3 de large, ratio 3/4) sans jamais varier avec le hover. align-items:
   stretch (au lieu de start) pour que .expo-card / .expo-card-media remplissent cette
   hauteur fixe en 100% plutôt que de la calculer eux-mêmes. */
.expo-row { display: flex; align-items: stretch; gap: var(--tok-space-xs); aspect-ratio: 9 / 4; }

.expo-card { display: flex; flex-direction: column; flex: 0 0 33.3333%; transition: flex-basis .5s ease; }
/* Sous 1200px, la grille (repeat(2, 1fr)) cassait le flux : .expo-showcase-outro garde
   grid-column: span 2 même à 1 seule colonne explicite (768px), ce qui forçait la grille
   à créer une colonne implicite supplémentaire pour l'accueillir — cette colonne fantôme
   rétrécissait la piste des cartes sur TOUTES les lignes (les pistes de grille sont
   partagées par les lignes), d'où des cartes visuellement écrasées/manquantes. Un flex-wrap
   classique n'a pas ce couplage entre lignes : chaque carte gère sa propre largeur (flex-
   basis), sans effet de bord sur les autres. Placé APRÈS la règle de base .expo-card
   ci-dessus (même spécificité — sinon la base, plus bas dans le fichier, gagnerait). */
@media (max-width: 1200px) {
  .expo-showcase { display: flex; flex-direction: row; flex-wrap: wrap; gap: var(--tok-space-sm); } /* flex-direction
  explicite : la règle de base (desktop) pose flex-direction: column, jamais réinitialisée
  sinon (display seul ne reset pas les autres propriétés flex) -> cartes empilées en
  colonne + flex-basis: 50% calculé sur une hauteur indéfinie = quasi 0, plus aucune
  carte visible. */
  .expo-row { display: contents; } /* les cartes redeviennent des items directs du
  flex-wrap .expo-showcase pour le layout (reflow 2 puis 1 colonne) — le hover, scopé à
  .expo-row en display:flex, ne s'applique plus (display:contents n'a plus de boîte flex,
  donc plus d'aspect-ratio de ligne non plus : chaque carte reprend son propre ratio,
  cf. media query .expo-card-media plus bas). */
  .expo-card { flex: 1 1 calc(50% - var(--tok-space-sm) / 2); }
  .expo-showcase-outro { flex: 1 1 100%; }
}
@media (max-width: 768px) {
  .expo-card { flex: 1 1 100%; }
}
.expo-card-media {
  position: relative;
  overflow: hidden; /* l'overlay (position absolute, ancré en bas) grandit vers le
  haut au hover — le clip-path ne clippe que le rendu visuel, pas la zone scrollable :
  sans ce overflow, la croissance de la description fait bouger la scrollbar (donc
  toute la page) même quand rien ne dépasse visuellement. */
  width: 100%; /* fixe, sinon le stretch flex du parent inclut le margin-left négatif
  (compensation ci-dessous) dans son calcul de largeur et élargit/agrandit la carte au
  lieu de juste la décaler — même hauteur garantie sur les cartes d'une ligne. */
  height: 100%; /* remplit la hauteur fixe de .expo-row (cf. commentaire ci-dessus) — pas
  d'aspect-ratio ici : les images sont en background-size cover, un ratio de boîte qui
  bouge avec la largeur (hover) ne les déforme jamais, juste un cadrage différent. */
  background-size: cover;
  background-position: center;
  background-color: var(--tok-surface);
  --tok-shear: 16%; /* fallback <1200px (aspect-ratio propre, cf. media query ci-dessous) —
  remplacé ≥1201px, cf. media query juste après. */
}
@media (max-width: 1200px) {
  /* Plus de .expo-row en display:flex ici (cf. display:contents plus haut) : chaque
     carte reprend un ratio propre, comme avant l'ajout du hover. */
  .expo-card-media { height: auto; aspect-ratio: 3 / 4; }
}
@media (min-width: 1201px) {
  /* container-type: size sur .expo-row (hauteur ET largeur définies : aspect-ratio +
     flex, cf. plus haut) expose cqh aux descendants = % de SA hauteur, qui ne bouge
     jamais au hover (contrairement à la largeur d'une carte). Un shear en % de la
     largeur de la carte (comme le 16% ci-dessus) changerait donc d'angle visuel au
     hover — cqh donne un shear en pixels constant quelle que soit la carte
     hovered/shrunk, même principe que #formes (ratio × hauteur) mais exprimé via la
     hauteur de la LIGNE plutôt que celle, désormais variable, de la carte elle-même. */
  .expo-row { container-type: size; }
  .expo-card-media { --tok-shear: calc(var(--tok-shear-ratio) * 100cqh); }
}
.expo-row > .expo-card .expo-card-media { clip-path: polygon(var(--tok-shear) 0, 100% 0, calc(100% - var(--tok-shear)) 100%, 0 100%); }
/* Compensation CUMULATIVE : depuis que toutes les cartes sont inclinées des 2 côtés
   (y compris la 1ère de ligne), chaque carte hérite aussi du décalage de celle qui la
   précède — la 2e compense son propre bord (-1×shear), la 3e doit rattraper le sien
   ET celui déjà appliqué à la 2e (-2×shear), sinon l'écart 2-3 se retrouve en trop
   (bug observé : cartes 1-2 quasi collées, gros vide entre 2-3). */
.expo-row > .expo-card:nth-child(2) .expo-card-media { margin-left: calc(-1 * var(--tok-shear)); }
.expo-row > .expo-card:nth-child(3) .expo-card-media { margin-left: calc(-2 * var(--tok-shear)); }
/* :nth-child(n) plutôt que .expo-card-media seul : même poids de spécificité que les
   règles margin-left ci-dessus (sinon celles-ci gagnent quel que soit l'ordre des règles).
   Sous 1200px, .expo-row passe en display:contents (cf. plus haut) : les cartes quittent
   la rangée flex de 3 pour devenir des items du flex-wrap .expo-showcase (2 puis 1
   colonne) — la compensation cumulative de gap n'a plus de sens (le gap du wrap suffit),
   d'où le reset margin-left.
   Le clip-path (bords inclinés), lui, reste : chaque carte garde son --tok-shear propre
   (16%, fallback posé plus haut sur .expo-card-media, cf. commentaire associé) qui est
   déjà correct pour son propre ratio 3/4 — rien à désactiver ici. */
@media (max-width: 1200px) {
  .expo-row > .expo-card:nth-child(n) .expo-card-media { margin-left: 0; }
}

/* Hover "accordéon" : réservé au layout desktop 3/ligne (.expo-row en flex, cf.
   media query 1201px plus haut) — sous 1200px .expo-row passe en display:contents
   mais :has() continue de matcher à travers (pas de boîte, le sélecteur DOM reste
   valide), donc sans cette garde la largeur des cartes changeait quand même au
   tap mobile (pas de vrai hover, juste l'état collant post-tap de certains
   navigateurs) → flash de largeur juste avant la navigation vers la page de
   l'exposition. Gardé desktop uniquement, où l'effet a un sens. */
@media (min-width: 1201px) {
  /* La carte survolée passe de 1/3 à 5/9 (~55.6%), ses 2 voisines DE LA MÊME LIGNE
     de 1/3 à 2/9 (~22.2%) chacune — 5/9 + 2/9 + 2/9 = 1, donc la ligne garde sa
     largeur totale (pas de reflow). Scopé avec :has(.expo-card:nth-child(3)) : une
     ligne incomplète (dernière ligne + .expo-showcase-outro, cf. plus bas) n'a pas
     de 3e carte, la garde empêche donc toute croissance qui romprait le total 100%
     de la ligne. */
  .expo-row:has(.expo-card:nth-child(3)):has(.expo-card:nth-child(1):hover) .expo-card:nth-child(1) { flex-basis: 55.5556%; }
  .expo-row:has(.expo-card:nth-child(3)):has(.expo-card:nth-child(1):hover) .expo-card:nth-child(2),
  .expo-row:has(.expo-card:nth-child(3)):has(.expo-card:nth-child(1):hover) .expo-card:nth-child(3) { flex-basis: 22.2222%; }

  .expo-row:has(.expo-card:nth-child(3)):has(.expo-card:nth-child(2):hover) .expo-card:nth-child(2) { flex-basis: 55.5556%; }
  .expo-row:has(.expo-card:nth-child(3)):has(.expo-card:nth-child(2):hover) .expo-card:nth-child(1),
  .expo-row:has(.expo-card:nth-child(3)):has(.expo-card:nth-child(2):hover) .expo-card:nth-child(3) { flex-basis: 22.2222%; }

  .expo-row:has(.expo-card:nth-child(3):hover) .expo-card:nth-child(3) { flex-basis: 55.5556%; }
  .expo-row:has(.expo-card:nth-child(3):hover) .expo-card:nth-child(1),
  .expo-row:has(.expo-card:nth-child(3):hover) .expo-card:nth-child(2) { flex-basis: 22.2222%; }

  /* Dernière ligne incomplète (cf. exposition-grid.php + .expo-showcase-outro plus bas) :
     même accordéon, mais un des "voisins" est le bloc texte plutôt qu'une carte. Il n'y a
     qu'une ou deux cartes à hover, jamais 3 -> pas besoin de garde :has(nth-child(3)). */
  .expo-row:has(.expo-showcase-outro--narrow):has(.expo-card:nth-child(1):hover) .expo-card:nth-child(1) { flex-basis: 55.5556%; }
  .expo-row:has(.expo-showcase-outro--narrow):has(.expo-card:nth-child(1):hover) .expo-card:nth-child(2),
  .expo-row:has(.expo-showcase-outro--narrow):has(.expo-card:nth-child(1):hover) .expo-showcase-outro--narrow { flex-basis: 22.2222%; }

  .expo-row:has(.expo-showcase-outro--narrow):has(.expo-card:nth-child(2):hover) .expo-card:nth-child(2) { flex-basis: 55.5556%; }
  .expo-row:has(.expo-showcase-outro--narrow):has(.expo-card:nth-child(2):hover) .expo-card:nth-child(1),
  .expo-row:has(.expo-showcase-outro--narrow):has(.expo-card:nth-child(2):hover) .expo-showcase-outro--narrow { flex-basis: 22.2222%; }

  /* Ligne à 1 seule carte (le bloc texte prend alors 2 cases, 66.7%, cf. plus bas) : la
     carte grandit pareil (5/9), le bloc texte se réduit d'autant (44.4% = 2 × 2/9). */
  .expo-row:has(.expo-showcase-outro:not(.expo-showcase-outro--narrow)):has(.expo-card:hover) .expo-card { flex-basis: 55.5556%; }
  .expo-row:has(.expo-showcase-outro:not(.expo-showcase-outro--narrow)):has(.expo-card:hover) .expo-showcase-outro { flex-basis: 44.4444%; }
}

/* Scrim assombrissant la partie basse au hover (même technique que .hero::after) :
   pseudo-élément séparé plutôt qu'un background direct sur .expo-card-overlay, pour
   fader indépendamment du texte (dont l'apparition est déjà gérée par .expo-card-reveal)
   et rester clippé par le même clip-path que l'image (cf. .expo-card-media plus haut). */
.expo-card-media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(rgba(0, 0, 0, 0) 40%, rgba(0, 0, 0, .6) 100%);
  opacity: 0;
  transition: opacity .5s ease-in-out;
  pointer-events: none;
}
@media (min-width: 1201px) {
  .expo-card:hover .expo-card-media::after,
  .expo-card:focus-within .expo-card-media::after { opacity: 1; }
}
/* Sous 1200px, la description reste toujours affichée (cf. .expo-card-reveal plus bas)
   -> le scrim reste allumé en permanence, pas de hover fiable au tactile. */
@media (max-width: 1200px) {
  .expo-card-media::after { opacity: 1; }
}

/* Contenu calé bas-gauche SUR l'image (jamais dans la zone rognée par le clip-path :
   le coin bas-gauche reste toujours intact, cf. polygon ci-dessus). z-index : au-dessus
   du scrim (::after ci-dessus, auto/DOM-order sinon peindrait par-dessus le texte). */
.expo-card-overlay {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 1;
  /* Marge de sécurité contre les bords inclinés (--tok-shear hérité de .expo-card-media) :
     à droite le bord rentre de tout le shear en bas de carte, là où vit le texte ; à
     gauche il rentre vers le haut, donc de moins en moins à hauteur du titre — la moitié
     suffit. Sans ça le texte long (description mobile) sortait du clip-path. */
  padding: var(--tok-space-2xl) calc(var(--tok-space-2xl) + var(--tok-shear)) var(--tok-space-2xl) calc(var(--tok-space-2xl) + var(--tok-shear) / 2);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.expo-card-overlay h3 { margin: 0; }
.expo-card-overlay h3 a { color: inherit; text-decoration: none; }

/* height: 0 -> auto animé : grid-template-rows (0fr -> 1fr) + overflow hidden sur
   l'enfant, seule technique CSS pure pour une hauteur de contenu variable (pas de JS,
   pas de max-height à deviner). */
.expo-card-reveal {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .5s ease-in-out;
  margin-top: var(--tok-space-xs);
}
@media (min-width: 1201px) {
  .expo-card:hover .expo-card-reveal,
  .expo-card:focus-within .expo-card-reveal { grid-template-rows: 1fr; }
}
/* Sous 1200px, pas de hover fiable (tactile) : la description reste toujours
   affichée plutôt que dépendre d'un :hover/:focus-within qui ne se déclenche pas
   proprement au tap — le "+" est un lien, un tap doit filer direct vers la page. */
@media (max-width: 1200px) {
  .expo-card-reveal { grid-template-rows: 1fr; }
}
.expo-card-reveal-inner { overflow: hidden; }
.expo-card-reveal-inner p { margin: 0; max-width: 440px; color: var(--tok-color-beige); }


/* Bloc texte à la suite de la dernière ligne de cartes (cf. exposition-grid.php) —
   comble la ligne incomplète quand le nombre de cartes n'est pas un multiple de 3 : 2
   cases de large (66.7%) si 1 carte manque, --narrow (1 case, 33.3%) si 2 cartes
   manquent. Sous 1200px (.expo-row en display: contents, layout = .expo-showcase en
   flex-wrap, cf. plus haut), ce bloc passe en pleine largeur (flex: 1 1 100%) comme
   n'importe quelle carte qui termine sa ligne — plus de span de grille à gérer ici. */
.expo-showcase-outro {
  flex-basis: 66.6667%;
  transition: flex-basis .5s ease; /* même accordéon que .expo-card, cf. plus haut */
  align-self: stretch; /* .expo-row a align-items: stretch (voulu pour que la hauteur de
  ligne, désormais fixe, s'applique aussi à ce bloc) — sans ça sa propre hauteur de
  contenu ne remplirait pas la ligne, empêchant le centrage vertical ci-dessous. */
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.expo-showcase-outro--narrow { flex-basis: 33.3333%; }

/* --- page Contact (formulaire de devis, cf. contact-page.pdf) --- */

/* Image plein viewport (100svh/100vw — svh : hauteur visible barre d'adresse mobile
   affichée, 100vh la déborderait), fixe au scroll : le contenu (colonne droite)
   vit dans son propre flux, potentiellement plus haut qu'un écran, sans jamais
   étirer l'image derrière lui. */
.contact-hero {
  position: relative;
  min-height: 100svh;
  background-size: cover;
  background-position: center;
  background-attachment: fixed;
}
.contact-hero::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, .6);
}
.contact-hero .container {
  position: relative;
  z-index: 1;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--tok-space-2xl);
  min-height: 100svh;
}
@media (max-width: 768px) {
  .contact-hero .container { flex-direction: column; }
}

/* Colonne gauche : titre + email/téléphone (rythme vertical tenu par les utilitaires
   mt-2xl/mt-lg posés directement dans le template), alignée à la même hauteur que la
   colonne de droite. */
.contact-title-col { padding-top: 128px; max-width: 640px; }
.contact-title-col a.h2 { text-decoration: none; }
@media (max-width: 768px) {
  .contact-title-col { max-width: none; }
}

/* Colonne droite : form-card, alignée à la même hauteur que le titre à gauche
   (padding-top 128px, cf. .contact-title-col). */
.contact-intro { width: 50%; padding-top: 128px; padding-bottom: var(--tok-space-2xl); }
@media (max-width: 768px) {
  .contact-intro { width: 100%; }
}

.form-card {
  background: rgba(235, 215, 176, .1); /* = var(--tok-color-beige) à 10% */
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  padding: var(--tok-space-4xl);
  display: flex;
  flex-direction: column;
  gap: var(--tok-space-2xl);
}
.form-fields { display: flex; flex-direction: column; gap: var(--tok-space-lg); }
.form-field { display: flex; flex-direction: column; gap: var(--tok-space-sm); }
.form-field label {
  font-family: var(--tok-font-degular);
  font-weight: 900;
  font-size: 16px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--tok-color-beige);
}
.form-field input,
.form-field select,
.form-field textarea {
  background: rgba(235, 215, 176, .04); /* = var(--tok-color-beige) à 4% */
  border: none;
  color: var(--tok-text);
  padding-inline: var(--tok-space-lg);
  font-family: var(--tok-font-degular);
  font-size: 14px;
}
.form-field input,
.form-field select { height: 48px; }
.form-field select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: calc(var(--tok-space-lg) * 2 + 12px);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1L6 6L11 1' stroke='%23EBD7B0' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--tok-space-lg) center;
  cursor: pointer;
}
/* Popup natif : background-color sur <option> est respecté par Chrome/Edge/Firefox
   (contrairement au reste du popup, hors contrôle CSS). */
.form-field select option {
  background-color: var(--tok-surface);
  color: var(--tok-text);
}
.form-field textarea { padding-block: var(--tok-space-sm); min-height: 120px; resize: vertical; }
.form-consent {
  display: flex;
  align-items: flex-start;
  gap: var(--tok-space-sm);
  font-size: 13px;
  color: var(--tok-text-muted);
}

/* --- bloc détails techniques / nos prestations (Accueil, Exposition, Location) --- */

/* Liste de prestations en lignes alternées texte/image (cf. .media-text plus haut) —
   une prestation = une carte .media-text, alternance gauche/droite pour faciliter la
   lecture (cf. tech-details.php). */
.tech-rows {
  display: flex;
  flex-direction: column;
  gap: var(--tok-space-2xl);
}
/* Texte en 1er dans le DOM ici (cf. tech-details.php) : il reprend la colonne qui grandit
   (flex: 2 1 400px de .media-text) et une longueur de ligne limitée pour rester
   confortable à lire sur grand écran. */
.tech-rows .media-text > div:first-child { flex: 2 1 400px; max-width: 620px; }
/* Image plus basse/large que le 16/10 par défaut de .media-text — format paysage
   plus marqué pour ces cartes (au lieu du carré 1/1 de teaser-section/numbered-teaser).
   Colonne plus étroite que le flex-basis 280px par défaut : l'image reste secondaire
   par rapport au texte dans ces lignes. */
.tech-rows .media-text > .card-media { flex: 1 1 220px; aspect-ratio: 16 / 9; }

/* --- section en bandeau + titre à icône (bandeau Atouts Creatiz, cf. D_012) --- */

/* Variante de .section : fond --tok-surface pleine largeur sans sortir du .container.
   border-image en outset peint le fond jusqu'aux bords de l'écran sans créer d'overflow
   horizontal (contrairement à 100vw, qui inclut la scrollbar réservée par scrollbar-gutter). */
.section--band { border-image: conic-gradient(var(--tok-surface) 0 0) fill 0 / / 0 100vw; }

/* Titre précédé d'une icône 48px. Hauteur mini = celle de l'icône : dans une grille, les
   textes sous le titre restent alignés même quand un titre tient sur une seule ligne. */
.icon-heading { display: flex; align-items: center; gap: var(--tok-space-sm); min-height: 48px; }
.icon-heading img { flex-shrink: 0; width: 48px; height: 48px; }

/* Titre en capitales droites (maquette Atouts) — combinable avec h1-h5 / .h1-.h5. */
.heading-caps { font-style: normal; text-transform: uppercase; }

/* --- galerie photo (page Exposition, cf. Expositions.pdf) --- */

/* Motif transversal (cf. #formes design-system.html) : seuls les bords EXTÉRIEURS de
   la rangée sont droits, tous les bords intérieurs inclinés à angle constant. Rangée à
   hauteur fixe (comme .shapes-demo du design system) — le cisaillement dépend de la
   hauteur du bloc, pas de sa largeur. Chaque carte déborde de --panel-shear dans le
   gap voisin (overflow visible, pas de clip sur la rangée) : sans ça l'écart réel
   grimperait à 16px + le décalage au lieu de rester 16px constant. 3 rangées de 3
   cartes ; le gap vertical entre rangées reste un simple margin-top (le haut/bas des
   panneaux n'est jamais incliné, seuls les bords gauche/droit le sont). */
.gallery-row {
  position: relative;
  height: 320px;
  --slot: calc((100% - 2 * var(--tok-space-4xl)) / 3);
  --panel-shear: calc(var(--tok-shear-ratio) * 320px);
}
.gallery-row + .gallery-row { margin-top: var(--tok-space-4xl); }
/* Tous les bords inclinés, y compris ceux de la rangée (contrairement à .shapes-demo) :
   la rangée vit déjà à l'intérieur du padding du .container (max-width 1720px), donc
   un bord extérieur droit n'a pas de bord de viewport à honorer — chaque carte déborde
   légèrement dans ce padding plutôt que de rester droite. Même largeur/clip-path pour
   les 3, seule la position change. */
.gallery-card {
  position: absolute;
  top: 0;
  height: 100%;
  width: calc(var(--slot) + var(--panel-shear));
  background-size: cover;
  background-position: center;
  background-color: var(--tok-surface);
  clip-path: polygon(var(--panel-shear) 0, 100% 0, calc(100% - var(--panel-shear)) 100%, 0 100%);
}
.gallery-row .gallery-card:nth-child(1) { left: 0; }
.gallery-row .gallery-card:nth-child(2) { left: calc(var(--slot) + var(--tok-space-4xl)); }
.gallery-row .gallery-card:nth-child(3) { left: calc(2 * (var(--slot) + var(--tok-space-4xl))); }
/* Sous 768px : plus de rangée à 3, empilement vertical simple — mais le cisaillement
   reste (contrairement à .expo-showcase) : --panel-shear recalculé sur la nouvelle
   hauteur de carte (240px au lieu des 320px de la rangée desktop) pour garder le
   même angle constant, même principe que .btn-icon qui recalcule --tok-shear par
   palier plutôt que de réutiliser une valeur pensée pour une autre hauteur. */
@media (max-width: 768px) {
  .gallery-row {
    height: auto;
    position: static;
    display: flex;
    flex-direction: column;
    gap: var(--tok-space-lg);
    --panel-shear: calc(var(--tok-shear-ratio) * 240px);
  }
  .gallery-row .gallery-card { position: static; width: 100%; height: 240px; }
}

/* --- footer (structure validée) --- */

.footer {
  background: var(--tok-bg-footer);
  border-top: 1px solid rgba(235, 215, 176, .2); /* = var(--tok-color-beige) à 20% */
  padding-block: var(--tok-space-2xl) var(--tok-space-lg);
  padding-inline: var(--tok-page-padding-inline);
}
.footer-columns { display: flex; justify-content: space-between; gap: var(--tok-space-2xl); flex-wrap: wrap; }
.footer-col { display: flex; flex-direction: column; gap: var(--tok-space-xs); min-width: 160px; }
.footer-col.brand { max-width: 260px; }
.footer-col .f-brand { display: flex; align-items: center; gap: var(--tok-space-sm); margin-bottom: var(--tok-space-sm); }
.footer-col .f-brand img { height: 40px; width: 40px; display: block; }
.footer-col .f-brand .wordmark {
  font-family: var(--tok-font-degular);
  font-weight: 900;
  font-size: 16px;
  color: var(--tok-color-beige);
  text-transform: uppercase;
}
.footer-col h4 {
  font-family: var(--tok-font-degular);
  font-weight: 700;
  font-size: 14px;
  color: var(--tok-color-beige);
  margin: 0 0 var(--tok-space-sm);
}
.footer-col a {
  font-family: var(--tok-font-degular);
  font-size: 14px;
  color: var(--tok-text-muted);
  text-decoration: none;
}
.footer-col a:hover { color: var(--tok-color-beige); }
.footer-rule {
  border: none;
  border-top: 1px solid rgba(235, 215, 176, .2); /* = var(--tok-color-beige) à 20% */
  margin: 32px 0;
}
.footer-bottom { display: flex; justify-content: space-between; align-items: center; gap: var(--tok-space-lg); flex-wrap: wrap; }
.footer-bottom p { font-family: var(--tok-font-degular); font-size: 13px; color: var(--tok-text-muted); margin: 0; }
.footer-legal { display: flex; gap: var(--tok-space-lg); }
.footer-legal a { font-family: var(--tok-font-degular); font-size: 13px; color: var(--tok-text-muted); text-decoration: none; }
.footer-legal a:hover { color: var(--tok-color-beige); }
