@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&family=Karla:wght@400;500&family=IBM+Plex+Mono:wght@400;500&display=swap');

/* Palette unique (pas de mode sombre), choisie à la main via l'outil de calibrage
   (voir static/js/calibrage_couleurs.js - retiré du site, à recharger ponctuellement
   pendant le dev si besoin d'ajuster). */
:root {
  --basalt: #e9e9e9;
  --basalt-2: #e9e9e9;
  --basalt-3: #ffffff;
  --fog: #1f2933;
  --moss: #6290c8;
  --moss-dim: #ffffff;
  --ember: #4682b4;
  --slate: #1f487e;
  --line: #e9e9e9;
  /* Certains éléments (légendes/boutons superposés aux photos, lightbox) gardent un
     fond sombre semi-transparent quelle que soit la charte — plus lisible par-dessus
     une photo quelconque qu'un fond clair. Ces deux variables leur donnent un texte
     clair adapté, puisque --fog/--slate sont sombres pour le fond blanc. */
  --overlay-texte: #f2f5f8;
  --overlay-texte-attenue: #c3ccd6;
  --radius: 3px;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--basalt);
  color: var(--fog);
  font-family: 'Karla', sans-serif;
  font-size: 16px;
  line-height: 1.5;
}

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

a { color: inherit; }
a:focus-visible, button:focus-visible, input:focus-visible {
  outline: 2px solid var(--ember);
  outline-offset: 2px;
}

.label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--slate);
}

/* ---------- Nav ---------- */
.nav {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.4rem 2rem;
  /* Ni fond ni bordure (transparent par défaut) : la carte déborde dessous (voir
     #map, margin-top négatif) et doit rester pleinement visible à travers la nav -
     une bordure aurait laissé une ligne claire coupant en travers.
     pointer-events:none sur la nav elle-même (remis à auto seulement sur ses liens,
     ci-dessous) : sinon sa boîte (pleine largeur, transparente) interceptait les
     clics destinés à la carte en dessous - un pays proche du haut (Canada, Islande,
     Îles Féroé...) au zoom initial se retrouvait avec un nom cliquable en théorie
     (voir map.js, .pays-label--actif) mais jamais atteint en pratique. */
  pointer-events: none;
}
.nav__mark {
  font-family: 'Outfit', sans-serif;
  font-size: 1.15rem;
  font-weight: 600;
  text-decoration: none;
  letter-spacing: 0.01em;
  pointer-events: auto;
}
/* Accueil : la carte passe sous la barre de navigation (voir .nav), et le titre se lisait mal
   par-dessus les pays colorés. Une zone sombre translucide part du coin haut gauche et
   s'estompe vers la droite et vers le bas ; le titre passe en clair, comme les autres textes
   posés sur un fond sombre (--overlay-texte). Réservé à l'accueil : ailleurs, la barre est
   sur le fond clair de la page et garde son titre sombre. */
.page-carte .nav__mark { color: var(--overlay-texte); }
.page-carte .nav::before {
  content: '';
  position: absolute;
  z-index: -1;
  top: 0;
  left: 0;
  width: min(36rem, 100%);
  height: calc(100% + 1.6rem);
  background: rgba(20, 24, 27, 0.6);
  -webkit-mask-image: linear-gradient(to right, #000 0%, #000 45%, transparent 100%),
                      linear-gradient(to bottom, #000 0%, #000 55%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(to right, #000 0%, #000 45%, transparent 100%),
              linear-gradient(to bottom, #000 0%, #000 55%, transparent 100%);
  mask-composite: intersect;
  pointer-events: none;
}
.nav__links { display: flex; gap: 1.8rem; list-style: none; margin: 0; padding: 0; }
.nav__links a {
  text-decoration: none;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--slate);
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
  transition: color 0.15s ease, border-color 0.15s ease;
  pointer-events: auto;
}
.nav__links a:hover, .nav__links a.active { color: var(--ember); border-color: var(--ember); }

/* ---------- Hero ---------- */
.hero {
  padding: 4.5rem 2rem 3rem;
  max-width: 780px;
}
.hero h1 {
  font-family: 'Outfit', sans-serif;
  font-weight: 600;
  font-size: clamp(2.2rem, 5vw, 3.4rem);
  line-height: 1.08;
  margin: 0 0 1rem;
}
.hero p {
  color: var(--slate);
  font-size: 1.05rem;
  max-width: 46ch;
  margin: 0;
}
.hero p.label { max-width: none; font-size: 0.72rem; }
.hero--compact { padding: 2.2rem 2rem 1.5rem; }
.hero--compact h1 { font-size: clamp(1.8rem, 4vw, 2.6rem); }

/* ---------- Grille photos ---------- */
/* Lignes "justifiées" : chaque ligne remplit toute la largeur, la hauteur des cartes
   s'ajuste légèrement d'une ligne à l'autre plutôt que de rogner les photos portrait
   au même gabarit que les paysages (largeur/hauteur calculées en JS, voir carousel.js). */
.grid {
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--line);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.grid__ligne {
  display: flex;
  gap: 2px;
}
.card {
  background: var(--basalt);
  position: relative;
  overflow: hidden;
  flex: 0 0 auto;
  display: flex;
  align-items: flex-end;
  cursor: pointer;
  transition: transform 0.1s ease;
}
.card:active { transform: scale(0.97); }
.card__ph {
  position: absolute;
  inset: 0;
  opacity: 0.9;
  background-size: cover;
  background-position: center;
}
.card__ph--vide {
  background: var(--basalt-3);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  opacity: 1;
}
.card__specimen {
  position: relative;
  z-index: 1;
  width: 100%;
  padding: 0.85rem 1rem;
  background: linear-gradient(180deg, transparent, rgba(20,24,27,0.92) 55%);
}
.card__espece {
  font-family: 'Outfit', sans-serif;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--overlay-texte);
  margin: 0 0 0.15rem;
}
.card__meta {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--overlay-texte);
  opacity: 0.85;
  display: flex;
  gap: 0.5rem;
}

/* ---------- Carte + carousel (page fusionnée) ---------- */
.explorer { display: flex; flex-direction: column; }
.map-wrap { position: relative; }
/* Pas de fond de tuiles (voir map.js) : cette couleur de fond EST l'océan, les
   polygones pays (styleInactif/styleActif dans map.js) dessinent les terres par-dessus.
   margin-top négatif : la carte déborde sous la nav (transparente, voir .nav) - au
   moins autant que la hauteur de la nav (~4.6rem), sinon une bande du fond de page
   (gris clair) reste visible entre le haut de l'écran et le haut de la carte. */
