/* ═══════════════════════════════════════════════════════════════════
   CARD GALLERY — carrousel de cartes en profondeur (lightbox)
   ───────────────────────────────────────────────────────────────────
   Voir HISTORIQUE.md § Chantier Galerie. Deux règles tiennent tout ce fichier :

   1. ANIMER `transform` ET `opacity`, RIEN D'AUTRE. La galerie s'affiche
      au-dessus d'une scène Three.js à 60 fps : ces deux propriétés sont
      composées par le GPU sans relancer la mise en page. Animer `width`,
      `top` ou une ombre portée ferait tomber le tout.
   2. Tokens de design-tokens.js — pas de couleur ni de taille en dur.
   ═══════════════════════════════════════════════════════════════════ */

.cg-root {
  position: fixed;
  inset: 0;
  z-index: 4000;                 /* au-dessus de #ui, sous les cartes (5000+) */
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  visibility: hidden;
  /* Le VOILE a sa propre durée de sortie (--cg-veil-out), plus courte que le
     build-out des cartes : à la fermeture, la caméra part vers l'entité et ce
     vol doit être VISIBLE. Un voile qui s'attarde 1,2 s le cacherait presque
     entièrement, et on croirait à une téléportation. Les cartes, elles,
     continuent leur build-out par-dessus. */
  transition: opacity var(--cg-veil-out, 420ms) var(--cg-ease, ease),
              visibility 0s linear var(--cg-veil-out, 420ms);
}
.cg-root.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--cg-build, 620ms) var(--cg-ease, ease),
              visibility 0s;
}

/* ── Le voile ──────────────────────────────────────────────────────
   PARTIEL, volontairement : le terrain reste lisible dessous. On ne quitte
   pas le monde, il passe au second plan — c'est ce qui distingue ce geste
   d'une modale d'application. */
.cg-veil {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at center,
      color-mix(in srgb, var(--black) 72%, transparent) 0%,
      color-mix(in srgb, var(--black) 93%, transparent) 70%);
  backdrop-filter: blur(7px) saturate(.72);
  -webkit-backdrop-filter: blur(7px) saturate(.72);
}

/* ── La scène ──────────────────────────────────────────────────────
   `perspective` donne sa profondeur à la pile : les cartes reculées en Z
   rapetissent réellement au lieu d'être juste mises à l'échelle. */
.cg-stage {
  position: absolute;
  inset: 0;
  perspective: 1800px;
  perspective-origin: 50% 50%;
  transform-style: preserve-3d;
  pointer-events: none;          /* les cartes réactivent le leur */
}

/* ── Une carte de la pile ──────────────────────────────────────────
   Position centrale fixe ; c'est le `transform` qui la place (JS). */
.cg-card {
  position: absolute;
  left: 50%;
  top: 50%;
  /* ⚠️ Hauteur DÉRIVÉE de la largeur — cf. .floating-card (main.css) : posée en
     px par le JS, elle ne suivait pas le redimensionnement de la fenêtre. */
  aspect-ratio: 618 / 1017;
  transform-origin: 50% 50%;
  will-change: transform, filter;
  cursor: pointer;
  /* Valeur de repos explicite : une transition ne peut pas partir d'une
     propriété jamais déclarée. Sans ce `translate: 0`, le build-in n'aurait
     pas d'état initial à interpoler. */
  translate: 0 0;
  transition:
    transform var(--cg-slide, 520ms) var(--cg-ease, ease),
    translate var(--cg-slide, 520ms) var(--cg-ease, ease),
    filter    var(--cg-slide, 520ms) var(--cg-ease, ease),
    opacity   var(--cg-slide, 520ms) var(--cg-ease, ease);
}
.cg-card.is-active { cursor: default; }

/* Les cartes latérales s'assombrissent — la profondeur ne suffit pas à
   hiérarchiser, l'œil suit la luminosité. L'intensité vient du JS (--cg-dark),
   qui la calcule par rang : plus la carte est loin, plus elle s'enfonce dans
   le noir. Le voile est OPAQUE sur une carte opaque — pas de transparence qui
   laisserait voir les cartes du dessous. */
/* ⚠️ `z-index` INDISPENSABLE : les couches de la carte sont empilées
   (.cf-overlay 0, .cf-window 2 — main.css §CHÂSSIS). Sans z-index, ce voile
   passe SOUS la fenêtre et n'assombrit que le cadre, pas l'illustration ni le
   texte : on croit alors que le voile est sur le mauvais conteneur.
   Le rayon suit celui de la carte (18px, comme --cs-radius) — un rayon plus petit
   laisserait les angles du voile dépasser de l'ornement. */
