/* ==========================================================================
   GI-DATABASE.CSS
   Le catalogue de la base de donnees : grille de portraits a gauche, colonne
   de filtres a droite, tuile d'entite, etat vide.

   TOUT RESTE SOMBRE. Le panneau de filtres a ete essaye sur parchemin clair le
   2 aout 2026 : refuse. La regle de surface du site tient donc telle quelle,
   le parchemin est reserve au contenu redactionnel, et une grille de cent
   vingt-quatre portraits comme son panneau de filtres sont des instruments de
   navigation.

   LES DEUX ZONES SONT AUX MEMES PLACES QUE SUR LA FICHE. La grille occupe la
   zone ou la fiche met ses onglets, la colonne de filtres occupe la zone ou la
   fiche met son portrait et ses statistiques. Passer de la liste a une fiche ne
   deplace donc aucun bloc a l'ecran.

   CE QUI N'EST PAS ECRIT ICI. Aucun nom d'element, aucun libelle de filtre,
   aucun intitule de tri : ils viennent tous du fichier servi. Les couleurs de
   rarete et d'element ci-dessous sont le seul point en dur, et elles ont toutes
   une valeur de repli : une huitieme valeur d'element ou une rarete inconnue
   s'affiche en or et rien ne casse.
   ========================================================================== */

.gi-cat {
  --cat-tuile: 10.5rem;         /* largeur minimale d'une tuile */
  --cat-gap: var(--sp-4);
  --cat-aside: 33rem;           /* MOITIE PLUS LARGE depuis le 03/08/2026 :
                                   a 22 rem, les etiquettes tenaient deux par
                                   ligne et la recherche et le tri s'empilaient.
                                   A 33 rem elles tiennent a trois, et les deux
                                   commandes partagent une ligne : la colonne
                                   gagne quatre rangees de hauteur et la grille
                                   passe a cinq portraits par ligne. La fiche de
                                   personnage suit la meme mesure. */
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--cat-aside);
  gap: var(--sp-6) var(--sp-7);
  align-items: start;
}

/* --------------------------------------------------------------------------
   Les couleurs de rarete, conventionnelles dans le jeu et reprises telles
   quelles : gris, vert, bleu, violet, or. Elles servent le fond des portraits
   et rien d'autre. Une rarete absente ou inconnue tombe sur --cat-r0.
   -------------------------------------------------------------------------- */
.gi-cat {
  /* LES TEINTES DE RARETE, DONNEES LE 03/08/2026 ET A NE PAS RETOUCHER.
     Elles servent les personnages, et serviront les armes et les ressources.
     Sans etoile et une etoile partagent le gris. */
  --cat-r0: #7d8083;
  --cat-r1: #7d8083;
  --cat-r2: #599479;
  --cat-r3: #5985ab;
  --cat-r4: #926eb9;
  --cat-r5: #c57b24;
}

/* ==========================================================================
   C0. LE FOND
   --------------------------------------------------------------------------
   DE LA PROFONDEUR SANS UNE SEULE PARTICULE. Les poussieres animees ont ete
   essayees le 2 aout 2026 : trop presentes derriere cent vingt-quatre
   portraits, la ou elles fonctionnent bien derriere trois cartes d'article.
   La profondeur vient donc de quatre couches fixes, et elle ne coute aucune
   image ni aucun script.

     1. deux halos elementaires tres dilues, l'un froid en haut a gauche,
        l'autre chaud en bas a droite : ils donnent la couleur ;
     2. un voile vertical qui fonce vers le bas : il donne la direction ;
     3. un semis d'etoiles minuscules en degrades radiaux, fige : il donne la
        matiere, sans le scintillement qui fatigue ;
     4. le grain a deux frequences et le vignettage, poses par les classes
        utilitaires du site sur le bloc de page.
   ========================================================================== */

.gi-cat-fond {
  position: relative;
  isolation: isolate;
  background-color: var(--gi-ink-950);
  background-image:
    radial-gradient(58% 42% at 12% 4%, rgba(79, 166, 227, 0.10), transparent 68%),
    radial-gradient(52% 44% at 88% 92%, rgba(198, 165, 93, 0.09), transparent 70%),
    radial-gradient(90% 60% at 50% 0%, rgba(177, 137, 221, 0.055), transparent 72%),
    linear-gradient(180deg, var(--gi-ink-900) 0%, var(--gi-ink-950) 62%, #060B11 100%);
}
/* Le semis. Trois tailles de points a trois pas differents : le motif ne se
   repete visiblement qu'au bout de plusieurs ecrans. */
.gi-cat-fond::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.5;
  background-image:
    radial-gradient(1.4px 1.4px at 18% 22%, rgba(241, 233, 218, 0.30), transparent 100%),
    radial-gradient(1.1px 1.1px at 73% 11%, rgba(219, 192, 132, 0.26), transparent 100%),
    radial-gradient(1.6px 1.6px at 41% 67%, rgba(241, 233, 218, 0.22), transparent 100%),
    radial-gradient(1px 1px at 88% 44%, rgba(198, 165, 93, 0.28), transparent 100%),
    radial-gradient(1.2px 1.2px at 9% 81%, rgba(241, 233, 218, 0.18), transparent 100%),
    radial-gradient(1.3px 1.3px at 61% 91%, rgba(219, 192, 132, 0.20), transparent 100%);
  background-size: 340px 300px, 470px 410px, 610px 520px, 390px 350px, 520px 470px, 430px 380px;
}

