/* ═══════════════════════════════════════════════════════════════════
   GALLERY NAV — 3 pastilles en bas au centre + sous-menu de filtres
   ───────────────────────────────────────────────────────────────────
   Volontairement EN BAS AU CENTRE, pas dans un coin : la nav est posée dans
   le monde à hauteur de main, elle n'est pas du chrome d'application.
   Voir HISTORIQUE.md § Chantier Galerie (« Le pourquoi »).
   ═══════════════════════════════════════════════════════════════════ */

.gn-root {
  position: fixed;
  left: 50%;
  /* Dégagement pour le libellé, qui passe SOUS les pastilles : à 30 px il
     ne restait que ~6 px sous le texte de la pastille active (agrandie ×1.34). */
  bottom: 52px;
  transform: translateX(-50%);
  z-index: 4200;                 /* au-dessus du voile de la galerie (4000) */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  pointer-events: none;          /* les enfants réactivent le leur */
}

/* ── Les 3 pastilles ───────────────────────────────────────────────── */
.gn-dots {
  display: flex;
  align-items: center;
  gap: 22px;
  pointer-events: auto;
}

.gn-dot {
  position: relative;
  width: 110px;
  height: 110px;
  border-radius: 0;
  border: none;
  padding: 0;
  cursor: pointer;
  background: transparent center/contain no-repeat;
  opacity: 1;
  transition: transform .28s ease, opacity .28s ease, filter .28s ease;
}
/* État REPOS = background sur le bouton lui-même. Le PNG hover vit sur ::after
   en overlay, opacity 0 → 1 pour un crossfade fluide (background-image n'a pas
   de transition native). */
.gn-dot[data-tab="characters"] { background-image: url('../../data/raw/nav/characters.png'); }
.gn-dot[data-tab="events"]     { background-image: url('../../data/raw/nav/events.png'); }
.gn-dot[data-tab="journeys"]   { background-image: url('../../data/raw/nav/journeys.png'); }

.gn-dot::after {
  content: '';
  position: absolute;
  inset: 0;
  background: transparent center/contain no-repeat;
  opacity: 0;
  transition: opacity .32s ease;
  pointer-events: none;
}
.gn-dot[data-tab="characters"]::after { background-image: url('../../data/raw/nav/characters_hover.png'); }
.gn-dot[data-tab="events"]::after     { background-image: url('../../data/raw/nav/events_hover.png'); }
.gn-dot[data-tab="journeys"]::after   { background-image: url('../../data/raw/nav/journeys_hover.png'); }

.gn-dot:hover::after,
.gn-dot.active::after { opacity: 1; }

.gn-dot:hover { transform: scale(1.06); }
.gn-dot.active { transform: scale(1.18); }

/* Pastille VERROUILLÉE (voyage en cours, voir gallery-nav.js `setLocked`) : grisée, inerte,
   sans effet de survol — elle doit se lire comme indisponible, pas comme cassée (sa raison
   est en infobulle). `:disabled` bat `:hover` : on annule explicitement l'agrandissement et
   le PNG de survol. */
.gn-dot:disabled { opacity: .35; filter: grayscale(.6); cursor: not-allowed; }
.gn-dot:disabled:hover { transform: none; }
.gn-dot:disabled:hover::after { opacity: 0; }

/* Le libellé n'apparaît qu'au survol / sur l'active : au repos, trois cercles
   suffisent — le texte permanent ferait barre d'outils. */
/* Libellé SOUS la pastille. ⚠️ Il est enfant du bouton, qui est agrandi de 34 %
   à l'état actif : sans contre-échelle le texte serait étiré avec lui. D'où le
   `scale(.75)` (≈ 1/1.34) sur l'active, et l'offset exprimé en % de la pastille
   pour que l'écart apparent reste constant. */
.gn-dot-label {
  position: absolute;
  left: 50%;
  top: calc(100% + 10px);
  transform: translateX(-50%);
  white-space: nowrap;
  font-family: var(--font);
  font-size: var(--t2);   /* cran --t2 */
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--grey1);
  opacity: 0;
  transition: opacity .24s ease;
  pointer-events: none;
}
/* Contre-échelle systématique sur hover ET active : le label reste à la
   MÊME taille apparente que le PNG grandit dans son bouton parent.
   hover  = 1/1.06 ≈ 0.943
   active = 1/1.18 ≈ 0.847 */
.gn-dot:hover .gn-dot-label {
  opacity: .9;
  transform: translateX(-50%) scale(0.943);
}
.gn-dot.active .gn-dot-label {
  opacity: 1;
  transform: translateX(-50%) scale(0.847);
  top: calc(100% + 7px);
}