.cg-card::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 5;
  background: var(--black);
  opacity: var(--cg-dark, 0);
  transition: opacity var(--cg-slide, 520ms) var(--cg-ease, ease);
  pointer-events: none;
  border-radius: 18px;
}
.cg-card.is-active::after { opacity: 0; }

/* ── BUILD-IN / BUILD-OUT ──────────────────────────────────────────
   Fade + léger déplacement vertical. Le `translateY` s'ajoute au transform
   posé par le JS via une variable, pour ne pas l'écraser. */
.cg-card.cg-in,
.cg-card.cg-out {
  opacity: 0 !important;
}
/* ⚠️ `translate` DOIT figurer ici. Le décalage vertical du build passe par la
   propriété `translate:` (et non par `transform`, réservé au placement inline
   du JS) : absente de cette liste, elle n'était pas animée — le déplacement
   sautait à sa valeur finale pendant que le fondu, lui, se déroulait. D'où
   l'impression que le mouvement se termine avant la fin du fade. */
.cg-card.cg-in,
.cg-card.cg-out {
  transition:
    transform var(--cg-build, 620ms) var(--cg-ease, ease),
    translate var(--cg-build, 620ms) var(--cg-ease, ease),
    filter    var(--cg-build, 620ms) var(--cg-ease, ease),
    opacity   var(--cg-build, 620ms) var(--cg-ease, ease);
}
/* Le décalage vertical du build est appliqué en surcouche du placement.
   `translate(-50%,-50%)` reste porté par le style inline du JS. */
.cg-card.cg-in  { translate: 0 var(--cg-build-rise, 100px); }
.cg-card.cg-out { translate: 0 calc(var(--cg-build-rise, 100px) * -0.6); }

/* ── Le chrome des cartes : MASQUÉ dans la pile ────────────────────
   Les 4 boutons ronds et la barre de dots sont pensés pour une carte à
   pleine taille. Réduits, ils ne sont que du bruit — et les latérales n'ont
   de toute façon aucune action propre. */
.cg-card .cf-close,
.cg-card .cf-locate,
.cg-card .cs-dots,
.cg-card .cs-arrow { display: none !important; }

/* ── Fermer ────────────────────────────────────────────────────────── */
.cg-close {
  position: absolute;
  top: 26px;
  right: 30px;
  width: 42px;
  height: 42px;
  background: none;
  border: none;
  cursor: pointer;
  z-index: 10;
  opacity: .78;
  transition: opacity .2s ease, transform .2s ease;
}
.cg-close:hover { opacity: 1; transform: scale(1.08); }
.cg-close span,
.cg-close span::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 26px;
  height: 1.5px;
  background: var(--gold-light);
  transform: translate(-50%, -50%) rotate(45deg);
}
.cg-close span::before { transform: translate(-50%, -50%) rotate(-90deg); }

/* ── Flèches ───────────────────────────────────────────────────────── */
.cg-arrow {
  /* ⚠️ UNE SEULE MARGE pour les deux flèches (04/10/2026) : la droite était décalée de 38 px vers
     l'intérieur pour dégager la bande alphabet (gallery-nav.css, right:18px + 20px de large — sans
     ce dégagement la flèche se superpose aux lettres), la gauche non : les deux marges différaient
     (50 px / 88 px à 1258 px de large). Les deux prennent maintenant la valeur qui dégage les
     lettres : symétriques à toutes les largeurs. */
  --cg-arrow-inset: max(3.9rem, calc(4vw + 2.4rem));   /* = l'ancienne flèche droite (62px / 4vw+38px), en relatif */
  position: absolute;
  top: 50%;
  width: 44px;
  height: 44px;
  transform: translateY(-50%);
  background: none;
  border: none;
  cursor: pointer;
  z-index: 10;
  opacity: .6;
  transition: opacity .2s ease;
}
.cg-arrow::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 15px;
  height: 15px;
  border-right: 1.5px solid var(--gold-light);
  border-bottom: 1.5px solid var(--gold-light);
}
.cg-prev { left:  var(--cg-arrow-inset); }
.cg-next { right: var(--cg-arrow-inset); }
.cg-prev::before { transform: translate(-40%, -50%) rotate(135deg); }
.cg-next::before { transform: translate(-60%, -50%) rotate(-45deg); }
.cg-arrow:hover:not(:disabled) { opacity: 1; }
.cg-arrow:disabled { opacity: .16; cursor: default; }