/* ==========================================================================
   C1. LA COLONNE DE FILTRES
   ========================================================================== */

/* La colonne reprend la matiere des panneaux du site : grain leger, filet
   dore, et ornements aux angles. Sans eux elle se lisait comme un aplat pose
   sur la page plutot que comme un objet du meme monde que les cartes. */
.gi-cat__aside::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  border-radius: inherit;
  opacity: 0.1;
  mix-blend-mode: overlay;
  background-image: var(--gi-noise), var(--gi-noise);
  background-size: 168px 168px, 820px 820px;
}
.gi-cat__aside > * { position: relative; z-index: 1; }
.gi-cat__aside::-webkit-scrollbar { width: 9px; }
.gi-cat__aside::-webkit-scrollbar-track { background: rgba(8, 14, 20, 0.5); border-radius: 5px; }
.gi-cat__aside::-webkit-scrollbar-thumb {
  background: var(--gi-gold-600);
  border-radius: 5px;
  border: 2px solid var(--gi-ink-900);
}
.gi-cat__aside::-webkit-scrollbar-thumb:hover { background: var(--gi-gold-500); }
.gi-cat__aside::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: inherit;
  background:
    linear-gradient(90deg, transparent 12%, var(--gi-line-strong) 50%, transparent 88%) top / 100% 1px no-repeat;
}

.gi-cat__aside {
  position: sticky;
  /* SOUS L'EN-TETE, PAS DERRIERE. La barre du site est elle-meme collante :
     avec `top: var(--sp-4)`, la colonne se calait a 17 px du haut de la
     fenetre, donc DERRIERE l'en-tete. `--gi-header-h` est mesuree par
     gi-ui.js a chaque chargement et a chaque changement de largeur ; le repli
     vaut la hauteur de la barre au repos. */
  top: calc(var(--gi-header-h, 74px) + var(--sp-3));
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  padding: var(--sp-5);
  background: linear-gradient(180deg, var(--gi-ink-850), var(--gi-ink-900));
  border: 1px solid var(--gi-line);
  border-radius: 4px;
  /* UN DEFILEMENT INTERIEUR, ET SEULEMENT QUAND IL FAUT.
     Le commentaire ci-dessous decrivait le choix inverse, tenu jusqu'au
     03/08/2026 : sur un ecran court, les derniers groupes passaient sous le
     bas de la fenetre et devenaient inatteignables, la colonne etant collante.
     `max-height` bornee a la fenetre plus `overflow-y: auto` rend la main.
     La barre horizontale parasite de l'ancienne tentative est evitee par
     `overflow-x: clip` : c'est le filet du bord haut qui debordait d'un pixel.
     Sur un ecran assez haut, rien ne change : la colonne tient et aucune barre
     n'apparait. */
  max-height: calc(100dvh - var(--gi-header-h, 74px) - var(--sp-6));
  overflow-y: auto;
  overflow-x: clip;
  /* LA BARRE DOIT SE VOIR QUAND ELLE SERT. `scrollbar-width: thin` seul laisse
     Chrome dessiner une barre par-dessus le contenu, quasi invisible sur un
     fond sombre : on la dessine. */
  scrollbar-width: thin;
  scrollbar-color: var(--gi-gold-600) rgba(8, 14, 20, 0.5);
  scrollbar-gutter: stable;

  /* Ancienne note, gardee pour memoire :
     PAS DE DEFILEMENT INTERIEUR. Une hauteur maximale plus `overflow: auto`
     faisait apparaitre deux barres, dont une horizontale de quelques pixels
     due au filet qui deborde d'un pixel. La colonne tient dans l'ecran une
     fois les groupes replies ; si un axe futur la rallonge, c'est la page qui
     defile, pas elle. */
  overflow: visible;
}

.gi-cat__titre {
  font-family: var(--gi-font-display);
  font-size: var(--fs-t-lg);
  font-weight: 700;
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-caps-lg);
  color: var(--gi-sand-50);
  text-align: center;
}
.gi-cat__titre + .gi-cat__count { margin-top: calc(var(--sp-2) * -1); }