/* ── Sous-menu de filtres ──────────────────────────────────────────── */
/* ── LES FILTRES — colonne verticale à gauche, centrée en hauteur ──
   ⚠️ SORTIS DE `.gn-root` (voir gallery-nav.js) : ils vivaient au-dessus
   des 3 pastilles, donc centrés en bas comme elles. Les imbriquer
   obligerait à annuler le positionnement du parent — c'est exactement ce
   qu'il aurait fallu faire pour les remonter. Même raison que la bande
   alphabet, séparée pour le même motif.
   Ils servent les DEUX modes — races en Personnages, types d'événement
   en Événements (la timeline emprunte le composant). */
/* ⚠️ CENTRÉE EN HAUTEUR, pas ancrée en bas.
   Posée contre le bord inférieur, la colonne se lisait comme un pied de
   page — et elle butait sur les 3 pastilles de nav, qui vivent au même
   niveau. Centrée, elle devient une palette d'outils : à hauteur de
   regard, à distance égale des deux bords. */
.gn-sub {
  position: fixed;
  left: clamp(16px, 2vw, 34px);
  top: 50%;
  /* ⚠️ Le centrage passe par `transform`, et l'animation d'apparition
     AUSSI : les deux doivent être écrites ENSEMBLE, sinon la seconde
     écrase la première et la colonne saute en haut de l'écran à
     l'ouverture. D'où le `-50%` répété dans les deux états. */
  transform: translateY(-50%) translateY(8px);
  z-index: 4200;                 /* au-dessus du voile, comme la nav */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  pointer-events: auto;
  opacity: 0;
  transition: opacity .34s ease, transform .34s ease;
}
.gn-sub[hidden] { display: none; }
/* ⚠️ La colonne n'est plus DESCENDANTE de `.gn-root` : le sélecteur
   `.gn-root.is-open .gn-sub` ne l'atteint plus. Elle se révèle donc sur
   son propre état — `hidden` est déjà piloté par le JS (renderChips /
   setChips / clearChips), il suffit de s'y accrocher. */
.gn-sub:not([hidden]) { opacity: 1; transform: translateY(-50%); }

/* ── Une puce : une icône carrée, son libellé au survol ────────────
   ⚠️ Le libellé est DANS le DOM (lu par les lecteurs d'écran, doublé en
   `title`), simplement masqué visuellement : une colonne de 15 libellés
   déployés mangerait la moitié de l'écran. */
.gn-chip {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  background: none;
  border: 1px solid var(--border);
  border-radius: 2px;
  padding: 0;
  width: 34px;
  height: 34px;
  cursor: pointer;
  font-family: var(--font);
  font-size: var(--t1);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--grey1);
  opacity: .68;
  overflow: visible;
  transition: opacity .2s ease, border-color .2s ease, color .2s ease;
}

/* ⚠️ ICÔNE GÉNÉRIQUE, IDENTIQUE POUR TOUS LES FILTRES — provisoire.
   Les 15 pictogrammes (8 types d'événement + 7 races) restent à dessiner.
   Le jour où ils arrivent dans `data/raw/icons/filters/`, il suffit de
   poser un `mask-image` par `[data-filter="…"]` : la colorisation par
   `background-color` est déjà en place, comme pour les boutons de carte. */
.gn-chip-ico {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
}
.gn-chip-ico::before {
  content: '';
  width: 11px;
  height: 11px;
  border: 1.5px solid currentColor;
  border-radius: 2px;
  transform: rotate(45deg);
  transition: background-color .2s ease;
}
.gn-chip.active .gn-chip-ico::before { background: currentColor; }

/* Le libellé sort SUR LA DROITE au survol — il ne pousse jamais la
   colonne, sinon les puces se décaleraient les unes sous les autres. */
.gn-chip-lb {
  position: absolute;
  left: calc(100% + 8px);
  top: 50%;
  padding: 4px 9px;
  white-space: nowrap;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: 2px;
  pointer-events: none;
  /* ⚠️ SEUL LE LIBELLÉ DU FILTRE ACTIF RESTE AFFICHÉ (04/10/2026). Tous visibles en permanence
     (essai du même jour) : les 8 libellés recouvraient la flèche précédent/suivant du slider.
     Au repos on lit donc UN filtre — celui qui est appliqué — ; les autres reviennent au survol et
     au focus clavier. (Pas de survol au tactile : on y lit au moins le filtre actif, ce qui est
     l'essentiel de « ce que je regarde ».) */
  opacity: 0;
  transform: translateY(-50%) translateX(-4px);
  transition: opacity .16s ease, transform .16s ease;
}
.gn-chip.active .gn-chip-lb,
.gn-chip:hover .gn-chip-lb,
.gn-chip:focus-visible .gn-chip-lb {
  opacity: 1;
  transform: translateY(-50%);
}
.gn-chip:hover:not(:disabled) { opacity: 1; border-color: var(--gold); }
.gn-chip.active {
  opacity: 1;
  color: var(--gold-light);
  border-color: var(--gold-light);
}