#map { width: 100%; height: 58vh; min-height: 380px; background: var(--basalt-2); margin-top: -4.7rem; position: relative; z-index: 1; }
/* Contrôle de zoom repoussé sous la nav (voir map.js, position 'topright') : sans ce
   décalage, avec le débordement de la carte ci-dessus, il se retrouve sous le lien
   "Explorer" en haut à droite de la nav et les deux se chevauchent visuellement. */
#map .leaflet-top.leaflet-right { margin-top: 5.5rem; }

/* Labels pays/océans (voir map.js) : décalés au-dessus de leur point (translate Y
   négatif) plutôt que centrés dessus - sinon le pin photo d'un pays actif (souvent
   placé près du centroïde utilisé pour le label) recouvre carrément le texte.
   pointer-events:none est indispensable : sans ça, le label (span réel, au-dessus du
   polygone dans la pile des panes Leaflet - markerPane à 600, le contour du pays est
   dans overlayPane à 400) intercepte le clic à la place du polygone en dessous, qui
   ne réagit alors plus (zoom + ouverture du carousel perdus). Halo de texte (pas de
   fond plein) pour rester lisible sur n'importe quel remplissage - terre blanche ou
   pays actif en bleu - sans ajouter d'encadré, hors sujet avec le reste de la carte
   (aucune bordure/boîte ailleurs). */
.pays-label {
  position: relative;
  transform: translate(-50%, calc(-50% - 10px));
  display: inline-block;
  white-space: nowrap;
  pointer-events: none;
  font-family: 'Outfit', sans-serif;
  font-size: 0.62rem;
  font-weight: 500;
  color: var(--slate);
  text-shadow: 0 0 3px var(--basalt-3), 0 0 3px var(--basalt-3), 0 0 3px var(--basalt-3);
}
/* pointer-events remis à auto seulement ici (voir pointer-events:none plus haut, sur
   .pays-label de base) : un pays actif est maintenant cliquable comme son contour
   (voir map.js, ouvrirPays) - un pays inactif ou un océan reste décoratif, le clic y
   continue de traverser jusqu'à ce qu'il y a en dessous. */
.pays-label--actif {
  font-size: 0.72rem; font-weight: 700; transform: translate(-50%, calc(-50% - 16px));
  pointer-events: auto; cursor: pointer;
}
.pays-label--actif:hover { color: var(--ember); }
.pays-label--ocean {
  font-style: italic;
  font-weight: 400;
  letter-spacing: 0.03em;
  color: var(--slate);
  opacity: 0.55;
}

.carousel__back { padding: 0.6rem 1rem; }
.carousel__back a {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.75rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--slate);
  text-decoration: none;
}
.carousel__back a:hover { color: var(--ember); }

/* Empilement vertical simple : l'appartenance à une ligne est décidée explicitement
   en JS (carousel.js, organiserParTaille) via .carousel-ligne, pas laissée au
   retour à la ligne automatique d'un flex-wrap — qui pouvait ne plus correspondre
   au regroupement calculé une fois les hauteurs mises à l'échelle (arrondis). */
.carousel-root {
  display: flex;
  flex-direction: column;
}
/* Une ligne = un ou plusieurs .carousel-pays côte à côte, chacun dimensionné à son
   propre contenu (fit-content) plutôt que de forcer toute la largeur : un pays/lieu
   avec peu de photos ne remplit pas sa ligne à lui seul, plusieurs se regroupent
   alors sur la même ligne au lieu de laisser des lignes à moitié vides. flex-shrink
   (sans flex-wrap) absorbe un éventuel micro-dépassement d'arrondi en compressant
   légèrement plutôt qu'en renvoyant un élément à la ligne suivante. */
.carousel-ligne {
  display: flex;
  flex-wrap: nowrap;
}
.carousel-pays {
  width: fit-content;
  max-width: 100%;
  min-width: 0;
  flex-shrink: 1;
}
/* Chevauchement volontaire d'1px entre carrousels voisins, comme entre les photos d'un même
   film (voir .filmstrip__item) : la largeur de chaque carrousel (fit-content) est fractionnaire,
   donc leur frontière tombe entre deux pixels d'écran - et chaque film étant une zone
   défilable dessinée séparément, l'arrondi peut laisser passer un trait de 1px du fond
   clair. Le suivant recouvre ce pixel ; imperceptible sur des photos. Valable côte à côte
   (dans une ligne) comme empilés (d'une ligne à l'autre). */
.carousel-pays + .carousel-pays { margin-left: -1px; }
.carousel-ligne + .carousel-ligne { margin-top: -1px; }

.carousel {
  position: relative;
  height: 130px;
  /* Fondu vers la couleur de fond de la page (pas blanc en dur) - s'adapte tout seul
     en mode sombre au lieu d'auréoler chaque carousel de blanc sur un fond noir. */
  box-shadow: 0 0 22px 4px color-mix(in srgb, var(--basalt) 45%, transparent);
  /* Le voile du titre de lieu (.filmstrip__titre::before) déborde volontairement à
     droite (3.6rem) pour son fondu - sans ça, il pouvait légèrement empiéter sur la
     tuile voisine ou la ligne suivante au lieu de rester contenu dans son propre
     carousel. box-shadow n'est pas affecté (peint hors du cadre de débordement). */
  overflow: hidden;
}
.carousel__prompt { text-align: center; padding: 1.8rem; }

/* Titres posés sur les photos (ligne pays/lieu en haut à gauche, tuile de groupe en bas à gauche) :
   plutôt qu'un rectangle net, un voile sombre qui part du COIN et s'estompe progressivement vers
   l'intérieur (voir ::before) - même principe que le titre du site sur la carte. Le texte se colle
   au coin ; le voile déborde largement du texte à droite et du côté opposé au coin, sans bord net. */