.gi-cat__field { position: relative; }
.gi-cat__field svg {
  position: absolute;
  inset-block: 0;
  left: 0.85rem;
  margin: auto;
  width: 16px; height: 16px;
  color: var(--gi-sand-500);
  pointer-events: none;
}
.gi-cat__input {
  width: 100%;
  padding: 0.62rem 0.9rem 0.62rem 2.4rem;
  font-family: var(--gi-font-body);
  font-size: var(--fs-base);
  color: var(--gi-text);
  background: var(--gi-ink-900);
  border: 1px solid var(--gi-line);
  border-radius: 3px;
  transition: border-color var(--t-fast);
}
.gi-cat__input::placeholder { color: var(--gi-sand-600); }
.gi-cat__input:hover { border-color: var(--gi-line-strong); }
.gi-cat__input:focus-visible { outline: none; border-color: var(--gi-gold-500); }

/* L'intitule « Trier par » reste dans le balisage pour les lecteurs d'ecran,
   mais il ne se voit plus : les libelles du menu se suffisent. */
.gi-cat__sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden; white-space: nowrap;
}
/* LA RECHERCHE ET LE TRI PARTAGENT UNE LIGNE. Empiles, ils prenaient deux
   rangees pour deux commandes qui se lisent d'un coup d'oeil. */
.gi-cat__commandes {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--sp-3);
  align-items: end;
}
.gi-cat__sort { display: flex; flex-direction: column; gap: var(--sp-2); }
.gi-cat__sort > label,
.gi-cat-group__label {
  font-family: var(--gi-font-display);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  color: var(--gi-gold-400);
  line-height: 1.3;
}
.gi-cat__select {
  width: 100%;
  padding: 0.55rem 2rem 0.55rem 0.75rem;
  font-family: var(--gi-font-body);
  font-size: var(--fs-sm);
  color: var(--gi-text);
  background-color: var(--gi-ink-900);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%238A7D6A' stroke-width='1.6' d='M1 1.6 6 6.4l5-4.8'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.7rem center;
  background-size: 10px;
  border: 1px solid var(--gi-line);
  border-radius: 3px;
  appearance: none;
  cursor: pointer;
}
.gi-cat__select:hover,
.gi-cat__select:focus-visible { outline: none; border-color: var(--gi-gold-500); }
/* PLUS CLAIR : mesure du 03/08/2026, le libelle du tri et le texte du menu
   etaient en `--gi-sand-300` sur `--gi-ink-800`, soit du sombre sur du sombre.
   Les deux remontent d'un cran. */
.gi-cat__select, .gi-cat__input { color: var(--gi-sand-50); }
.gi-cat__input::placeholder { color: var(--gi-sand-400); }
.gi-cat__sort > label { color: var(--gi-gold-300); }

.gi-cat__count {
  font-size: var(--fs-sm);
  color: var(--gi-text-faint);
}
.gi-cat__count b {
  font-weight: 600;
  color: var(--gi-sand-200);
  font-variant-numeric: tabular-nums;
}

.gi-cat__reset {
  padding: 0.5rem 0.85rem;
  font-family: var(--gi-font-body);
  font-size: var(--fs-sm);
  color: var(--gi-sand-300);
  background: transparent;
  border: 1px solid var(--gi-line);
  border-radius: 3px;
  cursor: pointer;
  transition: color var(--t-fast), border-color var(--t-fast);
}
.gi-cat__reset:hover { color: var(--gi-gold-300); border-color: var(--gi-line-strong); }

/* --------------------------------------------------------------------------
   Un groupe par axe du vocabulaire, dans l'ordre ou le fichier les donne.
   L'intitule vient de vocabulary.<axe>.label et JAMAIS d'ici : le meme axe
   s'appelle « Region » sur les personnages et « Lieu de farm » sur les
   artefacts.
   -------------------------------------------------------------------------- */
.gi-cat__filters {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--gi-line);
}
/* Un bloc vide ne doit pas laisser sa gouttiere : le compteur et le bouton de
   remise a zero n'existent que quand un filtre est pose, et sans ceci ils
   laissaient deux espaces morts sous le dernier groupe. */
.gi-cat__aside > :empty { display: none; }

.gi-cat-group { display: flex; flex-direction: column; gap: var(--sp-2); }
/* UN CHEVRON, ET IL EST LA A TOUTES LES LARGEURS.
   Les groupes etaient repliables depuis le debut, mais rien ne le disait sur
   ordinateur : le chevron n'apparaissait que sous 1080 px. Un affordance qui
   ne se voit pas n'existe pas. */
.gi-cat-group__label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 0.2rem 0;
  cursor: pointer;
  list-style: none;
}
.gi-cat-group__label::-webkit-details-marker { display: none; }
.gi-cat-group__label::after {
  content: "";
  width: 8px; height: 8px;
  flex: none;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  rotate: 45deg;
  translate: 0 -2px;
  transition: rotate var(--t-fast);
}
.gi-cat-group[open] > .gi-cat-group__label::after { rotate: -135deg; translate: 0 2px; }
/* TROIS PAR LIGNE, DE LARGEUR EGALE. En flex, chaque etiquette prenait la
   largeur de son texte : « Épée à deux mains » et « Arc » ne faisaient pas la
   meme, et chaque groupe dessinait un escalier different. En grille, toutes
   les etiquettes d'un groupe font exactement un tiers de ligne. */
.gi-cat-group__chips {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-2);
}