/* ── Confort ───────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .cg-card,
  .cg-card::after,
  .cg-root { transition-duration: 1ms !important; }
}

/* ── « VOIR PLUS » — dans l'encoche basse ─────────────────────────
   Du TEXTE, pas un bouton : le clic sur la carte fait déjà le geste (le
   délégué de la galerie sort sur l'entité active). Un vrai <button> aurait
   ajouté une cible de tabulation par carte — 5 dans la pile, 4 invisibles.
   POSÉ SUR .cg-card, pas dans .cf-window : l'encoche est découpée par le
   clip-path du masque, un élément placé dans la fenêtre y serait rogné.
   Calé sur la pastille DESSINÉE dans l'ornement, relevée au pixel dans
   carte_cadre_character_shadow.png (la seule où elle est rouge, donc isolable
   sans ambiguïté) : x 227..395, y 957..991 sur le PNG réel, 620×1020.
   ⚠️ PAS les valeurs de .cs-dots (main.css) : elles disent « y 956..990 sur
   618×1017 » et calaient le texte 1,5px trop bas — mauvais repère ET un pixel
   d'écart sur l'origine.
   ⚠️ z-index 3 : AU-DESSUS de la fenêtre (2), mais SOUS le voile des cartes
   latérales (.cg-card::after, 5). Au-dessus du voile, le label resterait clair
   sur une carte enfoncée dans le noir.
   Tout en %, comme le reste du châssis : le label suit la taille de la carte. */
.cg-more {
  position: absolute;
  /* 991 → bas : (1020-992)/1020. 35px de haut : 35/1020. 169 de large : 169/620. */
  bottom: 2.75%;
  left: 50%;
  height: 3.43%;
  width: 27.26%;
  /* ⚠️ -0.05em EN PLUS du centrage flex. Le flex centre la BOÎTE DE LIGNE, pas
     l'encre : « VOIR PLUS » n'a aucune descendante, mais la ligne en réserve la
     place, donc les capitales tombent d'une demi-descendante sous le centre.
     0.05em = la moitié de la descendante mesurée (0.1em). Le -50% en X reste
     dans la même déclaration — un second `transform` l'écraserait. */
  transform: translate(-50%, -0.05em);
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--white);
  font-family: var(--font-alt);
  font-size: var(--t1);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-2);
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;
  /* ⚠️ pointer-events AUTO alors que le label ne gère aucun clic : c'est ce qui
     lui permet de porter son `cursor: pointer`, l'affordance qui dit que la
     carte est cliquable. Le clic continue de remonter au délégué de .cg-root.
     Le curseur suffit à la justifier. */
  pointer-events: auto;
  cursor: pointer;
}
/* Cartes d'alignement `light` : leur ornement est clair, le label blanc s'y
   perdait. Encre sombre à la place.
   ⚠️ `:has()` — l'attribut data-alignment est porté par .cp-frame, qui est le
   FRÈRE de .cg-more, pas son parent (le label est posé sur la carte, hors de
   la fenêtre). Pas de descendance à remonter : on interroge la carte. */
.cg-card:has(.cp-frame[data-alignment="light"]) .cg-more { color: var(--grey3); }

/* ═══════════════════════════════════════════════════════════════════
   CARTE DE VOYAGE — format horizontal (cas particulier du carrousel Voyages)
   ───────────────────────────────────────────────────────────────────
   Mêmes tokens, mêmes mécaniques que les autres cartes ; seuls changent le CADRE (horizontal,
   data/raw/cards/carte_cadre_voyage.png, 1020×621) et la mise en page de la couverture.
   Rendu : card-render.js `renderJourneyCover`. Tailles : card-frame.js (JOURNEY_FRAME, CARD_SIZES.GALLERY_WIDE).
   ═══════════════════════════════════════════════════════════════════ */

/* Le ratio du cadre horizontal. Le rayon du VOILE de profondeur suit celui de la carte
   (1,86 % de sa largeur, mesuré sur le PNG). */