.filmstrip__titre {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 5;
  font-family: 'Outfit', sans-serif;
  font-size: 0.8rem;
  font-weight: 400;
  line-height: 1.4;
  color: var(--overlay-texte);
  text-shadow: 0 1px 3px rgba(20, 24, 27, 0.6);
  padding: 0.5rem 0.9rem 0.6rem 0.7rem;
  /* Ni fond ni bordure : les titres cliquables sont des <button>, dont le fond gris par défaut
     réapparaissait sous le voile en un rectangle net. */
  background: none;
  border: 0;
  pointer-events: none;
  /* Une seule ligne forcée, quel que soit le titre (nom de pays/lieu long, ou nom d'espèce +
     nombre de photos) : tronquée en "…" plutôt que passée à la ligne, qui mangeait une bonne partie
     d'un petit carousel (130px de haut). La troncature est portée par le texte lui-même
     (.filmstrip__titre-texte, ou les lignes d'une tuile de groupe), pas par ce cadre : son voile
     doit pouvoir déborder. */
  max-width: 100%;
  white-space: nowrap;
}
.filmstrip__titre-texte {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Le voile : plein sous le texte, puis en fondu très progressif (plusieurs paliers, pas un simple
   dégradé linéaire qui laisse deviner un bord) sur les 3.6rem à droite et les 1.1rem du côté opposé
   au coin. z-index négatif : derrière le texte, dans le contexte d'empilement du titre.
   Dernier palier transparent à "100% - 2px" plutôt que 100% pile : la transparence totale est
   ainsi atteinte un peu AVANT le bord du voile (et de .carousel, qui le rogne - voir overflow:hidden
   plus bas) - sans cette marge, un arrondi de sous-pixel pouvait laisser un liseré sombre d'1-2px
   visible au ras du bord plutôt qu'un fondu qui se termine bien avant. */
.filmstrip__titre::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: -3.6rem;
  bottom: -1.1rem;
  z-index: -1;
  /* Fondu vertical réduit à 1.1rem (au lieu de 1.9rem à l'origine) : sur une tuile qui
     porte à la fois ce titre de lieu (en haut) ET son propre titre de groupe en bas
     (voir .filmstrip__titre--bas, creerTuileGroupe) - le cas du tout premier groupe
     d'un lieu à plusieurs groupes - les deux voiles se chevauchaient et pouvaient
     couvrir l'essentiel d'une tuile de ~130px (GRILLE_HAUTEUR_CIBLE), même sans aucun
     rétrécissement de ligne. En plus de cette réduction de base, le voile rétrécit
     encore proportionnellement (scaleY, --echelle-voile posée par
     ajusterHauteursCarousels/repartirEnLignes) sur une ligne plus basse que la cible -
     le texte du titre, lui, n'est jamais transformé (élément séparé) : seul ce halo
     décoratif varie. */
  transform-origin: top;
  transform: scaleY(var(--echelle-voile, 1));
  background: rgba(20, 24, 27, 0.58);
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 3.6rem), rgba(0, 0, 0, 0.72) calc(100% - 2.7rem), rgba(0, 0, 0, 0.38) calc(100% - 1.6rem), rgba(0, 0, 0, 0.12) calc(100% - 0.9rem), transparent calc(100% - 2px)),
                      linear-gradient(to bottom, #000 calc(100% - 1.1rem), rgba(0, 0, 0, 0.7) calc(100% - 0.8rem), rgba(0, 0, 0, 0.35) calc(100% - 0.45rem), rgba(0, 0, 0, 0.1) calc(100% - 0.25rem), transparent calc(100% - 2px));
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(to right, #000 calc(100% - 3.6rem), rgba(0, 0, 0, 0.72) calc(100% - 2.7rem), rgba(0, 0, 0, 0.38) calc(100% - 1.6rem), rgba(0, 0, 0, 0.12) calc(100% - 0.9rem), transparent calc(100% - 2px)),
              linear-gradient(to bottom, #000 calc(100% - 1.1rem), rgba(0, 0, 0, 0.7) calc(100% - 0.8rem), rgba(0, 0, 0, 0.35) calc(100% - 0.45rem), rgba(0, 0, 0, 0.1) calc(100% - 0.25rem), transparent calc(100% - 2px));
  mask-composite: intersect;
  pointer-events: none;
}
.filmstrip__titre--clickable {
  pointer-events: auto;
  cursor: pointer;
  border: none;
  /* Pas de "font: inherit" ici : le shorthand "font" réinitialise TOUTES ses
     sous-propriétés (taille, graisse, hauteur de ligne...) à la valeur héritée du
     parent - or .filmstrip__titre définit déjà tout ça explicitement juste au-dessus,
     et une règle d'auteur l'emporte de toute façon sur le style par défaut du
     navigateur pour un <button>. "font: inherit" ne servait donc à rien ici, et
     causait même un bug : un titre posé dans un .filmstrip__item (qui met
     line-height:0 pour supprimer l'espace sous l'image) héritait ce 0 à la place
     du line-height voulu. */
  transition: transform 0.1s ease;
}
.filmstrip__titre--clickable:hover { color: var(--ember); }
.filmstrip__titre--clickable:active { transform: scale(0.96); }
/* Titre de tuile de groupe (voir carousel.js, creerTuileGroupe) : en bas plutôt qu'en
   haut comme le titre du pays/lieu qui coiffe déjà toute la ligne - les deux se
   superposeraient sinon. Ancré à gauche comme le titre du pays/lieu (pas étiré sur
   toute la largeur). */
.filmstrip__titre--bas {
  top: auto;
  bottom: 0;
  padding: 0.6rem 0.9rem 0.5rem 0.7rem;
}
.filmstrip__titre--bas::before {
  top: -1.1rem;
  bottom: 0;
  /* Même réduction (1.9rem -> 1.1rem) et même mise à l'échelle proportionnelle que
     .filmstrip__titre::before - voir son commentaire. Ancrée en bas puisque ce
     voile-ci grandit vers le haut (top négatif) plutôt que vers le bas. */
  transform-origin: bottom;
  transform: scaleY(var(--echelle-voile, 1));
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 3.6rem), rgba(0, 0, 0, 0.72) calc(100% - 2.7rem), rgba(0, 0, 0, 0.38) calc(100% - 1.6rem), rgba(0, 0, 0, 0.12) calc(100% - 0.9rem), transparent calc(100% - 2px)),
                      linear-gradient(to top, #000 calc(100% - 1.1rem), rgba(0, 0, 0, 0.7) calc(100% - 0.8rem), rgba(0, 0, 0, 0.35) calc(100% - 0.45rem), rgba(0, 0, 0, 0.1) calc(100% - 0.25rem), transparent calc(100% - 2px));
  mask-image: linear-gradient(to right, #000 calc(100% - 3.6rem), rgba(0, 0, 0, 0.72) calc(100% - 2.7rem), rgba(0, 0, 0, 0.38) calc(100% - 1.6rem), rgba(0, 0, 0, 0.12) calc(100% - 0.9rem), transparent calc(100% - 2px)),
              linear-gradient(to top, #000 calc(100% - 1.1rem), rgba(0, 0, 0, 0.7) calc(100% - 0.8rem), rgba(0, 0, 0, 0.35) calc(100% - 0.45rem), rgba(0, 0, 0, 0.1) calc(100% - 0.25rem), transparent calc(100% - 2px));
}
/* Titre de tuile de groupe sur deux lignes : espèce, puis lieu / nombre de médias / période. */
.filmstrip__titre--groupe {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
}
.filmstrip__titre-principal,
.filmstrip__titre-detail {
  display: block;
  max-width: 100%;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.filmstrip__titre-principal { font-weight: 500; }
.filmstrip__titre-detail { font-size: 0.68rem; opacity: 0.85; display: flex; align-items: center; }
.filmstrip__titre-detail:empty { display: none; }
.filmstrip__titre-principal:empty { display: none; }
.filmstrip__texte { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.filmstrip__comptes { flex: none; display: inline-flex; align-items: center; gap: 0.7em; }
.filmstrip__comptes + .filmstrip__texte::before { content: '·'; margin: 0 0.35em; }
.filmstrip__compte { display: inline-flex; align-items: center; gap: 0.25em; }
.filmstrip__compte svg { width: 1.05em; height: 1.05em; flex: none; }


.carousel__titre-groupe {
  font-family: 'Outfit', sans-serif;
  font-size: 1.3rem;
  font-weight: 600;
  color: var(--fog);
  padding: 1.2rem 1rem 0.6rem;
  margin: 0;
}

.filmstrip {
  display: flex;
  height: 100%;
  overflow-x: auto;
  scroll-behavior: smooth;
  scrollbar-width: none;
}
.filmstrip::-webkit-scrollbar { display: none; }
.filmstrip__item {
  position: relative;
  flex: 0 0 auto;
  height: 100%;
  cursor: pointer;
  line-height: 0;
  transition: transform 0.1s ease;
  /* Chevauchement volontaire d'1px avec la photo suivante : sur mobile, la largeur
     de chaque image (auto, calculée depuis une hauteur à 100% souvent fractionnaire
     en pixels d'écran) s'arrondit indépendamment d'une image à l'autre - l'écart
     d'arrondi laisse passer un fin liseré du fond (blanc/clair) entre deux photos.
     Imperceptible sur des photos opaques, ça absorbe l'écart quel que soit son sens. */
  margin-right: -1px;
}
.filmstrip__item:active { transform: scale(0.97); }
/* Bref surlignage de la vignette visée par un clic sur un pin de la carte (voir
   carousel.js, mettreEnEvidencePhoto) - confirme visuellement laquelle a été trouvée. */
.filmstrip__item--surligne { animation: surlignage-photo 1.8s ease; }
@keyframes surlignage-photo {
  0%, 100% { outline: 3px solid transparent; outline-offset: -3px; }
  15%, 60% { outline: 3px solid var(--ember); outline-offset: -3px; }
}
.filmstrip__item img { height: 100%; width: auto; display: block; }
/* Tuile de groupe (voir carousel.js, creerTuileGroupe) : les couvertures d'un même groupe côte à
   côte, sans coupure visible entre elles, le titre (lieu, année, comptes) posé par-dessus en bas.
   La tuile épouse exactement ses photos : pas de largeur minimale, qui laissait des bandes vides de
   part et d'autre d'une photo verticale seule. Le titre se tronque alors avec "…" (texte complet en
   infobulle). */
.filmstrip__item--groupe { display: flex; overflow: hidden; }
.filmstrip__item--groupe img { height: 100%; width: auto; display: block; cursor: pointer; margin-right: -1px; }
.filmstrip__img--vide {
  height: 100%;
  width: 200px;
  background: var(--basalt-3);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  cursor: default;
}
.filmstrip__caption {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 0.5rem 0.6rem;
  background: linear-gradient(180deg, transparent, rgba(20, 24, 27, 0.88));
  color: var(--overlay-texte);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  opacity: 0;
  transition: opacity 0.15s ease;
  pointer-events: none;
}
.filmstrip__item:hover .filmstrip__caption { opacity: 1; }


.leaflet-tooltip.pin-tooltip {
  background: var(--basalt-2);
  color: var(--fog);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.6rem;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.45);
  /* Leaflet met white-space:nowrap par défaut sur .leaflet-tooltip (pensé pour de
     courtes étiquettes) — sans ça, l'espèce ou le lieu déborde du cadre dès que le
     texte est un peu long au lieu de passer à la ligne. */
  white-space: normal;
}
.leaflet-tooltip-top.pin-tooltip::before { border-top-color: var(--line); }
.leaflet-tooltip-bottom.pin-tooltip::before { border-bottom-color: var(--line); }
/* Variables posées par placerTooltipDansCarte (map.js) quand la vignette est décalée
   pour rester dans la carte : la flèche garde sa cible, l'image rétrécit si besoin. */
.leaflet-tooltip-top.pin-tooltip::before,
.leaflet-tooltip-bottom.pin-tooltip::before { transform: translateX(var(--decalage-fleche-x, 0px)); }
.leaflet-tooltip-left.pin-tooltip::before,
.leaflet-tooltip-right.pin-tooltip::before { transform: translateY(var(--decalage-fleche-y, 0px)); }
.pin-tooltip .pin-preview__img { max-height: var(--hauteur-max-vignette, none); }
.leaflet-tooltip-left.pin-tooltip::before { border-left-color: var(--line); }
.leaflet-tooltip-right.pin-tooltip::before { border-right-color: var(--line); }

.pin-preview { display: flex; flex-direction: column; gap: 0.4rem; width: 150px; }
.pin-preview__img {
  width: 100%;
  aspect-ratio: 4 / 3;
  background-size: cover;
  background-position: center;
  border-radius: 2px;
}
.pin-preview__img--vide {
  background: var(--basalt-3);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.pin-preview__espece {
  font-family: 'Outfit', sans-serif;
  font-size: 0.85rem;
  font-weight: 600;
  margin: 0;
}

/* ---------- Blog : index (cartes) et articles ---------- */
.blog-index { display: flex; flex-direction: column; gap: 2.2rem; padding: 0 2rem 4rem; max-width: 640px; }
.blog-rubrique__titre { margin: 0 0 0.8rem; font-weight: 400; }
.blog-cartes { display: flex; flex-direction: column; gap: 0.9rem; }
.blog-carte {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1.4rem 1.5rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--basalt-2);
  text-decoration: none;
  transition: border-color 0.15s ease, transform 0.1s ease;
}
.blog-carte:hover { border-color: var(--ember); }
.blog-carte:active { transform: scale(0.98); }
.blog-carte__titre {
  font-family: 'Outfit', sans-serif;
  font-weight: 600;
  font-size: 1.2rem;
  color: var(--fog);
  margin: 0;
}
.blog-carte__desc {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  color: var(--slate);
  line-height: 1.4;
  margin: 0;
}

/* Corps d'un article (HTML produit depuis le markdown, voir articles.py) - sert aussi aux
   mentions légales. Pas de max-width globale : le texte se limite lui-même pour rester
   lisible, mais photos et rangées de cartes profitent de toute la largeur disponible. */
.article-corps { padding: 0 2rem 4rem; }
.article-corps > h2 {
  font-family: 'Outfit', sans-serif;
  font-size: 1.1rem;
  font-weight: 600;
  margin: 1.4rem 0 0.5rem;
}
.article-corps > * + h2 { margin-top: 0.6rem; padding-top: 1.4rem; border-top: 1px solid var(--line); }
.article-corps h3 { font-family: 'Outfit', sans-serif; font-size: 1rem; font-weight: 600; margin: 1.2rem 0 0.4rem; }
.article-corps p,
.article-corps ul,
.article-corps ol,
.article-corps blockquote { color: var(--slate); line-height: 1.6; max-width: 70ch; }
.article-corps p { margin: 0 0 0.8rem; }
.article-corps ul,
.article-corps ol { margin: 0.6rem 0 0.8rem; padding-left: 1.2rem; }
.article-corps li { margin-bottom: 0.4rem; }
.article-corps strong { color: var(--fog); }
.article-corps p img { max-width: 100%; height: auto; }
.article-corps p a,
.article-corps li a { color: var(--ember); text-decoration: underline; text-underline-offset: 0.15em; }
.article-corps p a:hover,
.article-corps li a:hover { color: var(--fog); }
.article-corps blockquote { margin: 1rem 0; padding: 0.1rem 0 0.1rem 1rem; border-left: 2px solid var(--ember); }
.article-corps code {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.85em;
  background: var(--basalt-3);
  padding: 0.1rem 0.35rem;
  border-radius: 2px;
}
.article-corps table { border-collapse: collapse; margin: 1rem 0; color: var(--slate); font-size: 0.9rem; }
.article-corps th,
.article-corps td { text-align: left; padding: 0.45rem 0.8rem; border-bottom: 1px solid var(--line); }
.article-corps th { color: var(--fog); }
.article-corps hr { border: none; border-top: 1px solid var(--line); margin: 1.6rem 0; }

/* Blocs de code (```python ... ``` dans un article), colorés côté serveur par Pygments : les
   classes ci-dessous sont ses noms de jetons (k = mot-clé, s2 = chaîne, c1 = commentaire...).
   Un long code défile horizontalement plutôt que de passer à la ligne. */
.article-corps .code {
  position: relative;
  max-width: 1100px;
  margin: 1rem 0 1.2rem;
  background: var(--basalt-3);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.article-corps .code pre {
  margin: 0;
  padding: 0.9rem 1.1rem;
  overflow-x: auto;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.78rem;
  line-height: 1.55;
  color: var(--fog);
  tab-size: 4;
}
.article-corps .code pre code { background: none; padding: 0; font-size: inherit; }
.code .k, .code .kn, .code .kc, .code .kd, .code .kr, .code .kp, .code .ow { color: var(--ember); font-weight: 600; }
.code .nf, .code .nc, .code .fm, .code .nn { color: var(--slate); font-weight: 600; }
.code .nb, .code .bp, .code .nd { color: var(--moss); }
.code .s, .code .s1, .code .s2, .code .sa, .code .sb, .code .sc, .code .sd,
.code .se, .code .sh, .code .si, .code .sx, .code .sr, .code .ss { color: #3d7a4f; }
.code .c, .code .c1, .code .cm, .code .ch, .code .cs, .code .cp, .code .cpf { color: #7a8591; font-style: italic; }
.code .m, .code .mi, .code .mf, .code .mh, .code .mo, .code .il { color: #a0561f; }
.code .copier {
  position: absolute;
  top: 0.45rem;
  right: 0.45rem;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--slate);
  background: var(--basalt-3);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.25rem 0.5rem;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s ease, color 0.15s ease;
}
.code:hover .copier,
.code .copier:focus-visible { opacity: 1; }
.code .copier:hover { color: var(--ember); border-color: var(--ember); }
@media (hover: none) { .code .copier { opacity: 1; } } /* pas de survol sur téléphone */

/* Photos/vidéos d'un article : seules, ou en rangée à hauteur égale (chaque figure prend une
   largeur proportionnelle à son ratio, posé en style par articles.py). */
.article-medias { margin: 1.4rem 0; max-width: 1100px; }
.article-medias--rangee { display: flex; gap: 0.6rem; align-items: flex-start; }
.article-media { margin: 0; min-width: 0; }
.article-media img,
.article-media video { display: block; width: 100%; height: auto; border-radius: var(--radius); background: var(--basalt-2); }
/* Seule : jamais plus haute que l'écran (un portrait pleine largeur serait immense) - largeur
   et hauteur "auto" gardent le ratio sous les deux plafonds. */
.article-medias:not(.article-medias--rangee) img,
.article-medias:not(.article-medias--rangee) video { width: auto; max-width: 100%; max-height: 80vh; }
.article-media img[data-media] { cursor: zoom-in; }
.article-media figcaption {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem;
  line-height: 1.4;
  color: var(--slate);
  margin-top: 0.45rem;
  max-width: 70ch;
}
/* Référence site:... introuvable, ambiguë ou masquée (voir app.py, resoudre_medias_site) :
   visible exprès, pour la corriger à la relecture. */
.article-erreur {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.75rem;
  color: var(--fog);
  border: 1px dashed var(--ember);
  border-radius: var(--radius);
  padding: 0.7rem 0.9rem;
  margin: 0;
}
@media (max-width: 600px) {
  /* Sur téléphone, une rangée de 3 photos donnerait des vignettes minuscules : on empile. */
  .article-medias--rangee { flex-direction: column; }
  .article-medias--rangee .article-media { flex: none !important; width: 100%; }
}

/* Rangée de cartes de liens (HTML brut dans un article, voir blog/astrophoto.md) : pas de
   max-width, chaque carte grandit pour occuper l'espace disponible. */
.article-liens { display: flex; flex-wrap: wrap; gap: 0.9rem; margin-top: 0.8rem; }
.article-lien {
  flex: 1 1 220px;
  display: flex;
  align-items: center;
  gap: 0.8rem;
  padding: 0.9rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--basalt-2);
  text-decoration: none;
  transition: border-color 0.15s ease;
}
.article-lien:hover { border-color: var(--ember); }
/* Miniature optionnelle (<img class="article-lien__vignette">) - sans elle, pas de case vide. */
.article-lien__vignette {
  flex: 0 0 44px;
  width: 44px;
  height: 44px;
  border-radius: 4px;
  object-fit: cover;
}
.article-lien__texte { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }
.article-lien__titre {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  color: var(--fog);
}
.article-lien__desc {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.66rem;
  color: var(--slate);
  line-height: 1.4;
}

footer {
  padding: 2rem;
  color: var(--slate);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  border-top: 1px solid var(--line);
}

/* ---------- Lightbox (agrandissement photo) ---------- */
.lightbox {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 2000;
  background: rgba(20, 24, 27, 0.96);
  align-items: center;
  justify-content: center;
  padding: 2rem;
}
.lightbox.open { display: flex; }

.lightbox__viewport {
  position: relative; /* ancre .lightbox__demarrer-video, centré sur la photo affichée */
  max-width: 100%;
  max-height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* max-height:100% seul ne suffit pas ici : .lightbox__viewport n'a pas de hauteur
   explicite (hauteur "auto" en flexbox), donc un pourcentage ne s'y résout pas et
   une photo en portrait pouvait dépasser verticalement l'écran. On borne donc
   directement en unités de viewport, en tenant compte du padding de .lightbox (2rem). */
.lightbox__viewport img {
  max-width: calc(100vw - 4rem - var(--reserve-gauche, 0px) - var(--reserve-droite, 0px));
  max-height: calc(100vh - 4rem);
  object-fit: contain;
  border-radius: var(--radius);
  cursor: zoom-in;
  -webkit-user-drag: none;
  user-select: none;
}
.filmstrip__item img {
  -webkit-user-drag: none;
  user-select: none;
}
#lightbox-video {
  display: none;
  max-width: calc(100vw - 4rem - var(--reserve-gauche, 0px) - var(--reserve-droite, 0px));
  max-height: calc(100vh - 4rem);
  border-radius: var(--radius);
}
.lightbox--video #lightbox-img { display: none; }
.lightbox--video #lightbox-video { display: block; }

/* Mode zoomé (au-delà de l'ajustement à l'écran, voir lightbox.js/facteurZoom) : le
   conteneur devient défilable, la largeur explicite posée en JS (appliquerZoom)
   l'emporte sur width:auto ci-dessous (spécificité d'un style en ligne). */
.lightbox.zoomed .lightbox__viewport {
  display: block;
  width: 100%;
  height: 100%;
  overflow: auto;
  cursor: zoom-out;
  scrollbar-width: none;
}
.lightbox.zoomed .lightbox__viewport::-webkit-scrollbar { display: none; }
.lightbox.zoomed .lightbox__viewport img {
  max-width: none;
  max-height: none;
  width: auto;
  height: auto;
  cursor: zoom-out;
  border-radius: 0;
}

.lightbox__close {
  position: absolute;
  top: 1.2rem;
  background: rgba(20, 24, 27, 0.85);
  border: 1px solid var(--line);
  color: var(--overlay-texte);
  border-radius: var(--radius);
  cursor: pointer;
  z-index: 2001;
  right: 1.5rem;
  font-size: 1.6rem;
  line-height: 1;
  padding: 0.3rem 0.8rem;
  border: none;
  background: none;
  transition: transform 0.1s ease;
}
.lightbox__close:hover { color: var(--ember); }
.lightbox__close:active { transform: scale(0.92); }

/* Contrôles de zoom (voir lightbox.js, zoomerVers) : deux boutons +/- et le niveau
   actuel ("100%" = taille réelle des pixels, jamais dépassé - voir facteurZoomMaxCourant).
   Masqué pour une vidéo (voir estVideo), comme l'était l'ancien bouton unique. */
.lightbox__zoom-controles {
  position: absolute;
  top: 1.2rem;
  right: 4.5rem;
  z-index: 2001;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  background: rgba(20, 24, 27, 0.85);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.3rem;
}
.lightbox__zoom-bouton {
  background: none;
  border: none;
  color: var(--overlay-texte);
  cursor: pointer;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 1rem;
  line-height: 1;
  width: 1.8rem;
  height: 1.8rem;
  border-radius: calc(var(--radius) - 2px);
  transition: transform 0.1s ease, background 0.15s ease, color 0.15s ease;
}
.lightbox__zoom-bouton:hover:not(:disabled) { background: rgba(255, 255, 255, 0.12); color: var(--ember); }
.lightbox__zoom-bouton:active:not(:disabled) { transform: scale(0.9); }
.lightbox__zoom-bouton:disabled { opacity: 0.35; cursor: default; }
/* Bouton dédié, à côté du stepper -/+ : cliquer la photo saute aussi au 1:1, mais ce
   raccourci n'est pas devinable — ce bouton rend l'action explicite. */
.lightbox__zoom-1-1 { width: auto; padding: 0 0.45rem; font-size: 0.68rem; letter-spacing: 0.02em; }
.lightbox__zoom-1-1[aria-pressed="true"] { background: rgba(255, 255, 255, 0.12); color: var(--ember); }
.lightbox__zoom-separateur { width: 1px; height: 1.2rem; background: var(--line); }
.lightbox__zoom-niveau {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.7rem;
  letter-spacing: 0.03em;
  color: var(--overlay-texte);
  min-width: 2.6rem;
  text-align: center;
}
.lightbox--video .lightbox__zoom-controles { display: none; }

.lightbox__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2001;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: rgba(20, 24, 27, 0.85);
  border: 1px solid var(--line);
  color: var(--overlay-texte);
  font-size: 1.3rem;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, transform 0.1s ease;
}
.lightbox__nav:hover { border-color: var(--ember); color: var(--ember); }
.lightbox__nav:active { transform: translateY(-50%) scale(0.9); }
.lightbox__prev { left: 1.2rem; }
.lightbox__next { right: 1.2rem; }
.lightbox.zoomed .lightbox__nav { display: none; }

/* Miniatures des photos précédentes/suivantes, de part et d'autre de la photo
   affichée (voir lightbox.js, mettreAJourVignettes) - un aperçu de ce qui vient,
   pas juste des flèches aveugles. Un groupe par côté (jusqu'à VIGNETTES_MAX
   miniatures, voir lightbox.js) plutôt qu'une seule : quand la photo affichée est
   étroite (portrait), l'espace libéré de chaque côté sert à en montrer plusieurs.
   Largeur réservée (--reserve-gauche/droite) posée en JS (pas en CSS) car son nombre
   de miniatures varie d'une photo à l'autre - un simple :has(.visible) ne suffirait
   plus à en déduire la largeur. flex:0 0 auto sur le groupe : ne rétrécit jamais au
   profit de .lightbox__viewport, qui absorbe déjà tout l'espace restant. */
.lightbox__vignettes {
  display: none;
  flex: 0 0 auto;
  gap: 0.6rem;
}
/* Un peu d'air entre les aperçus et la photo affichée, plutôt que collés contre elle. */
/* Côté extérieur : 2.8rem de plus, pour que le groupe ne passe pas sous la flèche de navigation. */
.lightbox__vignettes--prev { margin: 0 1.8rem 0 2.8rem; }
.lightbox__vignettes--next { margin: 0 2.8rem 0 1.8rem; }
.lightbox__vignette {
  display: none;
  flex: 0 0 auto;
  width: 90px;
  height: 90px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  cursor: default;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}
/* Réservée (espace occupé) dès que la photo/vidéo est naviguable, visible (contenu
   affiché) seulement si un média distinct existe à cette position précise - voir
   mettreAJourVignettes. Les deux états sont distincts : une miniature peut occuper
   sa place sans rien montrer (ex: une galerie de 2 médias, où un seul est distinct),
   pour garder la photo affichée centrée plutôt que décalée vers le seul côté rempli. */
.lightbox__vignette.reservee { display: block; }
.lightbox__vignette.visible { opacity: 0.55; cursor: pointer; pointer-events: auto; }
.lightbox__vignette img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lightbox__vignette.visible:hover { opacity: 1; border-color: var(--ember); }
.lightbox__vignette.visible:active { transform: scale(0.94); }
.lightbox.zoomed .lightbox__vignettes { display: none; } /* pas de distraction en zoom 1:1 */
@media (max-width: 900px) {
  .lightbox__vignettes { display: none !important; } /* pas la place, priorité à la photo */
}

.lightbox__meta {
  display: none;
  position: absolute;
  left: 50%;
  bottom: 1.2rem;
  transform: translateX(-50%);
  max-width: min(90vw, 640px);
  background: rgba(20, 24, 27, 0.85);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.5rem 0.9rem;
  text-align: center;
  z-index: 2001;
  pointer-events: none;
}
.lightbox__meta.visible { display: block; }
.lightbox__meta-toggle {
  background: none;
  border: none;
  padding: 0;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: var(--overlay-texte-attenue);
  text-decoration: underline;
  text-decoration-color: var(--line);
  cursor: pointer;
  pointer-events: auto; /* le panneau est pointer-events:none par défaut */
  display: block;
  margin: 0 auto;
}
.lightbox__meta-toggle:hover { color: var(--ember); text-decoration-color: var(--ember); }
/* Repliée par défaut (voir metaOuverte dans lightbox.js) : seul le bouton ci-dessus
   est visible tant qu'on n'a pas demandé le détail, pour ne pas recouvrir la photo. */
.lightbox__meta-corps { display: none; margin-top: 0.6rem; }
.lightbox__meta.ouverte .lightbox__meta-corps { display: block; }
.lightbox__meta-espece {
  font-family: 'Outfit', sans-serif;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--overlay-texte);
  margin: 0;
}
.lightbox__meta-sci {
  font-family: 'Karla', sans-serif;
  font-style: italic;
  font-weight: 400;
  color: var(--overlay-texte-attenue);
  margin-left: 0.4em;
}
.lightbox__meta-grille {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 0.8rem;
  row-gap: 0.3rem;
  margin: 0.5rem 0 0;
  text-align: left;
}
.lightbox__meta-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.6rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--overlay-texte-attenue);
  opacity: 0.65;
  white-space: nowrap;
  align-self: baseline;
}
.lightbox__meta-valeur {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  letter-spacing: 0.03em;
  color: var(--overlay-texte-attenue);
  align-self: baseline;
}
.lightbox__meta-legende {
  font-size: 0.85rem;
  font-style: italic;
  color: var(--overlay-texte);
  margin: 0.45rem 0 0;
}
.lightbox__meta-copyright {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.62rem;
  letter-spacing: 0.03em;
  color: var(--overlay-texte-attenue);
  opacity: 0.6;
  margin: 0.5rem 0 0;
}
.lightbox__meta-lien {
  /* Réinitialise le chrome par défaut des <button> (le pays est un bouton, pas un
     lien, pour déclencher de la navigation JS plutôt qu'une vraie URL) — sans effet
     sur les <a>, qui n'ont pas ces propriétés par défaut. */
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  cursor: pointer;
  color: var(--overlay-texte);
  text-decoration: underline;
  text-decoration-color: var(--line);
  pointer-events: auto; /* le panneau est pointer-events:none, sauf ce lien */
}
.lightbox__meta-lien:hover { color: var(--ember); text-decoration-color: var(--ember); }
/* Vidéo : le lecteur natif a déjà ses propres contrôles en bas, on évite le chevauchement. */
.lightbox--video .lightbox__meta { bottom: 4.5rem; }

/* Sur téléphone, la grille label/valeur ("auto 1fr") écrase la colonne de valeur et
   fait passer les champs longs (lieu, objectif) sur 3 lignes, gonflant le panneau
   jusqu'à recouvrir presque toute la photo. On empile label au-dessus de valeur
   (pleine largeur, moins de retours à la ligne) et on plafonne la hauteur avec un
   défilement interne plutôt que de laisser le panneau grandir sans limite. */
@media (max-width: 900px) {
  .lightbox__meta { max-width: calc(100vw - 2rem); }
  .lightbox__meta-corps {
    max-height: 32vh;
    overflow-y: auto;
    pointer-events: auto; /* nécessaire pour pouvoir défiler le corps au doigt */
    -webkit-overflow-scrolling: touch;
  }
  .lightbox__meta-grille {
    grid-template-columns: 1fr;
    row-gap: 0.1rem;
  }
  .lightbox__meta-label { margin-top: 0.3rem; }
  .lightbox__meta-espece { font-size: 0.95rem; }
  .lightbox__meta-label, .lightbox__meta-valeur { font-size: 0.66rem; }
}

/* Vidéo avec photo associée (voir lightbox.js, demarrerVideoAssociee) : affiche
   d'abord la photo comme une photo classique, avec ce bouton par-dessus plutôt que de
   démarrer la vidéo automatiquement — laisse le temps de l'apprécier avant de lancer
   la lecture. */
.lightbox__demarrer-video {
  display: none;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2001;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  background: none;
  border: none;
  color: var(--overlay-texte);
  cursor: pointer;
}
.lightbox__demarrer-video.visible { display: flex; }
.lightbox__demarrer-video-icone {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background: rgba(20, 24, 27, 0.72);
  border: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.6rem;
  padding-left: 4px; /* recentre visuellement le triangle de lecture */
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease, transform 0.1s ease;
}
.lightbox__demarrer-video:active .lightbox__demarrer-video-icone { transform: scale(0.9); }
.lightbox__demarrer-video-texte {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: rgba(20, 24, 27, 0.72);
  padding: 0.35rem 0.8rem;
  border-radius: var(--radius);
}
.lightbox__demarrer-video:hover .lightbox__demarrer-video-icone,
.lightbox__demarrer-video:hover .lightbox__demarrer-video-texte {
  border-color: var(--ember); color: var(--ember);
}

/* ---------- Badge vidéo sur les vignettes ---------- */
.play-badge {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: rgba(20, 24, 27, 0.72);
  color: var(--overlay-texte);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
  padding-left: 3px; /* recentre visuellement le triangle de lecture */
}
.pin-preview__img { position: relative; }
.pin-preview .play-badge { width: 32px; height: 32px; font-size: 0.8rem; }

/* Nombre de photos masquées derrière une couverture (voir carousel.js,
   ouvrirPhotoOuGroupe) - sans lui, rien ne laisse deviner qu'il y en a plus. */
.groupe-badge {
  position: absolute;
  bottom: 0.4rem;
  right: 0.4rem;
  background: rgba(20, 24, 27, 0.78);
  color: var(--overlay-texte);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem;
  font-weight: 600;
  padding: 0.15rem 0.45rem;
  border-radius: 999px;
}

/* ---------- Arbre de matériel ---------- */
.arbre-controles {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2rem;
  padding: 0 2rem 2rem;
}
.arbre-categories { display: flex; flex-wrap: wrap; gap: 1rem; }
.arbre-categorie {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  cursor: pointer;
}
.arbre-categorie input { accent-color: var(--ember); }

/* Récapitulatif en haut de page (boîtier choisi, objectif monté en cliquant
   une carte, masse/encombrement totaux). En dessous, la grille : une
   ligne par spécialité, une colonne par gamme d'objectif
   (entrée/intermédiaire/pro). */
.setup {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 1.5rem;
  padding: 0 2rem 2rem;
}
.setup__infos {
  flex: 1 1 300px;
  min-width: 260px;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.setup__infos select {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.78rem;
  background: var(--basalt-2);
  color: var(--fog);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.35rem 0.6rem;
  margin-left: 0.5rem;
}
#setup-details { display: flex; flex-direction: column; gap: 1rem; }
.setup__bloc { display: flex; align-items: flex-start; gap: 0.9rem; }
.setup__bloc p { margin: 0; }
.setup__mini { flex: 0 0 96px; }
.setup__mini .noeud__vignette { margin-bottom: 0; }
.setup__nom {
  font-family: 'Outfit', sans-serif;
  font-size: 1.05rem;
  font-weight: 600;
  margin-top: 0.2rem;
}
.setup__specs {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.7rem;
  color: var(--slate);
  line-height: 1.5;
  margin-top: 0.15rem;
}
.setup__totaux {
  border-top: 1px solid var(--line);
  padding-top: 0.9rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.setup__total-ligne {
  margin: 0;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.85rem;
  color: var(--fog);
  display: flex;
  justify-content: space-between;
  gap: 1rem;
}

.arbre-scroll { overflow-x: auto; padding: 0 2rem 3rem; }
.arbre-grille {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  min-width: fit-content;
}
/* Nombre de colonnes défini en style inline par arbre.js (dépend du nombre
   d'objectifs de chaque catégorie, pas forcément 3). */
.arbre-grille__ligne {
  display: grid;
  gap: 0.9rem;
}
.arbre-grille__cell { display: flex; }
.arbre-grille__entete {
  align-items: center;
  justify-content: center;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--slate);
  border-bottom: 1px solid var(--line);
  padding-bottom: 0.5rem;
}
.arbre-grille__categorie {
  align-items: center;
  font-family: 'Outfit', sans-serif;
  font-size: 0.98rem;
  font-weight: 600;
  color: var(--fog);
  background: var(--moss-dim);
  border: 1px solid var(--moss);
  border-radius: var(--radius);
  padding: 0.6rem 0.9rem;
  text-decoration: none;
  transition: border-color 0.15s ease;
}
.arbre-grille__categorie:hover { border-color: var(--ember); }

/* ---------- Comparatif détaillé (boîtiers entre eux, ou gammes d'une
   spécialité entre elles) — même grille label + colonnes que l'arbre. ---------- */
.comparatif-scroll { overflow-x: auto; padding: 0 2rem 3rem; }
.comparatif-grille {
  display: grid;
  gap: 0.7rem;
  width: 100%;
  align-items: stretch;
}
.cmp-cell {
  border-top: 1px solid var(--line);
  padding: 0.9rem 0;
  display: flex;
  align-items: center;
  min-width: 0; /* laisse le texte des specs se compresser/wrapper plutôt que forcer la grille à dépasser la largeur de la page */
  overflow-wrap: break-word;
}
.cmp-cell--label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--slate);
}
.cmp-cell--coin { border-top: none; }
.cmp-cell--item {
  flex-direction: column;
  align-items: stretch;
  border-top: none;
  padding-top: 0;
}
.cmp-item__nom {
  font-family: 'Outfit', sans-serif;
  font-size: 0.98rem;
  font-weight: 600;
  margin: 0 0 0.2rem;
}
.cmp-cell--section {
  grid-column: 1 / -1;
  border-top: none;
  padding: 1.6rem 0 0.3rem;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ember);
}
.noeud {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--basalt-2);
  transition: opacity 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}