.gi-cat-chip {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 0.4rem;
  min-width: 0;
  padding: 0.34rem 0.62rem;
  text-align: left;
  font-family: var(--gi-font-body);
  font-size: var(--fs-sm);
  color: var(--gi-sand-300);
  background: var(--gi-ink-800);
  border: 1px solid var(--gi-line-soft);
  border-radius: 999px;
  cursor: pointer;
  transition: color var(--t-fast), border-color var(--t-fast), background var(--t-fast);
}
/* Le libelle occupe la place restante et se coupe proprement ; le compteur
   reste colle a droite et ne bouge pas d'une etiquette a l'autre. */
.gi-cat-chip > .gi-cat-chip__t {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gi-cat-chip:hover { color: var(--gi-sand-100); border-color: var(--gi-line-strong); }
.gi-cat-chip:focus-visible { outline: 2px solid var(--gi-gold-500); outline-offset: 2px; }
.gi-cat-chip__n {
  margin-left: auto;
  font-size: var(--fs-xs);
  color: var(--gi-sand-600);
  font-variant-numeric: tabular-nums;
}
.gi-cat-chip[aria-pressed="true"] {
  color: var(--gi-ink-900);
  background: var(--gi-gold-400);
  border-color: var(--gi-gold-300);
  font-weight: 600;
}
.gi-cat-chip[aria-pressed="true"] .gi-cat-chip__n { color: rgba(13, 22, 32, 0.62); }

/* ==========================================================================
   C2. LES SYMBOLES
   --------------------------------------------------------------------------
   UN SYMBOLE VIENT DE LA DONNEE, PAS D'ICI. Le versement peut donner une image
   a chaque valeur de vocabulaire, sous la cle `icon`. Quand elle existe, le
   script pose une image ; quand elle manque, il pose la pastille de couleur
   ci-dessous. Les deux portent la meme classe, se placent au meme endroit, et
   passer de l'une a l'autre ne demande aucune modification de cette feuille.
   ========================================================================== */

.gi-sym {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  object-fit: contain;
  border-radius: 50%;
}
/* LE SYMBOLE PEINT. L'icone sert de MASQUE et la couleur vient de `--chip`,
   que `data-v` pose. C'est ainsi que les sept elements cessent de se
   ressembler. `--ico` est ecrite par le script, jamais ici. */
/* NEUTRE PAR DEFAUT, COLORE SEULEMENT QUAND UNE TEINTE EXISTE. `--chip` n'est
   posee que par `[data-v]` des sept elements : partout ailleurs le symbole
   reste clair, et c'est ce qui fait ressortir l'element. */
.gi-sym--peint {
  background: var(--chip, var(--gi-sand-100));
  -webkit-mask: var(--ico) center / contain no-repeat;
          mask: var(--ico) center / contain no-repeat;
  border-radius: 0;
}
.gi-cat-chip[aria-pressed="true"] .gi-sym--peint { background: var(--gi-ink-900); }
/* PLUS DE ROND CREUX DANS UNE ETIQUETTE DE FILTRE.
   Il tenait la place d'une icone a venir, mais sur l'axe des regions, le seul
   concerne, il n'y en aura peut-etre jamais : sept ronds vides alignes ne
   disaient rien et volaient la place du libelle. Il reste dans la COLONNE de
   la tuile, ou il annonce vraiment un symbole manquant. */
.gi-cat-chip .gi-sym--creux { display: none; }
.gi-sym--creux {
  width: 0.72em;
  height: 0.72em;
  box-shadow: inset 0 0 0 1px var(--gi-sand-600);
}
.gi-cat-chip[aria-pressed="true"] .gi-sym--creux { box-shadow: inset 0 0 0 1px rgba(13, 22, 32, 0.45); }

/* La forme de repli : une pastille pleine, teintee par --chip. Quand aucune
   couleur n'est connue pour la valeur, --chip-on vaut zero et la pastille
   prend une largeur nulle : elle disparait au lieu de poser un point dore
   qui ne veut rien dire devant « Arme d'hast ». */
.gi-sym--point {
  width: calc(0.62em * var(--chip-on, 0));
  height: 0.62em;
  background: var(--chip, transparent);
}
.gi-cat-chip[aria-pressed="true"] .gi-sym--point { box-shadow: 0 0 0 1px rgba(13, 22, 32, 0.35); }

/* Les sept elements de Teyvat. Une huitieme valeur tombe sur l'or de repli
   declare dans .gi-sym--point, et la barre reste lisible. */
[data-v="anemo"]   { --chip: var(--el-anemo);   --chip-on: 1; --fx: var(--el-anemo); }
[data-v="geo"]     { --chip: var(--el-geo);     --chip-on: 1; --fx: var(--el-geo); }
[data-v="electro"] { --chip: var(--el-electro); --chip-on: 1; --fx: var(--el-electro); }
[data-v="dendro"]  { --chip: var(--el-dendro);  --chip-on: 1; --fx: var(--el-dendro); }
[data-v="hydro"]   { --chip: var(--el-hydro);   --chip-on: 1; --fx: var(--el-hydro); }
[data-v="pyro"]    { --chip: var(--el-pyro);    --chip-on: 1; --fx: var(--el-pyro); }
[data-v="cryo"]    { --chip: var(--el-cryo);    --chip-on: 1; --fx: var(--el-cryo); }

/* L'etoile du jeu, reprise du jeton --gi-star : quatre branches effilees.
   Elle sert la rarete partout, en puce de filtre comme sur un portrait. */
.gi-etoile {
  width: 1em;
  height: 1em;
  flex: none;
  background: var(--et, var(--gi-gold-400));
  -webkit-mask: var(--gi-star) center / contain no-repeat;
          mask: var(--gi-star) center / contain no-repeat;
}
.gi-cat-chip[aria-pressed="true"] .gi-etoile { --et: var(--gi-ink-900); }

/* ==========================================================================
   C3. LA GRILLE ET LA TUILE
   ========================================================================== */

/* SIX COLONNES, ET C'EST UN NOMBRE, PAS UNE LARGEUR MINIMALE.
   En `auto-fill`, la grille rendait cinq ou sept colonnes selon l'ecran et la
   tuile changeait de taille a chaque palier. Un nombre fixe par palier garde
   des rangees pleines et une tuile qui suit la largeur disponible. */
.gi-cat__grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--cat-gap);
  list-style: none;
  margin: 0;
  padding: 0;
}
/* Le <li> est l'hote du nuage de particules : il doit pouvoir le laisser
   deborder, sinon les etincelles sont coupees au ras de la tuile. */