.cg-card--wide { aspect-ratio: 1020 / 621; }
.cg-card--wide::after { border-radius: 1.86% / 3.05%; }
/* La carte de voyage ACTIVE se clique en entier (elle lance le voyage) : la main, y compris sur la carte
   active, où les autres types gardent la flèche (`.cg-card.is-active { cursor: default }`). */
.cg-card--wide.is-active { cursor: pointer; }

/* Ornement : le PNG horizontal à la place de carte_cadre.png — MÊME principe (multiplié sur la
   couleur d'accent). `?v=` : cache-bust, le fichier garde son nom d'une version à l'autre. */
.cp-frame[data-card-type="journey"] { border-radius: 1.86% / 3.05%; }
.cp-frame[data-card-type="journey"] .cf-overlay {
  background-image: url('../../data/raw/cards/carte_cadre_voyage.png?v=1');
}

/* La FENÊTRE. Pas de masque SVG (celui des cartes verticales est dessiné pour 618×1017) : elle est
   découpée par le centre TRANSPARENT du PNG — le calque du cadre en masque, composé en `exclude` avec
   un aplat plein, ce qui ne laisse voir la fenêtre que là où le cadre est transparent. */
.cp-frame[data-card-type="journey"] .cf-window {
  clip-path: none;
  transform: none;
  contain: none;
  background: none;
  -webkit-mask-image: url('../../data/raw/cards/carte_cadre_voyage.png?v=1'), linear-gradient(var(--black), var(--black));
  mask-image: url('../../data/raw/cards/carte_cadre_voyage.png?v=1'), linear-gradient(var(--black), var(--black));
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}

/* La couverture : plein cadre sur l'illustration (recadrée au centre : les 7 images actuelles sont
   verticales, des versions paysage les remplaceront sans toucher au code). */
.jc {
  position: absolute;
  /* Marges de la FENÊTRE du cadre, réduites de MOITIÉ : la fenêtre mesure 8,70 % en haut, 9,18 % en bas,
     4,71 % de chaque côté (carte_cadre_voyage.png, 1020×621, fenêtre de 924×510 px). On ne prend que la
     moitié ; le reste se règle directement sur les éléments (padding du titre, du bouton, des chiffres). */
  inset: 4.35% 2.355% 4.59%;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  text-align: center;
}
/* Voile bas pour la lisibilité — même dégradé que la légende des cartes (.cs-illus-caption,
   main.css), sur la moitié basse. */
.jc::before {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 62%;
  background: linear-gradient(to top,
    var(--black) 0%,
    color-mix(in srgb, var(--black) 78%, transparent) 45%,
    transparent 100%);
}
.jc-body {
  position: relative;
  width: 100%;
  /* Dans la fenêtre : marges = celles de .cs-screen (gutter), et le bas respire comme le pied des
     autres cartes. */
  padding: 0 calc(var(--cs-gutter) * 2) calc(var(--cs-gap) * 2.6);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--cs-gap) * 2.4);   /* entre titre, bouton et chiffres */
}
/* Titre : le titre « héros » des autres cartes (--t6, serif, ombre de lisibilité). La base de texte est
   celle des autres cartes (voir card-gallery.js) : les tokens y valent les mêmes tailles. */
.jc-title {
  margin: 0;
  max-width: 78%;
  font-family: var(--font);
  font-size: var(--t6);
  line-height: 0.95;
  color: var(--white);
  text-align: center;
  text-shadow: var(--shadow-title);
}
/* Bouton : affordance du clic sur la carte (pas de listener, voir card-render.js). Le vert du projet
   (--cat-vegetation, la couleur de catégorie des forêts), texte --black. */
.jc-cta {
  padding: 1em 1.8em;
  border-radius: 0;
  background: var(--cat-vegetation);
  color: var(--black);
  font-family: var(--font-alt);
  font-size: var(--t3);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-3);
  text-transform: var(--style-upper);
  line-height: 1;
  cursor: pointer;
}
/* Ligne de chiffres : même registre que les clés de la table d'attributs (--t2, capitales). */
.jc-stats {
  margin: calc(var(--cs-gap) * .6) 0 calc(var(--cs-gap) * 3);   /* le bas : pour ne pas coller au cadre */
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 1.8em;
  font-family: var(--font-alt);
  font-size: var(--t2);
  font-weight: var(--fw-bold);
  letter-spacing: .2em;
  text-transform: var(--style-upper);
  color: var(--white);
  text-shadow: var(--shadow-text);
}