.noeud--niveau {
  width: 100%;
  padding: 0.9rem 1rem;
  text-align: left;
  cursor: pointer;
}
.noeud--niveau:hover { border-color: var(--ember); }
.noeud--niveau:active { transform: scale(0.98); }
.noeud--monte {
  border-color: var(--ember);
  box-shadow: 0 0 0 1px var(--ember);
}
.noeud__badge {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  z-index: 1;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--basalt);
  background: var(--ember);
  border-radius: 2px;
  padding: 0.15rem 0.4rem;
}

/* Cadre à ratio fixe (4:3) : exporte tes images à cette proportion (ex.
   480×360px) pour qu'elles remplissent le cadre pile, sans recadrage — sinon
   object-fit: contain les affiche entières, quitte à laisser une marge. */
.noeud__vignette {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  background: var(--basalt-3);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  margin-bottom: 0.7rem;
}
.noeud__vignette img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.noeud__vignette-icone {
  display: flex;
  width: 28px;
  height: 28px;
  color: var(--slate);
  opacity: 0.4;
}
.noeud__vignette-icone svg { width: 100%; height: 100%; }
.noeud__nom {
  font-family: 'Outfit', sans-serif;
  font-size: 0.92rem;
  font-weight: 600;
  margin: 0 0 0.3rem;
}
.noeud__specs {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem;
  color: var(--slate);
  line-height: 1.4;
  margin: 0 0 0.4rem;
}
.noeud__description {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem;
  color: var(--slate);
  line-height: 1.4;
  margin: 0;
}

/* ---------- Pied de page ---------- */
/* Discret à dessein : un simple lien vers les mentions légales, en petit, en bas de
   chaque page — suffisant pour l'obligation réglementaire sans alourdir le design. */
.pied-de-page {
  padding: 1.2rem 2rem 2rem;
  text-align: center;
}
.pied-de-page a {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--slate);
  opacity: 0.6;
  text-decoration: none;
}
.pied-de-page a:hover { opacity: 1; color: var(--ember); }

/* ---------- Page 404 ---------- */
.page-404 {
  min-height: 50vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.page-404__lien {
  display: inline-block;
  margin-top: 0.6rem;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.78rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ember);
  text-decoration: none;
}
.page-404__lien:hover { text-decoration: underline; }