/* Compte de cartes du groupe — en retrait : c'est une indication, pas le
   libellé. Il dit combien le filtre va produire. */
.gn-chip-n {
  margin-left: 7px;
  opacity: .5;
  font-size: .9em;
  letter-spacing: .04em;
}
.gn-chip.active .gn-chip-n { opacity: .75; }

/* Groupe vide : affiché mais inerte — son absence est une information, et le
   retirer ferait sauter la rangée d'un onglet à l'autre. */
.gn-chip.is-empty {
  opacity: .28;
  cursor: default;
}

/* ── Message d'échec (réseau / API) ──────────────────────────────────
   Posé juste AU-DESSUS des pastilles. En absolu dans `.gn-root` (fixed) :
   un enfant de la colonne flex, même invisible, aurait décalé les pastilles
   de sa hauteur + du `gap`. Même fondu que `.gn-title`. */
.gn-msg {
  position: absolute;
  left: 50%;
  bottom: 100%;
  margin-bottom: 18px;           /* = le `gap` de .gn-root */
  transform: translateX(-50%) translateY(6px);
  max-width: 90vw;
  padding: .5em 1em;
  font-family: var(--font-alt);
  font-size: var(--t3);
  text-align: center;
  color: var(--grey1);
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity .34s ease, transform .34s ease, visibility 0s linear .34s;
}
.gn-msg.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
  transition: opacity .34s ease, transform .34s ease, visibility 0s;
}

/* ── Titre de la pile (en haut, centré) ──────────────────────────────
   Dit CE QU'ON REGARDE (« 41 Hobbits », « 214 Personnages ») — sans lui,
   rien à l'écran ne nomme le groupe filtré. Seul espace encore libre :
   filtres à gauche, alphabet à droite, nav en bas. Élément SÉPARÉ pour
   la même raison que .gn-sub et .gn-alpha (position propre, imbriquer
   dans .gn-root obligerait à annuler son centrage bas). */
.gn-title {
  position: fixed;
  left: 50%;
  top: clamp(40px, 7vh, 90px);
  transform: translateX(-50%) translateY(-8px);
  z-index: 4200;                 /* au-dessus du voile, comme la nav */
  font-family: var(--font);
  font-size: var(--t5);
  letter-spacing: .05em;
  color: var(--grey1);
  text-align: center;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity .34s ease, transform .34s ease, visibility 0s linear .34s;
}
.gn-title.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
  transition: opacity .34s ease, transform .34s ease, visibility 0s;
}
.gn-title-n {
  color: var(--gold-light);
  font-weight: var(--fw-bold);
}

/* ── Bande alphabet (à droite) ─────────────────────────────────────
   Les 26 lettres sont TOUJOURS là, les vides grisées : la bande garde une
   hauteur stable d'un filtre à l'autre. N'afficher que les lettres présentes
   la ferait sauter à chaque changement. */
.gn-alpha {
  position: fixed;
  right: 18px;
  top: 50%;
  transform: translateY(-50%) translateX(10px);
  z-index: 4200;                 /* au-dessus du voile, comme la nav */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  opacity: 0;
  visibility: hidden;
  transition: opacity .34s ease, transform .34s ease, visibility 0s linear .34s;
}
.gn-alpha.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(-50%) translateX(0);
  transition: opacity .34s ease, transform .34s ease, visibility 0s;
}

.gn-letter {
  width: 20px;
  height: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: var(--font);
  font-size: var(--t1);
  line-height: 1;
  letter-spacing: .04em;
  color: var(--grey1);
  opacity: .62;
  transition: opacity .18s ease, color .18s ease, transform .18s ease;
}
.gn-letter:hover:not(:disabled) {
  opacity: 1;
  color: var(--gold-light);
  transform: scale(1.35);
}

/* Lettre sans carte : visible mais inerte — elle dit « il n'y a rien ici »,
   ce qui est une information, pas un manque. */
.gn-letter.is-empty {
  opacity: .16;
  cursor: default;
}

/* Lettre de la carte au centre. */
.gn-letter.active {
  opacity: 1;
  color: var(--gold-light);
  font-weight: var(--fw-bold);
  transform: scale(1.3);
}

@media (prefers-reduced-motion: reduce) {
  .gn-dot, .gn-sub, .gn-dot-label, .gn-msg,
  .gn-alpha, .gn-letter { transition-duration: 1ms !important; }
}