.gi-cat__grid > li {
  min-width: 0;
  display: flex;
  position: relative;
}

.gi-cat-card {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  width: 100%;
  overflow: hidden;
  text-decoration: none;
  background: var(--gi-ink-850);
  border: 1px solid var(--gi-line-soft);
  border-radius: 4px;
  /* AU REPOS AUSSI, LA TUILE SE DETACHE. Trois couches, et aucune n'est un
     effet de survol : une ombre portee large et tres douce qui creuse le fond
     sous la tuile, une ombre courte qui la pose, et un liseré interieur clair
     sur le bord haut, qui simule la lumiere rasante et donne l'epaisseur. Sans
     elles, cent vingt-quatre rectangles sombres flottaient sur un fond sombre
     et la grille n'avait de relief qu'au passage du curseur. */
  box-shadow:
    0 14px 30px -18px rgba(0, 0, 0, 0.85),
    0 3px 8px -4px rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(241, 233, 218, 0.07);
  transition: border-color var(--t-base) var(--ease-out),
              translate var(--t-base) var(--ease-out),
              box-shadow var(--t-base) var(--ease-out);
}
a.gi-cat-card:hover,
a.gi-cat-card:focus-visible {
  border-color: var(--gi-line-strong);
  translate: 0 -3px;
  box-shadow:
    0 22px 42px -18px rgba(0, 0, 0, 0.9),
    0 6px 14px -6px rgba(0, 0, 0, 0.6),
    inset 0 1px 0 rgba(241, 233, 218, 0.12);
}
a.gi-cat-card:focus-visible { outline: 2px solid var(--gi-gold-500); outline-offset: 2px; }
/* Une tuile sans lien reste une tuile : meme dessin, sans le mouvement. Elle
   ne doit surtout pas ressembler a un lien qui ne marche pas. */
.gi-cat-card--muet { cursor: default; }

/* Le fond suit la rarete, et c'est la premiere information que l'oeil prend
   sur une grille de cent vingt-quatre portraits. */
.gi-cat-card__portrait::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.2;
  mix-blend-mode: overlay;
  background-image: var(--gi-noise), var(--gi-noise);
  background-size: 158px 158px, 780px 780px;
}
/* PLUS DE VIGNETTAGE. Il reste une ombre BASSE, tres douce, qui pose le
   personnage sur son fond sans toucher aux bords ni a la couleur. */
.gi-cat-card__portrait::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(180deg, transparent 62%, rgba(6, 11, 18, 0.28) 100%);
}
.gi-cat-card__portrait {
  position: relative;
  aspect-ratio: 1;
  overflow: hidden;
  /* LE FOND DE RARETE, ET C'EST LA RECETTE DE TOUT LE SITE.
     Elle sert la grille, les emplacements d'equipe, et servira les armes et
     les ressources : partout ou l'on pose l'icone d'une entite, on pose ce
     fond-la, a la couleur de SA rarete.

     UN DEGRADE, PLUS UN VIGNETTAGE. Le vignettage a ete essaye deux fois et
     refuse les deux fois : il assombrit les bords, donc il mange la couleur
     par les quatre cotes, et c'est justement la couleur qu'on veut lire. Un
     simple degrade vertical, le haut un peu plus sombre, donne le relief sans
     rien reprendre. Le grain fait le reste. */
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--rar, var(--cat-r0)) 78%, #060b12) 0%,
      var(--rar, var(--cat-r0)) 62%,
      color-mix(in srgb, var(--rar, var(--cat-r0)) 88%, #f1e9da) 100%);
}
.gi-cat-card[data-r="1"] { --rar: var(--cat-r1); }
.gi-cat-card[data-r="2"] { --rar: var(--cat-r2); }
.gi-cat-card[data-r="3"] { --rar: var(--cat-r3); }
.gi-cat-card[data-r="4"] { --rar: var(--cat-r4); }
.gi-cat-card[data-r="5"] { --rar: var(--cat-r5); }

/* Deux portraits sur cent vingt-quatre ne sont pas carres : `contain` les
   laisse entiers au lieu de les rogner, et les dimensions ecrites dans le
   balisage evitent que la grille saute a chaque image chargee. */
.gi-cat-card__portrait img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center bottom;
  display: block;
  transition: scale var(--t-slow) var(--ease-out);
}
a.gi-cat-card:hover .gi-cat-card__portrait img { scale: 1.06; }

/* La colonne de symboles, contre le bord gauche du portrait : arme, statistique
   d'elevation, element. Elle ne contient que ce qui existe. */
.gi-cat-card__ico {
  position: absolute;
  /* MEME ECART EN HAUT ET A GAUCHE : la colonne se calait plus bas qu'elle
     n'etait rentree, et l'oeil lisait un decalage vers la droite. */
  inset: 0.35rem auto auto 0.35rem;
  display: flex;
  flex-direction: column;
  gap: 0.28rem;
  margin: 0;
  padding: 0;
  list-style: none;
  z-index: 2;
}
.gi-cat-card__ico li {
  display: grid;
  place-items: center;
  width: 1.65rem;
  height: 1.65rem;
  font-size: 0.9rem;
  background: rgba(8, 14, 20, 0.6);
  border: 1px solid color-mix(in srgb, var(--chip, var(--gi-gold-600)) 42%, transparent);
  border-radius: 50%;
  backdrop-filter: blur(3px);
}
/* LE LOGO D'ELEMENT PREND LA COULEUR DE SON ELEMENT.
   L'image versee est blanche sur fond transparent : posee telle quelle, les
   sept elements se ressemblaient tous. En MASQUE, elle prend `--chip`, que
   `[data-v]` pose deja. Le type d'arme, lui, garde son image : elle porte un
   cartouche decoratif qu'un masque effacerait. */
.gi-cat-card__ico li[data-v] .gi-sym {
  background: var(--chip, var(--gi-gold-400));
  -webkit-mask: var(--ico) center / contain no-repeat;
          mask: var(--ico) center / contain no-repeat;
  border-radius: 0;
}
.gi-cat-card__ico li[data-v] img.gi-sym { background: var(--chip, var(--gi-gold-400)); }

/* UN EMPLACEMENT PLUTOT QU'UN TROU. Tant que le versement ne porte pas
   d'icone, un axe sans couleur garde son rond, avec un point sourd dedans :
   la colonne montre des le premier jour combien de symboles elle recevra, et
   la tuile ne changera pas de hauteur quand ils arriveront. */
.gi-cat-card__ico li:not(:has(img)):not([data-v])::after {
  content: "";
  width: 0.42rem;
  height: 0.42rem;
  border-radius: 50%;
  background: var(--gi-sand-600);
}
/* L'emplacement d'un axe que le versement ne porte pas encore du tout. */
.gi-cat-card__ico li[data-attente]::after { background: transparent; box-shadow: inset 0 0 0 1px var(--gi-sand-600); }

.gi-cat-card__rank {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  display: flex;
  justify-content: center;
  gap: 1px;
  padding: 1.2rem 0 0.35rem;
  background: linear-gradient(180deg, transparent, rgba(8, 14, 20, 0.8));
}
/* Grandes, parce que c'est ce qu'on lit en premier sur une grille. */
.gi-cat-card__rank .gi-etoile { width: 1.2rem; height: 1.2rem; }

/* LE PIED FAIT TOUJOURS DEUX LIGNES.
   Le parchemin y a ete essaye le 2 aout 2026 : refuse, le sombre tenait mieux.
   Ne pas le represser.

   LA HAUTEUR N'EST PAS RESERVEE, ELLE EST PARTAGEE PAR RANGEE.
   Mesure du 2 aout 2026 : « Uumemizuki Mizuki » passait a la ligne et faisait
   grandir la rangee entiere, donc Citlali, Lan Yan et Mavuika avec elle. Le
   premier remede, deux lignes reservees partout, corrigeait le decalage mais
   grossissait AUSSI les rangees qui n'ont que des noms courts.

   Le bon remede tient en une ligne : `flex: 1` sur le pied. La grille etire
   deja toutes les tuiles d'une meme rangee a la meme hauteur ; c'est le pied
   qui absorbe cet etirement, au lieu de le laisser au portrait. Une rangee
   sans nom long garde donc un pied d'une seule ligne, et seule la rangee qui
   porte un nom long grandit. Au-dela de deux lignes, on coupe. */
.gi-cat-card__name {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
  padding: var(--sp-3);
  font-family: var(--gi-font-title);
  font-size: var(--fs-t-sm);
  font-weight: 600;
  letter-spacing: -0.014em;
  line-height: var(--lh-snug);
  text-align: center;
  text-wrap: balance;
  color: var(--gi-sand-50);
  /* LE BLEU DES PANNEAUX, PAS L'ENCRE. Sous un fond de rarete franc, un pied
     presque noir faisait un trou. Il prend donc la matiere de la colonne de
     filtres : le meme degrade, le meme grain, et un vignettage doux qui le
     recentre. */
  background: linear-gradient(180deg, var(--gi-ink-800), var(--gi-ink-850));
  border-top: 1px solid var(--gi-line);
  overflow: hidden;
  isolation: isolate;
}
.gi-cat-card__name::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0.12;
  mix-blend-mode: overlay;
  background-image: var(--gi-noise), var(--gi-noise);
  background-size: 128px 128px, 620px 620px;
}
.gi-cat-card__name::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(120% 160% at 50% 0%, transparent 34%, rgba(6, 11, 18, 0.42) 100%);
}
.gi-cat-card__name > span {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}
a.gi-cat-card:hover .gi-cat-card__name { color: var(--gi-gold-300); }

/* ==========================================================================
   C3 bis. LES ETINCELLES DU SURVOL
   --------------------------------------------------------------------------
   La tuile emprunte le nuage de particules de gi-hover.js, dans la teinte de
   son element. Deux reglages sont necessaires, et tous deux ont ete trouves en
   regardant l'ecran.

   PAS DE LAMPE. `.gi-card--hover` apporte AUSSI la lueur qui suit le curseur,
   dessinee par un degrade elliptique en fond de carte. Sur une tuile de
   portrait elle se lit comme une tache ovale etiree, et non comme une lampe :
   on la coupe et on ne garde que les etincelles.

   LE NUAGE PASSE DERRIERE LES TUILES VOISINES. La feuille de jetons ne
   remonte que `.gi-card`, or nos tuiles s'appellent `.gi-cat-card` : sans les
   deux regles ci-dessous, le canvas d'une tuile se dessine PAR-DESSUS ses
   voisines. C'est le porteur qui monte au survol, pas la tuile, pour que le
   nuage puisse deborder au-dessus des rangees du dessus.
   ========================================================================== */

/* `!important` parce que gi-hover.js ecrit --lamp en style en ligne sur la
   carte : sans lui, la lueur revient au premier survol. */
/* LE NUAGE PASSE DERRIERE TOUTES LES TUILES, PAS SEULEMENT LES VOISINES.
   Aucun z-index sur le porteur : s'il en portait un, il ouvrirait son propre
   contexte d'empilement et son canvas ne pourrait plus passer sous les tuiles
   des autres porteurs. Sans lui, canvas et tuiles s'empilent tous dans le meme
   contexte, celui de la grille, et le 1 des tuiles bat le 0 des canvas
   quelle que soit leur place dans la page. */
.gi-cat-card.gi-card--hover { --lamp: 0 !important; }

/* LE CANVAS DEBORDE LA TUILE, IL NE DOIT PAS ETRE BORNE PAR ELLE. La feuille
   de jetons pose `max-width: 100%` sur tout canvas. Mesure du 2 aout 2026 :
   le nuage demandait 317 px de large, la regle le ramenait a 193 alors que sa
   surface de dessin en faisait toujours 317. Les etincelles etaient donc
   ECRASEES d'un facteur 1,64 a l'horizontale, et rondes a la verticale : d'ou
   les ovales. */
.gi-cat__grid .gi-fx { max-width: none; }
.gi-cat__grid > li > .gi-cat-card { position: relative; z-index: 1; }
.gi-cat__grid .gi-fx { z-index: 0; }

/* ==========================================================================
   C4. LE RESUME AU SURVOL
   --------------------------------------------------------------------------
   Un panneau unique, deplace d'une tuile a l'autre : cent vingt-quatre
   panneaux poses d'avance couteraient cent vingt-quatre fois leur balisage
   pour un seul visible a la fois.
   ========================================================================== */

.gi-cat-apercu {
  position: fixed;
  z-index: 60;
  width: 17rem;
  padding: var(--sp-4);
  background: linear-gradient(180deg, var(--gi-ink-800), var(--gi-ink-900));
  border: 1px solid var(--gi-line-strong);
  border-radius: 4px;
  box-shadow: 0 18px 40px -18px rgba(0, 0, 0, 0.9);
  pointer-events: none;
  opacity: 0;
  translate: 0 6px;
  transition: opacity var(--t-fast), translate var(--t-fast);
}
.gi-cat-apercu[data-ouvert="1"] { opacity: 1; translate: 0; }
.gi-cat-apercu__tete { display: flex; align-items: center; gap: var(--sp-3); }
.gi-cat-apercu__tete > div { min-width: 0; }
.gi-cat-apercu__img {
  width: 3.4rem; height: 3.4rem;
  flex: none;
  object-fit: contain;
  background: linear-gradient(180deg, var(--gi-ink-750), var(--gi-ink-850));
  border: 1px solid var(--gi-line-soft);
  border-radius: 3px;
}
.gi-cat-apercu__et { display: flex; gap: 1px; margin-top: 0.25rem; }
.gi-cat-apercu__et .gi-etoile { width: 0.8rem; height: 0.8rem; }
.gi-cat-apercu__t {
  font-family: var(--gi-font-title);
  font-size: var(--fs-t-sm);
  font-weight: 600;
  color: var(--gi-sand-50);
}
.gi-cat-apercu__sub {
  margin-top: 0.15rem;
  font-size: var(--fs-xs);
  font-style: italic;
  color: var(--gi-gold-300);
}
.gi-cat-apercu__l {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem 0.7rem;
  margin-top: var(--sp-3);
  font-size: var(--fs-xs);
  color: var(--gi-sand-300);
}
.gi-cat-apercu__l span { display: inline-flex; align-items: center; gap: 0.32rem; }
.gi-cat-apercu__note {
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  font-size: 0.6875rem;
  color: var(--gi-sand-600);
  border-top: 1px solid var(--gi-line-soft);
}

/* ==========================================================================
   C5. ATTENTE, ETAT VIDE, PANNE
   ========================================================================== */

.gi-cat__note { padding: var(--sp-8) var(--sp-4); text-align: center; }
.gi-cat__note-star {
  width: 20px; height: 20px;
  margin: 0 auto var(--sp-4);
  background: var(--gi-gold-600);
  -webkit-mask: var(--gi-star) center / contain no-repeat;
          mask: var(--gi-star) center / contain no-repeat;
}
.gi-cat__note-title {
  font-family: var(--gi-font-display);
  font-size: var(--fs-lg);
  font-weight: 700;
  letter-spacing: var(--ls-caps-md);
  text-transform: uppercase;
  color: var(--gi-sand-200);
}
.gi-cat__note-desc { margin-top: var(--sp-2); font-size: var(--fs-base); color: var(--gi-text-faint); }

.gi-cat__skel {
  aspect-ratio: 0.82;
  width: 100%;
  border-radius: 4px;
  background: linear-gradient(180deg, var(--gi-ink-800), var(--gi-ink-850));
  border: 1px solid var(--gi-line-soft);
  animation: gi-cat-pulse 1.4s var(--ease-in-out) infinite;
}
@keyframes gi-cat-pulse { 50% { opacity: 0.45; } }
@media (prefers-reduced-motion: reduce) {
  .gi-cat__skel { animation: none; }
  .gi-cat-card, .gi-cat-card__portrait img, .gi-cat-apercu { transition: none; }
}

/* ==========================================================================
   C6. PALIERS
   --------------------------------------------------------------------------
   Sous 1080 px, la colonne de filtres repasse au-dessus de la grille : a cette
   largeur elle mangerait plus de place qu'elle n'en rend.
   ========================================================================== */

@media (max-width: 1080px) {
  .gi-cat { grid-template-columns: minmax(0, 1fr); }
  .gi-cat__aside {
    position: static;
    max-height: none;
    order: -1;
  }
  .gi-cat__filters { flex-direction: row; flex-wrap: wrap; gap: var(--sp-3) var(--sp-5); }
  .gi-cat-group { flex: 1 1 16rem; }

  /* SUR PETIT ECRAN, LES GROUPES SE REPLIENT, ET ILS S'OUVRENT FERMES. Quatre
     axes deroules poussaient la grille a plus d'un ecran de haut avant le
     premier portrait. Le script pose `open`, la feuille le retire ici :
     l'attribut reste dans le DOM, donc rouvrir un groupe le rouvre pour de
     bon, et l'ordinateur n'est pas concerne. */
  .gi-cat-group__label {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: 0.35rem 0;
    cursor: pointer;
  }
  .gi-cat-group__label::after {
    content: "";
    width: 9px; height: 9px;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    rotate: 45deg;
    translate: 0 -2px;
    transition: rotate var(--t-fast);
  }
  .gi-cat-group[open] > .gi-cat-group__label::after { rotate: -135deg; translate: 0 2px; }
}
@media (max-width: 1400px) { .gi-cat__grid { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
@media (max-width: 1180px) { .gi-cat__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 860px)  { .gi-cat__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .gi-cat__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

@media (max-width: 760px) {
  .gi-cat { --cat-tuile: 8rem; --cat-gap: var(--sp-3); }
  .gi-cat-card__ico li { width: 1.4rem; height: 1.4rem; font-size: 0.78rem; }
}
@media (max-width: 400px) {
  .gi-cat { --cat-tuile: 6.8rem; }
  .gi-cat-card__name { padding: var(--sp-2); font-size: var(--fs-sm); }
}
