/* ==========================================================================
   FASTGF — FEUILLE DE STYLE UNIQUE
   --------------------------------------------------------------------------
   IDENTITE. Les couleurs viennent du logo, et de nulle part ailleurs : le
   cristal violet, le G bleu, les lettres blanches biseautees. Un site de
   serveur Minecraft qui reprend la palette de Hypixel ou de Funcraft n'a pas
   d'identite ; celui-ci se reconnait au premier coup d'oeil parce qu'il est
   litteralement fait de la matiere du logo.

   LE RELIEF EST LA CLE. Les lettres du logo sont EXTRUDEES : une face claire,
   une tranche sombre en bas a droite. Toutes les surfaces du site reprennent ce
   biseau (--biseau), et c'est ce qui donne l'aspect "3D Minecraft" sans une
   seule image de texture, donc sans un octet de plus a telecharger.

   SOBRIETE. Fond presque noir, deux accents seulement, beaucoup de vide. Les
   sites de serveurs FR sont satures de degrades et de neon ; celui-ci mise sur
   le contraste et le mouvement. C'est ce qui le fera passer pour "pro".
   ========================================================================== */

/* ==================== JETONS ==================== */

:root {
    /* Fonds — du plus profond au plus clair */
    --noir:        #060510;
    --fond:        #090814;
    --fond-2:      #0D0B1A;
    --surface:     #141127;
    --surface-2:   #1C1832;
    --surface-3:   #262040;

    /* Accents, tires du logo */
    --violet:        #A855F7;
    --violet-clair:  #C9A5FF;
    --violet-fonce:  #6D28D9;
    --violet-nuit:   #3B1D7A;
    --bleu:          #2F7DF6;
    --bleu-clair:    #6FA8FF;
    --or:            #F2B33D;
    --vert:          #34D399;
    --rouge:         #F05B6B;

    /* Texte */
    --texte:       #EFEDF8;
    --texte-doux:  #A8A2C4;
    --texte-faible:#6B6588;

    /* Bordures */
    --bord:        rgba(168, 85, 247, .14);
    --bord-fort:   rgba(168, 85, 247, .34);
    --bord-clair:  rgba(255, 255, 255, .07);

    /* BISEAU : la signature visuelle. Lumiere en haut a gauche, tranche
       sombre en bas a droite — exactement l'extrusion des lettres du logo. */
    --biseau: inset 1px 1px 0 rgba(255,255,255,.10),
              inset -1px -2px 0 rgba(0,0,0,.55);
    --biseau-fort: inset 1.5px 1.5px 0 rgba(255,255,255,.16),
                   inset -2px -3px 0 rgba(0,0,0,.65);

    /* Ombres */
    --ombre-douce: 0 2px 8px rgba(0,0,0,.4);
    --ombre:       0 18px 44px -18px rgba(0,0,0,.85);
    --ombre-haute: 0 40px 90px -30px rgba(0,0,0,.95);
    --lueur:       0 0 0 1px var(--bord-fort), 0 24px 60px -24px rgba(168,85,247,.65);

    /* Rythme */
    --r-s:  8px;
    --r-m:  14px;
    --r-l:  22px;
    --r-xl: 32px;

    /* Courbes. La "douce" est celle d'iOS : elle demarre vite et se pose,
       c'est ce qui fait qu'un mouvement parait pilote et non calcule. */
    --douce:   cubic-bezier(.22, 1, .36, 1);
    --ressort: cubic-bezier(.34, 1.56, .64, 1);
    --net:     cubic-bezier(.4, 0, .2, 1);

    --largeur: 1240px;
    --nav-h: 72px;
}

/* ==================== BASES ==================== */

*, *::before, *::after { box-sizing: border-box; }

html {
    scroll-behavior: smooth;
    /* La nav est fixe : sans ca, une ancre place le titre dessous. */
    scroll-padding-top: calc(var(--nav-h) + 24px);
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    background: var(--fond);
    color: var(--texte);
    font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
    font-size: 16px;
    line-height: 1.65;
    letter-spacing: -.005em;
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
}

/* Le fond n'est jamais uni : deux halos fixes, tres larges et tres faibles,
   donnent une profondeur que l'oeil percoit sans pouvoir la nommer. */
body::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: -2;
    background:
        radial-gradient(60rem 40rem at 12% -8%,  rgba(109,40,217,.26), transparent 62%),
        radial-gradient(52rem 38rem at 92% 12%,  rgba(47,125,246,.16), transparent 60%),
        radial-gradient(70rem 50rem at 50% 118%, rgba(168,85,247,.14), transparent 65%);
    pointer-events: none;
}

/* Grille isometrique tres discrete : le rappel Minecraft, a 3 % d'opacite.
   Au-dela, ca devient un fond d'ecran ; en dessous, ca ne sert a rien. */
body::after {
    content: '';
    position: fixed;
    inset: 0;
    z-index: -1;
    background-image:
        linear-gradient(rgba(168,85,247,.045) 1px, transparent 1px),
        linear-gradient(90deg, rgba(168,85,247,.045) 1px, transparent 1px);
    background-size: 56px 56px;
    mask-image: radial-gradient(90rem 60rem at 50% 0%, #000 20%, transparent 78%);
    -webkit-mask-image: radial-gradient(90rem 60rem at 50% 0%, #000 20%, transparent 78%);
    pointer-events: none;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; }

::selection { background: rgba(168,85,247,.35); color: #fff; }

/* Barre de defilement assortie : un detail que personne ne remarque, mais
   dont l'absence fait "site pas fini". */
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: var(--noir); }
::-webkit-scrollbar-thumb {
    background: linear-gradient(var(--violet-fonce), var(--violet-nuit));
    border-radius: 99px;
    border: 3px solid var(--noir);
}
::-webkit-scrollbar-thumb:hover { background: var(--violet); }

/* Accessibilite : un anneau visible au clavier, sur TOUT ce qui se focalise. */
:focus-visible {
    outline: 2px solid var(--violet-clair);
    outline-offset: 3px;
    border-radius: 4px;
}

/* ==================== TYPOGRAPHIE ==================== */

h1, h2, h3, h4, .display {
    font-family: 'Outfit', 'Inter', system-ui, sans-serif;
    font-weight: 800;
    line-height: 1.05;
    letter-spacing: -.035em;
    margin: 0;
}

h1 { font-size: clamp(2.6rem, 7.5vw, 5.4rem); }
h2 { font-size: clamp(1.9rem, 4.4vw, 3.1rem); }
h3 { font-size: clamp(1.15rem, 2.2vw, 1.5rem); letter-spacing: -.02em; }

p { margin: 0 0 1rem; color: var(--texte-doux); }
p:last-child { margin-bottom: 0; }

.eyebrow {
    display: inline-flex;
    align-items: center;
    gap: .55rem;
    font-size: .7rem;
    font-weight: 700;
    letter-spacing: .22em;
    text-transform: uppercase;
    color: var(--violet-clair);
    margin-bottom: 1.1rem;
}
.eyebrow::before {
    content: '';
    width: 26px; height: 2px;
    background: linear-gradient(90deg, var(--violet), transparent);
}

/* Le degrade du logo, applique au texte. Reserve aux mots qui comptent :
   l'utiliser partout le viderait de son sens. */
.grad {
    background: linear-gradient(115deg, var(--violet-clair) 0%, var(--violet) 30%, var(--bleu) 72%, var(--bleu-clair) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.doux  { color: var(--texte-doux); }
.faible{ color: var(--texte-faible); }
.lead  { font-size: clamp(1.02rem, 1.7vw, 1.2rem); line-height: 1.7; }

/* ==================== MISE EN PAGE ==================== */

.wrap {
    width: min(100% - 2.5rem, var(--largeur));
    margin-inline: auto;
}

.section { padding: clamp(4.5rem, 10vw, 8.5rem) 0; position: relative; }
.section--serre { padding: clamp(3rem, 6vw, 5rem) 0; }

.entete-section { max-width: 62ch; margin-bottom: clamp(2.5rem, 5vw, 4rem); }
.entete-section.centre { margin-inline: auto; text-align: center; }
.entete-section.centre .eyebrow::before { display: none; }
.entete-section p { margin-top: 1.1rem; font-size: 1.05rem; }

.grille { display: grid; gap: 1.25rem; }
.g-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }
.g-3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr)); }
.g-4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)); }

/* ==================== NAVIGATION ==================== */

.nav {
    position: fixed;
    top: 0; left: 0; right: 0;
    height: var(--nav-h);
    z-index: 100;
    display: flex;
    align-items: center;
    transition: background .4s var(--net), backdrop-filter .4s var(--net),
                border-color .4s var(--net), height .4s var(--net);
    border-bottom: 1px solid transparent;
}
/* Au repos la nav flotte sur le hero ; des le premier defilement elle se
   solidifie. Elle ne masque donc jamais l'image d'accueil. */
.nav.collee {
    height: 62px;
    background: rgba(9, 8, 20, .82);
    backdrop-filter: blur(18px) saturate(150%);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
    border-bottom-color: var(--bord);
}

.nav .wrap { display: flex; align-items: center; gap: 1.5rem; width: min(100% - 2rem, 1400px); }

.marque { display: flex; align-items: center; gap: .7rem; margin-right: auto; }
.marque img {
    width: 40px; height: 40px;
    filter: drop-shadow(0 3px 10px rgba(168,85,247,.55));
    transition: transform .5s var(--ressort);
}
.marque:hover img { transform: rotate(-8deg) scale(1.12); }
.marque span {
    font-family: 'Outfit', sans-serif;
    font-weight: 800;
    font-size: 1.22rem;
    letter-spacing: -.03em;
}

.liens { display: flex; align-items: center; gap: .25rem; }
.liens a {
    position: relative;
    padding: .5rem .85rem;
    font-size: .9rem;
    font-weight: 600;
    color: var(--texte-doux);
    border-radius: var(--r-s);
    transition: color .25s var(--net), background .25s var(--net);
}
.liens a:hover, .liens a.actif { color: var(--texte); background: rgba(168,85,247,.10); }
/* Le trait ne grandit pas depuis la gauche mais depuis le centre : le
   mouvement est symetrique, donc plus calme. */
.liens a::after {
    content: '';
    position: absolute;
    left: 50%; bottom: 2px;
    width: 0; height: 2px;
    background: linear-gradient(90deg, var(--violet), var(--bleu));
    border-radius: 2px;
    transform: translateX(-50%);
    transition: width .35s var(--douce);
}
.liens a:hover::after, .liens a.actif::after { width: 42%; }

.burger {
    display: none;
    width: 42px; height: 42px;
    background: var(--surface);
    border: 1px solid var(--bord);
    border-radius: var(--r-s);
    box-shadow: var(--biseau);
    cursor: pointer;
    padding: 0;
    place-items: center;
}
.burger span {
    display: block;
    width: 18px; height: 2px;
    background: var(--texte);
    border-radius: 2px;
    transition: transform .35s var(--douce), opacity .2s;
}
.burger span + span { margin-top: 4px; }
.burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* ==================== BOUTONS ==================== */

.btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .55rem;
    padding: .82rem 1.6rem;
    font-family: 'Outfit', sans-serif;
    font-size: .95rem;
    font-weight: 700;
    letter-spacing: -.01em;
    border: 1px solid var(--bord-fort);
    border-radius: var(--r-m);
    background: var(--surface-2);
    box-shadow: var(--biseau), var(--ombre-douce);
    cursor: pointer;
    overflow: hidden;
    isolation: isolate;
    transition: transform .35s var(--ressort), box-shadow .35s var(--douce),
                border-color .3s, background .3s;
    will-change: transform;
}
.btn:hover { border-color: var(--violet); box-shadow: var(--biseau), var(--lueur); }
.btn:active { filter: brightness(.88); }

.btn--primaire {
    background: linear-gradient(135deg, var(--violet) 0%, var(--violet-fonce) 55%, var(--bleu) 130%);
    border-color: transparent;
    color: #fff;
    box-shadow: var(--biseau-fort), 0 14px 34px -14px rgba(168,85,247,.9);
}
.btn--primaire:hover { box-shadow: var(--biseau-fort), 0 22px 50px -16px rgba(168,85,247,1); }

/* Reflet qui balaie le bouton au survol. Une seule passe, pas une boucle :
   une brillance qui tourne en permanence devient un clignotant. */
.btn--primaire::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(105deg, transparent 35%, rgba(255,255,255,.32) 50%, transparent 65%);
    transform: translateX(-130%);
    transition: transform .75s var(--douce);
}
.btn--primaire:hover::before { transform: translateX(130%); }

.btn--fantome { background: rgba(255,255,255,.03); }
.btn--grand { padding: 1.05rem 2.2rem; font-size: 1.03rem; }
.btn--plein { width: 100%; }

/* ==================== CARTE 3D — LE COEUR DU SITE ====================
   Chaque carte s'incline vers le curseur et allume une lueur exactement
   sous lui. Les deux effets sont pilotes par deux variables posees en JS
   (--mx / --my en pourcentage, --rx / --ry en degres), ce qui evite de
   toucher au style en ligne a chaque mouvement de souris.

   Le relief tient a trois couches superposees :
     1. le biseau, qui donne l'epaisseur,
     2. le halo sous le curseur, qui donne la matiere,
     3. le liseré qui s'allume du cote eclaire, qui donne l'arete.
   ================================================================== */

.carte {
    position: relative;
    padding: 1.9rem;
    background: linear-gradient(160deg, var(--surface) 0%, var(--fond-2) 100%);
    border: 1px solid var(--bord);
    border-radius: var(--r-l);
    box-shadow: var(--biseau), var(--ombre);
    overflow: hidden;
    isolation: isolate;
    transition: transform .5s var(--douce), border-color .4s, box-shadow .5s var(--douce);
}

/* .carte3d ne porte plus l'inclinaison : .bulle le fait desormais pour TOUT
   le site. Il ne reste ici que la profondeur interne — le contenu qui flotte
   au-dessus du fond de la carte. */
.carte3d {
    transform-style: preserve-3d;
}
.carte3d > * { transform: translateZ(28px); }

/* LE HALO SOUS LE CURSEUR. C'est lui que l'oeil lit comme "de la lumiere
   sur une surface", et donc comme du volume. */
.carte::before {
    content: '';
    position: absolute;
    inset: -1px;
    z-index: -1;
    border-radius: inherit;
    background: radial-gradient(
        22rem 22rem at var(--mx, 50%) var(--my, 50%),
        rgba(168,85,247,.28), rgba(47,125,246,.10) 42%, transparent 68%);
    opacity: 0;
    transition: opacity .45s var(--net);
}
.carte:hover::before { opacity: 1; }

/* L'arete eclairee : un trait fin qui suit le curseur sur le bord. */
.carte::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    padding: 1px;
    background: radial-gradient(
        14rem 14rem at var(--mx, 50%) var(--my, 50%),
        rgba(201,165,255,.85), transparent 60%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
            mask-composite: exclude;
    opacity: 0;
    transition: opacity .45s var(--net);
}
.carte:hover::after { opacity: 1; }

.carte:hover { border-color: var(--bord-fort); box-shadow: var(--biseau-fort), var(--ombre-haute); }

/* ==================== MODES DE JEU ==================== */

.modes { display: grid; gap: 1.15rem; }

.mode {
    --teinte: var(--violet);
    position: relative;
    display: grid;
    grid-template-columns: auto auto 1fr auto;
    align-items: center;
    gap: 1.5rem;
    padding: 1.5rem 1.8rem;
    background: linear-gradient(150deg, var(--surface) 0%, var(--fond-2) 92%);
    border: 1px solid var(--bord);
    border-radius: var(--r-l);
    box-shadow: var(--biseau), var(--ombre);
    cursor: pointer;
    overflow: hidden;
    isolation: isolate;
    text-align: left;
    width: 100%;
    transition: transform .5s var(--douce), border-color .4s, box-shadow .5s var(--douce);
}
.mode::before {
    content: '';
    position: absolute;
    inset: -1px;
    z-index: -1;
    border-radius: inherit;
    background: radial-gradient(26rem 20rem at var(--mx, 50%) var(--my, 50%),
                color-mix(in srgb, var(--teinte) 30%, transparent), transparent 64%);
    opacity: 0;
    transition: opacity .45s var(--net);
}
.mode:hover::before, .mode.ouvert::before { opacity: 1; }
.mode:hover { border-color: color-mix(in srgb, var(--teinte) 45%, transparent); }
.mode.ouvert {
    border-color: color-mix(in srgb, var(--teinte) 50%, transparent);
    /* Ouvert, l'en-tete et le panneau ne font plus qu'un bloc : on aplatit
       les coins du bas, sinon un liseré arrondi coupe la carte en deux et
       le panneau parait flotter en dessous. */
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
    border-bottom-color: transparent;
}
/* Et surtout : la carte ne se souleve plus une fois ouverte. Un panneau
   ancre a un en-tete qui recule de 4 px laisse un trou visible entre les
   deux. */
.mode.ouvert:hover { border-color: color-mix(in srgb, var(--teinte) 50%, transparent); }

/* Le rang, en gros chiffre fantome. Il donne l'ordre voulu — Manhunt,
   FastPvp, RushFFA, TeamSpell — sans avoir a l'ecrire. */
.mode__rang {
    font-family: 'Outfit', sans-serif;
    font-size: 2.4rem;
    font-weight: 800;
    line-height: 1;
    color: transparent;
    -webkit-text-stroke: 1.5px color-mix(in srgb, var(--teinte) 55%, transparent);
    transition: -webkit-text-stroke-color .4s, transform .5s var(--ressort);
}
.mode:hover .mode__rang { transform: scale(1.1) translateX(2px); }

/* L'ICONE. Elle flotte en permanence, tres lentement ; au survol elle se
   redresse, grossit et prend la lumiere. C'est le "logo qui s'anime" :
   l'animation dit "ce bloc est vivant", pas "regardez-moi". */
.mode__icone {
    width: 72px; height: 72px;
    display: grid;
    place-items: center;
    border-radius: var(--r-m);
    background: linear-gradient(150deg,
        color-mix(in srgb, var(--teinte) 22%, var(--surface-2)),
        var(--fond-2));
    border: 1px solid color-mix(in srgb, var(--teinte) 30%, transparent);
    box-shadow: var(--biseau);
    flex-shrink: 0;
    animation: flotte 6s ease-in-out infinite;
    transition: transform .55s var(--ressort), box-shadow .5s, border-color .4s;
}
.mode__icone svg { width: 40px; height: 40px; }
.mode:hover .mode__icone {
    transform: rotate(-6deg) scale(1.1);
    translate: 0 -6px;
    border-color: var(--teinte);
    box-shadow: var(--biseau-fort), 0 16px 34px -12px color-mix(in srgb, var(--teinte) 80%, transparent);
}

/* LE FLOTTEMENT PASSE PAR `translate`, PAS PAR `transform`, ET C'EST LA
   RAISON POUR LAQUELLE IL FONCTIONNE.
   Une animation @keyframes qui ecrit `transform` gagne toujours contre une
   regle :hover qui ecrit `transform` — la valeur animee ecrase la valeur
   calculee. L'icone flottait donc, mais ne reagissait plus du tout au survol.
   `translate` est une propriete distincte, composee par le navigateur AVEC
   `transform` : les deux effets s'additionnent au lieu de se disputer. */
@keyframes flotte {
    0%, 100% { translate: 0 0; }
    50%      { translate: 0 -7px; }
}

.mode__titre {
    display: flex;
    align-items: center;
    gap: .7rem;
    flex-wrap: wrap;
    font-family: 'Outfit', sans-serif;
    font-size: clamp(1.2rem, 2.5vw, 1.55rem);
    font-weight: 800;
    letter-spacing: -.03em;
    margin: 0 0 .3rem;
}
.mode__titre, .mode__accroche, .mode__corps { display: block; }
.mode__accroche { color: var(--texte-doux); font-size: .95rem; margin: 0; }

/* L'etiquette d'etat. "OUVERT" en vert se lit avant le nom du mode : c'est
   la premiere question que se pose un visiteur devant une liste de modes. */
.etat {
    padding: .18rem .55rem;
    font-family: 'Inter', sans-serif;
    font-size: .64rem;
    font-weight: 800;
    letter-spacing: .12em;
    border-radius: 5px;
    white-space: nowrap;
}
.etat--ouvert { color: var(--vert);        background: rgba(52,211,153,.14); }
.etat--beta   { color: var(--violet-clair);background: rgba(168,85,247,.16); }
.etat--bientot{ color: var(--or);          background: rgba(242,179,61,.13); }

.mode__fleche {
    width: 38px; height: 38px;
    display: grid; place-items: center;
    border-radius: 50%;
    background: rgba(255,255,255,.04);
    border: 1px solid var(--bord);
    color: var(--texte-doux);
    transition: transform .45s var(--ressort), background .3s, color .3s;
}
.mode:hover .mode__fleche { background: var(--teinte); color: #fff; transform: scale(1.12); }
.mode.ouvert .mode__fleche { transform: rotate(180deg); background: var(--teinte); color: #fff; }

/* Le panneau qui se deplie. La hauteur est animee par grid-template-rows :
   c'est la seule facon d'animer "auto" sans mesurer en JS, donc sans saut
   quand le texte change de longueur ou que la fenetre est redimensionnee. */
.mode__detail {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows .55s var(--douce), opacity .4s var(--net);
    opacity: 0;
}
.mode__detail.ouvert { grid-template-rows: 1fr; opacity: 1; }
.mode__detail > div { overflow: hidden; }
.mode__detail-inner {
    display: grid;
    grid-template-columns: 1.25fr .95fr;
    gap: 2rem;
    padding: 1.7rem 1.8rem 1.9rem;
    margin-top: -1px;
    background: linear-gradient(var(--fond-2), var(--noir));
    border: 1px solid var(--bord);
    border-top: 0;
    border-radius: 0 0 var(--r-l) var(--r-l);
}

.pastilles { display: flex; flex-wrap: wrap; gap: .45rem; margin-top: 1.1rem; }
.pastille {
    padding: .28rem .7rem;
    font-size: .74rem;
    font-weight: 600;
    letter-spacing: .01em;
    color: var(--texte-doux);
    background: rgba(255,255,255,.045);
    border: 1px solid var(--bord-clair);
    border-radius: 99px;
}

.liste-points { list-style: none; margin: 1.1rem 0 0; padding: 0; display: grid; gap: .7rem; }
.liste-points li {
    position: relative;
    padding-left: 1.6rem;
    color: var(--texte-doux);
    font-size: .94rem;
    line-height: 1.6;
}
.liste-points li::before {
    content: '';
    position: absolute;
    left: 0; top: .55em;
    width: 8px; height: 8px;
    background: var(--teinte, var(--violet));
    /* Losange : le cristal du logo, en miniature. */
    transform: rotate(45deg);
    border-radius: 2px;
    box-shadow: 0 0 10px color-mix(in srgb, var(--teinte, var(--violet)) 80%, transparent);
}
.liste-points strong { color: var(--texte); font-weight: 650; }

/* Emplacement de la capture du mode. Tant qu'aucune image n'est fournie,
   le bloc affiche un cristal anime : le site ne parait jamais inachevee. */
.visuel-mode {
    position: relative;
    aspect-ratio: 4 / 3;
    border-radius: var(--r-m);
    background: radial-gradient(120% 100% at 50% 0%,
                color-mix(in srgb, var(--teinte) 24%, var(--surface)), var(--noir));
    border: 1px solid color-mix(in srgb, var(--teinte) 28%, transparent);
    box-shadow: var(--biseau);
    overflow: hidden;
    display: grid;
    place-items: center;
}
.visuel-mode img { width: 100%; height: 100%; object-fit: cover; }
.visuel-mode__vide { display: grid; place-items: center; gap: .8rem; text-align: center; padding: 1rem; }
.visuel-mode__vide svg { width: 74px; height: 74px; animation: pivote 9s linear infinite; }
.visuel-mode__vide span { font-size: .74rem; color: var(--texte-faible); letter-spacing: .06em; }
@keyframes pivote {
    from { transform: rotateY(0deg); }
    to   { transform: rotateY(360deg); }
}

/* ==================== HERO ==================== */

.hero {
    position: relative;
    min-height: 100svh;
    display: grid;
    align-items: center;
    padding: calc(var(--nav-h) + 3rem) 0 4rem;
    overflow: hidden;
}

.hero__grille {
    display: grid;
    grid-template-columns: 1.15fr .85fr;
    gap: clamp(2rem, 5vw, 4.5rem);
    align-items: center;
}

/* ==================== L'ENTREE DE PAGE ====================

   Ce qui est deja a l'ecran au chargement n'apparaissait jamais : le module
   d'apparition ne surveille que le defilement, et le haut de l'accueil est
   par definition deja visible. En revenant du wiki, la page s'affichait donc
   d'un bloc, immobile.

   Le flou qui se leve (`filter: blur`) fait plus que le glissement seul : il
   donne l'impression d'une mise au point, et c'est ce qui distingue une
   arrivee d'un simple deplacement.

   `backwards` retient l'etat initial pendant le delai : sans lui, chaque
   element serait affiche en clair puis disparaitrait pour reapparaitre.

   La classe est retiree par le JS au bout de 1,8 s. Une animation qui reste
   accrochee bloquerait `transform`, et le champ magnetique n'aurait plus la
   main sur ces memes elements. */
@keyframes entree-monte {
    from { opacity: 0; transform: translateY(28px); filter: blur(7px); }
    to   { opacity: 1; transform: none; filter: none; }
}
@keyframes entree-souffle {
    from { opacity: 0; transform: scale(.9) translateY(14px); filter: blur(10px); }
    to   { opacity: 1; transform: none; filter: none; }
}
@keyframes entree-glisse {
    from { opacity: 0; transform: translateY(-14px); }
    to   { opacity: 1; transform: none; }
}

.entree .nav { animation: entree-glisse .6s var(--douce) backwards; }

/* Colonne de gauche : chaque bloc arrive l'un apres l'autre, de haut en bas.
   80 ms d'ecart — assez pour lire une cascade, trop peu pour attendre. */
.entree .hero__grille > div:first-child > * {
    animation: entree-monte .8s var(--douce) backwards;
}
.entree .hero__grille > div:first-child > *:nth-child(1) { animation-delay: .06s; }
.entree .hero__grille > div:first-child > *:nth-child(2) { animation-delay: .14s; }
.entree .hero__grille > div:first-child > *:nth-child(3) { animation-delay: .22s; }
.entree .hero__grille > div:first-child > *:nth-child(4) { animation-delay: .30s; }
.entree .hero__grille > div:first-child > *:nth-child(5) { animation-delay: .38s; }
.entree .hero__grille > div:first-child > *:nth-child(6) { animation-delay: .46s; }

/* Colonne de droite : le cristal. Il gonfle au lieu de monter — c'est un
   objet, pas une ligne de texte, et il arrive en dernier. */
.entree .hero__grille > div:nth-child(2) {
    animation: entree-souffle .95s var(--douce) .28s backwards;
}

/* Les autres pages n'ont pas de hero : on anime leur premier ecran. */
.entree main > .wrap > .entete-section,
.entree main > .wrap > *:first-child {
    animation: entree-monte .75s var(--douce) .08s backwards;
}

.hero h1 { margin-bottom: 1.6rem; }
.hero .lead { max-width: 52ch; margin-bottom: 2.2rem; }

.hero__actions { display: flex; flex-wrap: wrap; gap: .9rem; align-items: center; }

/* L'ADRESSE DU SERVEUR, copiable en un clic. C'est l'element le plus
   utile de la page : un visiteur vient chercher ca, et rien d'autre. */
.ip {
    display: inline-flex;
    align-items: center;
    gap: .9rem;
    padding: .7rem .8rem .7rem 1.3rem;
    background: var(--surface);
    border: 1px solid var(--bord-fort);
    border-radius: var(--r-m);
    box-shadow: var(--biseau);
    cursor: pointer;
    font-family: 'Outfit', monospace;
    font-weight: 700;
    font-size: 1.02rem;
    letter-spacing: -.01em;
    transition: transform .35s var(--ressort), border-color .3s, box-shadow .35s;
}
.ip:hover { border-color: var(--violet); box-shadow: var(--biseau), var(--lueur); }
.ip__badge {
    padding: .3rem .65rem;
    font-family: 'Inter', sans-serif;
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .04em;
    color: var(--violet-clair);
    background: rgba(168,85,247,.14);
    border-radius: var(--r-s);
    transition: background .3s, color .3s;
}
.ip.copie .ip__badge { background: rgba(52,211,153,.18); color: var(--vert); }

/* Statut serveur : une pastille qui bat. Le battement ne s'arrete jamais —
   c'est ce qui distingue "en ligne maintenant" de "capture d'ecran". */
.statut {
    display: inline-flex;
    align-items: center;
    gap: .6rem;
    padding: .4rem .95rem .4rem .8rem;
    font-size: .84rem;
    font-weight: 600;
    color: var(--texte-doux);
    background: rgba(255,255,255,.035);
    border: 1px solid var(--bord-clair);
    border-radius: 99px;
}
.pouls {
    position: relative;
    width: 8px; height: 8px;
    border-radius: 50%;
    background: var(--texte-faible);
    flex-shrink: 0;
}
.statut.enligne .pouls { background: var(--vert); }
.statut.enligne .pouls::after {
    content: '';
    position: absolute;
    inset: -4px;
    border-radius: 50%;
    border: 1px solid var(--vert);
    animation: onde 2.2s ease-out infinite;
}
.statut.horsligne .pouls { background: var(--rouge); }
@keyframes onde {
    0%   { transform: scale(.6); opacity: .9; }
    100% { transform: scale(1.9); opacity: 0; }
}

/* LE CRISTAL DU HERO. Un vrai objet 3D en CSS : huit faces triangulaires
   assemblees en bipyramide, comme le logo. Il tourne lentement et suit la
   souris. Aucune image, aucune bibliotheque : il pese zero octet et reste
   net sur un ecran 4K. */
.scene {
    perspective: 1200px;
    display: grid;
    place-items: center;
    min-height: 380px;
}
/* Deux couches, et c'est indispensable : l'enveloppe porte l'inclinaison vers
   la souris, le cristal porte la rotation continue. Sur un seul element, la
   propriete `transform` ne peut pas etre a la fois animee par @keyframes et
   ecrite par le JS — l'animation gagne toujours, et le suivi du curseur
   n'aurait aucun effet visible. */
.cristal-enveloppe {
    /* `position: relative` est INDISPENSABLE : le logo qui se pose au centre
       est en `position: absolute`, et sans reference ici il se calait sur le
       hero entier — c'est-a-dire en haut a gauche de l'ecran, hors du cristal.
       Le defaut ne se voit qu'au rendu, jamais a la lecture du HTML. */
    position: relative;
    transform-style: preserve-3d;
    transform: rotateY(var(--souris-x, 0deg)) rotateX(var(--souris-y, 0deg));
    transition: transform .5s var(--net);
    display: grid;
    place-items: center;
}
.cristal {
    position: relative;
    width: 190px; height: 190px;
    transform-style: preserve-3d;
    animation: tourne 22s linear infinite;
}
@keyframes tourne {
    from { transform: rotateX(-14deg) rotateY(0deg); }
    to   { transform: rotateX(-14deg) rotateY(360deg); }
}
.cristal__face {
    position: absolute;
    inset: 0;
    background: linear-gradient(160deg, var(--violet-clair), var(--violet) 42%, var(--violet-nuit));
    clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
    opacity: .88;
    box-shadow: inset 0 0 60px rgba(255,255,255,.22);
}
.cristal__face:nth-child(1) { transform: rotateY(0deg)   translateZ(58px); }
.cristal__face:nth-child(2) { transform: rotateY(60deg)  translateZ(58px); }
.cristal__face:nth-child(3) { transform: rotateY(120deg) translateZ(58px); }
.cristal__face:nth-child(4) { transform: rotateY(180deg) translateZ(58px); }
.cristal__face:nth-child(5) { transform: rotateY(240deg) translateZ(58px); }
.cristal__face:nth-child(6) { transform: rotateY(300deg) translateZ(58px); }

.cristal__halo {
    position: absolute;
    inset: -70%;
    background: radial-gradient(circle, rgba(168,85,247,.42), transparent 62%);
    filter: blur(28px);
    animation: respire 5s ease-in-out infinite;
    pointer-events: none;
}
@keyframes respire {
    0%, 100% { opacity: .55; transform: scale(.94); }
    50%      { opacity: 1;   transform: scale(1.08); }
}

.logo-hero {
    position: absolute;
    width: 168px;
    filter: drop-shadow(0 22px 45px rgba(0,0,0,.85));
    animation: flotte 7s ease-in-out infinite;
    /* `z-index` NE SUFFIT PAS ICI, et c'est le piege classique du 3D en CSS.
       Dans un contexte `preserve-3d`, l'ordre de peinture ne vient plus de
       z-index mais de la POSITION EN PROFONDEUR. Les faces du cristal sont a
       translateZ(58px) : le logo, reste a 0, se retrouvait donc a l'interieur
       du cristal, a moitie recouvert par les faces avant.
       On le pousse devant, en profondeur reelle. */
    transform: translateZ(112px);
}

/* Cubes voxel qui derivent en fond. Le rappel Minecraft le plus direct
   du site, et le seul assume comme tel. */
.voxels { position: absolute; inset: 0; pointer-events: none; overflow: hidden; z-index: 0; }
.voxel {
    position: absolute;
    width: 34px; height: 34px;
    background: linear-gradient(150deg, rgba(168,85,247,.28), rgba(47,125,246,.14));
    border: 1px solid rgba(201,165,255,.24);
    border-radius: 5px;
    box-shadow: var(--biseau);
    animation: derive linear infinite;
    opacity: 0;
}
@keyframes derive {
    0%   { transform: translateY(30px) rotate(0deg)   scale(.7); opacity: 0; }
    12%  { opacity: .8; }
    88%  { opacity: .55; }
    100% { transform: translateY(-118vh) rotate(320deg) scale(1.05); opacity: 0; }
}

/* ==================== CHIFFRES ==================== */

.chiffres {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 1rem;
    margin-top: 2.6rem;
    padding-top: 2.2rem;
    border-top: 1px solid var(--bord-clair);
}
.chiffre__val {
    font-family: 'Outfit', sans-serif;
    font-size: clamp(1.7rem, 3.6vw, 2.5rem);
    font-weight: 800;
    letter-spacing: -.04em;
    line-height: 1;
}
.chiffre__lib {
    margin-top: .35rem;
    font-size: .78rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--texte-faible);
}

/* ==================== GRADES / BOUTIQUE ==================== */

.grade { display: flex; flex-direction: column; padding: 2rem 1.8rem; }
.grade--vedette {
    border-color: var(--bord-fort);
    background: linear-gradient(165deg, var(--surface-2), var(--fond-2) 70%);
}
.grade__ruban {
    position: absolute;
    top: 1.2rem; right: -2.6rem;
    padding: .3rem 3rem;
    font-size: .68rem;
    font-weight: 800;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: #fff;
    background: linear-gradient(90deg, var(--violet), var(--bleu));
    transform: rotate(38deg);
    box-shadow: var(--ombre-douce);
}
.grade__nom {
    font-family: 'Outfit', sans-serif;
    font-size: 1.5rem;
    font-weight: 800;
    letter-spacing: -.03em;
}
.grade__prix { display: flex; align-items: baseline; gap: .6rem; margin: 1rem 0 .3rem; }
.grade__prix b {
    font-family: 'Outfit', sans-serif;
    font-size: 2.3rem;
    font-weight: 800;
    letter-spacing: -.045em;
}
.grade__barre { color: var(--texte-faible); text-decoration: line-through; font-size: 1rem; }
.grade__remise {
    padding: .2rem .55rem;
    font-size: .72rem;
    font-weight: 800;
    color: var(--vert);
    background: rgba(52,211,153,.13);
    border-radius: var(--r-s);
}
.grade__duree { font-size: .82rem; color: var(--texte-faible); margin-bottom: 1.4rem; }
.grade__liste { list-style: none; margin: 0 0 1.8rem; padding: 0; display: grid; gap: .65rem; flex: 1; }
.grade__liste li {
    position: relative;
    padding-left: 1.7rem;
    font-size: .9rem;
    color: var(--texte-doux);
    line-height: 1.55;
}
.grade__liste li::before {
    content: '';
    position: absolute;
    left: 0; top: .42em;
    width: 15px; height: 9px;
    border-left: 2px solid var(--vert);
    border-bottom: 2px solid var(--vert);
    transform: rotate(-45deg) scale(.85);
    transform-origin: center;
}
/* ==================== AVIS D'ACHAT ====================

   Pose sous la liste d'un article apres verification du pseudo. Trois tons,
   trois messages differents — et surtout, un seul BLOQUE :

     bloque   l'article est definitif et deja possede : le bouton devient
              inerte, parce que ce clic ne peut aboutir a rien de bon ;
     cumul    VIP ou VIP+ deja actif : l'achat ajoute des jours, on le dit ;
     inutile  l'achat marcherait mais n'apporterait rien (VIP par-dessus un
              VIP+). On previent, on ne bloque pas : c'est son argent, et il
              peut vouloir offrir le grade a quelqu'un d'autre. */
.avis-achat {
    margin: 0 0 1.1rem;
    padding: .8rem 1rem;
    border-radius: var(--r-s);
    font-size: .87rem;
    line-height: 1.5;
    border: 1px solid transparent;
}
.avis-achat--bloque {
    color: #FFC9CC;
    background: rgba(229,72,77,.12);
    border-color: rgba(229,72,77,.34);
}
.avis-achat--cumul {
    color: #BFF3D8;
    background: rgba(52,211,153,.11);
    border-color: rgba(52,211,153,.30);
}
.avis-achat--inutile {
    color: #FFE3B0;
    background: rgba(242,179,61,.11);
    border-color: rgba(242,179,61,.30);
}

/* La carte d'un article deja possede s'efface : elle reste lisible, mais ne
   se dispute plus l'attention avec celles qu'on peut encore acheter. */
.carte.possede { opacity: .74; }
.carte.possede:hover { opacity: 1; }

.btn--inerte {
    pointer-events: none;
    filter: grayscale(1);
    opacity: .55;
}

/* Une ligne de BONUS : ce n'est pas un avantage de plus dans la liste, c'est
   une offre liee a un autre article. Le violet et l'etoile la detachent des
   coches vertes, sans quoi elle se lisait comme un point ordinaire. */
.grade__liste li.bonus { color: var(--texte); }
.grade__liste li.bonus::before {
    content: '✦';
    border: 0;
    transform: none;
    top: .1em;
    width: auto; height: auto;
    color: var(--violet-clair);
    font-size: .95em;
}

.grade__liste li.non { color: var(--texte-faible); }
.grade__liste li.non::before {
    border: 0;
    top: .5em;
    width: 11px; height: 2px;
    background: var(--texte-faible);
    transform: none;
}
.grade__liste b { color: var(--texte); font-weight: 650; }

/* ==================== BOUTIQUE : DEUX BLOCS ====================
   BLOC 1 — les cartes de rayon. Une carte par famille d'articles, avec son
   icone. On choisit d'abord DE QUOI on parle.
   BLOC 2 — le panneau de detail, qui change de contenu sans que la page ne
   bouge. Choisir un autre rayon remplace ce qui est affiche.

   POURQUOI PAS TROIS SECTIONS EMPILEES. La page precedente demandait de
   faire defiler trois blocs pour comparer deux articles, et l'on perdait de
   vue ce qu'on avait lu plus haut. Ici le choix reste toujours visible en
   haut, et le detail se remplace dessous. */

.rayons {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 178px), 1fr));
    gap: 1rem;
}

.carte-vente {
    position: relative;
    display: grid;
    justify-items: center;
    gap: .75rem;
    padding: 1.6rem 1rem 1.4rem;
    background: linear-gradient(160deg, var(--surface), var(--fond-2));
    border: 1px solid var(--bord);
    border-radius: var(--r-l);
    box-shadow: var(--biseau), var(--ombre-douce);
    cursor: pointer;
    overflow: hidden;
    isolation: isolate;
    text-align: center;
    color: inherit;
    font: inherit;
    transition: border-color .35s var(--net), box-shadow .4s var(--douce),
                background .35s var(--net);
}
.carte-vente::before {
    content: '';
    position: absolute;
    inset: -1px;
    z-index: -1;
    border-radius: inherit;
    background: radial-gradient(16rem 16rem at 50% 0%,
                color-mix(in srgb, var(--teinte, var(--violet)) 34%, transparent), transparent 68%);
    opacity: 0;
    transition: opacity .4s var(--net);
}
.carte-vente:hover::before, .carte-vente[aria-selected="true"]::before { opacity: 1; }
.carte-vente:hover { border-color: color-mix(in srgb, var(--teinte, var(--violet)) 50%, transparent); }
.carte-vente[aria-selected="true"] {
    border-color: var(--teinte, var(--violet));
    box-shadow: var(--biseau-fort), 0 18px 44px -20px color-mix(in srgb, var(--teinte, var(--violet)) 90%, transparent);
}

/* ==================== L'ICONE 3D D'UN RAYON ====================

   UN VRAI VOLUME, PAS UNE VIGNETTE PLATE.

   Trois plans empiles dans le meme espace 3D (`preserve-3d`) :
     - le socle, avec sa bordure et son ombre portee ;
     - une face interieure legerement soulevee, qui cree l'epaisseur ;
     - le pictogramme, pousse encore plus haut, qui flotte au-dessus.
   Un reflet oblique traverse le tout : c'est lui qui fait lire du verre
   plutot qu'un carre colore.

   ELLE SUIT LE CURSEUR SANS UNE LIGNE DE JS EN PLUS. Le champ magnetique
   (module 3) ecrit deja `--rx` et `--ry` sur la carte parente pour son propre
   pivot ; les variables CSS descendent aux enfants, on les reutilise en les
   amplifiant. L'icone se tourne donc vers la souris plus fort que la carte
   qui la porte, et c'est exactement l'effet recherche : l'objet regarde le
   curseur.

   Le flottement passe par `translate` et le redressement par `rotate` :
   `transform` est reserve a l'inclinaison 3D. Les trois proprietes
   individuelles se composent au lieu de s'ecraser. */
.carte-vente__icone {
    position: relative;
    width: 62px; height: 62px;
    display: grid;
    place-items: center;
    border-radius: 18px;
    background: linear-gradient(150deg,
        color-mix(in srgb, var(--teinte, var(--violet)) 26%, var(--surface-2)), var(--fond-2));
    border: 1px solid color-mix(in srgb, var(--teinte, var(--violet)) 32%, transparent);
    box-shadow: var(--biseau);
    animation: flotte 5.5s ease-in-out infinite;
    transform-style: preserve-3d;
    transform: perspective(420px)
               rotateX(calc(var(--rx, 0deg) * 2.4))
               rotateY(calc(var(--ry, 0deg) * 2.4));
    transition: box-shadow .45s var(--douce), border-color .35s,
                rotate .5s var(--ressort), transform .5s var(--douce);
}

/* L'epaisseur : une face interieure soulevee de 6 px dans la profondeur. */
.carte-vente__icone::before {
    content: '';
    position: absolute;
    inset: 5px;
    border-radius: 13px;
    background: linear-gradient(160deg,
        color-mix(in srgb, var(--teinte, var(--violet)) 34%, transparent),
        transparent 68%);
    transform: translateZ(6px);
}

/* Le reflet. `pointer-events: none` : sans lui, il intercepterait le survol
   du pictogramme et l'icone ne suivrait plus la souris au centre. */
.carte-vente__icone::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 18px;
    background: linear-gradient(140deg, rgba(255,255,255,.28), transparent 46%);
    transform: translateZ(19px);
    pointer-events: none;
    opacity: .7;
    transition: opacity .4s var(--net);
}

.carte-vente__icone svg {
    width: 32px; height: 32px;
    transform: translateZ(15px);
}

.carte-vente:hover .carte-vente__icone,
.carte-vente[aria-selected="true"] .carte-vente__icone {
    rotate: -7deg;
    border-color: var(--teinte, var(--violet));
    box-shadow: var(--biseau-fort),
                0 14px 30px -10px color-mix(in srgb, var(--teinte, var(--violet)) 85%, transparent);
}
.carte-vente:hover .carte-vente__icone::after { opacity: 1; }

/* LE TOUR SUR SOI-MEME, AU CLIC.
   C'est le pictogramme qui tourne, pas le socle : le socle continue pendant
   ce temps a suivre le curseur, et les deux mouvements se lisent ensemble au
   lieu de se combattre. Il sort aussi de la profondeur a mi-course — un tour
   a plat ne se distingue pas d'un simple clignotement. */
@keyframes pivot-icone {
      0% { transform: translateZ(15px) rotateY(0deg)   scale(1); }
     45% { transform: translateZ(34px) rotateY(200deg) scale(1.16); }
    100% { transform: translateZ(15px) rotateY(360deg) scale(1); }
}
.carte-vente__icone.tourne svg { animation: pivot-icone .72s var(--douce); }
.carte-vente__icone.tourne {
    box-shadow: var(--biseau-fort),
                0 20px 42px -12px color-mix(in srgb, var(--teinte, var(--violet)) 95%, transparent);
}

.carte-vente__nom {
    font-family: 'Outfit', sans-serif;
    font-size: 1.02rem;
    font-weight: 800;
    letter-spacing: -.02em;
}
.carte-vente__prix { font-size: .8rem; color: var(--texte-faible); }
.carte-vente__prix b { color: var(--or); font-weight: 700; }

/* Le panneau de detail. Il ne disparait jamais : il se remplace. */
.panneau-vente { position: relative; min-height: 220px; }
.rayon { display: none; }
.rayon.actif { display: block; animation: entree-rayon .5s var(--douce); }
@keyframes entree-rayon {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
}
.rayon__titre {
    display: flex;
    align-items: center;
    gap: .9rem;
    margin-bottom: 1.4rem;
    flex-wrap: wrap;
}

/* ==================== COFFRE ====================
   Le defilement d'objets d'un coffre : une bande qui glisse et ralentit
   jusqu'a s'arreter sur une case. L'animation est PUREMENT DECORATIVE — le
   resultat est decide avant qu'elle ne commence, jamais par elle. */

.coffre {
    position: relative;
    overflow: hidden;
    border: 1px solid var(--bord-fort);
    border-radius: var(--r-m);
    background: var(--noir);
    box-shadow: var(--biseau);
    padding: 1rem 0;
}
.coffre__bande {
    display: flex;
    gap: .6rem;
    padding: 0 .6rem;
    will-change: transform;
}
.coffre__case {
    flex: 0 0 104px;
    height: 104px;
    display: grid;
    place-items: center;
    gap: .3rem;
    padding: .5rem;
    text-align: center;
    background: linear-gradient(155deg, var(--surface-2), var(--fond-2));
    border: 1px solid var(--bord);
    border-radius: var(--r-s);
    box-shadow: var(--biseau);
    font-size: .68rem;
    line-height: 1.25;
    color: var(--texte-doux);
}
.coffre__case svg { width: 26px; height: 26px; }
.coffre__case b { display: block; font-size: .72rem; color: var(--texte); }
.coffre__case.rare  { border-color: rgba(47,125,246,.5); }
.coffre__case.epique{ border-color: rgba(168,85,247,.6); }
.coffre__case.legend{ border-color: rgba(242,179,61,.7); }

/* Le curseur central : c'est lui qui designe la case gagnante. Sans repere
   fixe, un defilement qui s'arrete ne designe rien. */
.coffre__viseur {
    position: absolute;
    top: 0; bottom: 0; left: 50%;
    width: 2px;
    background: linear-gradient(var(--violet), var(--bleu));
    box-shadow: 0 0 14px rgba(168,85,247,.9);
    transform: translateX(-50%);
    z-index: 2;
    pointer-events: none;
}
.coffre__viseur::before, .coffre__viseur::after {
    content: '';
    position: absolute;
    left: 50%;
    border: 6px solid transparent;
    transform: translateX(-50%);
}
.coffre__viseur::before { top: -1px; border-top-color: var(--violet); }
.coffre__viseur::after  { bottom: -1px; border-bottom-color: var(--bleu); }

/* Un voile sur les bords : la bande parait sortir de l'ombre au lieu d'etre
   coupee net. */
.coffre::before, .coffre::after {
    content: '';
    position: absolute;
    top: 0; bottom: 0;
    width: 70px;
    z-index: 1;
    pointer-events: none;
}
.coffre::before { left: 0;  background: linear-gradient(90deg, var(--noir), transparent); }
.coffre::after  { right: 0; background: linear-gradient(-90deg, var(--noir), transparent); }

.coffre-gain {
    display: none;
    align-items: center;
    gap: 1rem;
    margin-top: 1rem;
    padding: 1rem 1.2rem;
    border: 1px solid var(--bord-fort);
    border-radius: var(--r-m);
    background: rgba(168,85,247,.09);
    animation: monte .5s var(--douce);
}
.coffre-gain.visible { display: flex; }

.table-chances td:last-child { text-align: right; font-variant-numeric: tabular-nums; }

/* ==================== WIKI ==================== */

.wiki { display: grid; grid-template-columns: 250px 1fr; gap: 2.5rem; align-items: start; }

/* LA LIGNE QUI EMPECHE LE TEXTE D'ETRE COUPE A DROITE SUR TELEPHONE.
   Un element de grille vaut `min-width: auto` par defaut : il refuse de
   devenir plus etroit que son contenu le plus large. Or ces colonnes
   contiennent des tableaux dont le `min-width` vaut 460 px. Sur un ecran de
   360 px, la colonne se dilatait donc a 460 px, entrainant AVEC ELLE tous
   les paragraphes qui la suivent — leur moitie droite passait hors de
   l'ecran, et `body { overflow-x: hidden }` la coupait net, sans laisser
   la moindre possibilite de faire defiler pour la lire.
   `min-width: 0` rend la colonne compressible : le tableau redevient seul
   responsable de sa propre largeur, et le texte se remet a la ligne. */
.wiki > *,
.repli__contenu > *,
.mode__detail-inner > * { min-width: 0; }

.wiki__menu {
    position: sticky;
    top: calc(var(--nav-h) + 24px);
    display: grid;
    gap: .2rem;
    padding: 1rem;
    background: var(--surface);
    border: 1px solid var(--bord);
    border-radius: var(--r-l);
    box-shadow: var(--biseau);
}
.wiki__menu a {
    padding: .55rem .8rem;
    font-size: .89rem;
    font-weight: 600;
    color: var(--texte-doux);
    border-radius: var(--r-s);
    border-left: 2px solid transparent;
    transition: all .28s var(--net);
}
.wiki__menu a:hover, .wiki__menu a.actif {
    color: var(--texte);
    background: rgba(168,85,247,.10);
    border-left-color: var(--violet);
}
.wiki__menu .titre-menu {
    padding: .9rem .8rem .35rem;
    font-size: .68rem;
    font-weight: 800;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--texte-faible);
}

/* LES SECTIONS DEPLIANTES. Cliquer ouvre un panneau DANS la page, texte a
   gauche et image a droite : le joueur voit de quoi on parle en meme temps
   qu'il le lit. C'est toute la difference entre un wiki qu'on parcourt et
   un wiki qu'on comprend. */
/* On n'utilise PAS <details>, et c'est un choix technique.
   Un <details> ferme ne REND PAS ses enfants : le navigateur les retire de la
   mise en page. Une transition de hauteur n'a alors rien a animer — le contenu
   apparait d'un coup a l'ouverture, puis l'animation part d'un etat deja
   visible. Ici le contenu reste toujours dans le flux, simplement ecrase a
   zero et masque par `overflow`. Il s'anime donc vraiment, et il reste
   trouvable par la recherche du navigateur (Ctrl+F). */
.repli { border: 1px solid var(--bord); border-radius: var(--r-m); background: var(--surface); box-shadow: var(--biseau); overflow: hidden; }
.repli + .repli { margin-top: .8rem; }
.repli__tete {
    display: flex;
    align-items: center;
    gap: 1rem;
    width: 100%;
    padding: 1.1rem 1.4rem;
    background: none;
    border: 0;
    color: inherit;
    font: inherit;
    cursor: pointer;
    text-align: left;
    transition: background .3s var(--net);
}
.repli__tete:hover { background: rgba(168,85,247,.07); }
.repli__num {
    display: grid;
    place-items: center;
    width: 30px; height: 30px;
    flex-shrink: 0;
    font-family: 'Outfit', sans-serif;
    font-size: .82rem;
    font-weight: 800;
    color: var(--violet-clair);
    background: rgba(168,85,247,.13);
    border: 1px solid var(--bord);
    border-radius: var(--r-s);
}
.repli__titre {
    flex: 1;
    font-family: 'Outfit', sans-serif;
    font-size: 1.03rem;
    font-weight: 700;
    letter-spacing: -.02em;
}
.repli__plus {
    position: relative;
    width: 22px; height: 22px;
    flex-shrink: 0;
    transition: transform .45s var(--ressort);
}
.repli__plus::before, .repli__plus::after {
    content: '';
    position: absolute;
    top: 50%; left: 50%;
    background: var(--texte-doux);
    border-radius: 2px;
    transform: translate(-50%, -50%);
    transition: opacity .3s, background .3s;
}
.repli__plus::before { width: 13px; height: 2px; }
.repli__plus::after  { width: 2px; height: 13px; }
.repli.ouvert .repli__plus { transform: rotate(180deg); }
.repli.ouvert .repli__plus::after { opacity: 0; }
.repli.ouvert .repli__plus::before { background: var(--violet); }

.repli__corps { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .5s var(--douce); }
.repli.ouvert .repli__corps { grid-template-rows: 1fr; }

/* Le temps d'un clic dans le sommaire, tout se fige : les volets se referment
   d'un coup et la fenetre se recale dessus. Sans cette parenthese, la page
   change de hauteur pendant une demi-seconde et le defilement vise une
   position qui n'existe deja plus — c'etait le bug du wiki et du reglement.
   Le JS retire la classe des la ligne suivante : rien de tout cela ne dure. */
.sans-transition { scroll-behavior: auto !important; }
.sans-transition .repli__corps,
.sans-transition .repli__plus,
.sans-transition .mode__detail { transition: none !important; }
.repli__corps > div { overflow: hidden; }
.repli__contenu {
    display: grid;
    grid-template-columns: 1fr .85fr;
    gap: 1.8rem;
    padding: 0 1.4rem 1.5rem;
    border-top: 1px solid var(--bord-clair);
    padding-top: 1.4rem;
    margin-top: .2rem;
}

/* Emplacement d'illustration du wiki. Vide, il indique quoi y mettre :
   personne n'oublie une image dont l'emplacement porte son nom. */
.figure { margin: 0; }
.figure__cadre {
    position: relative;
    aspect-ratio: 16 / 10;
    border-radius: var(--r-m);
    overflow: hidden;
    background: linear-gradient(150deg, var(--surface-2), var(--noir));
    border: 1px solid var(--bord);
    box-shadow: var(--biseau);
    display: grid;
    place-items: center;
}
/* Le cadre est une grille centree, pour que l'emplacement vide se pose au
   milieu. Une capture, elle, doit remplir le cadre : on l'etire. */
.figure__cadre img { width: 100%; height: 100%; object-fit: cover; transition: transform .7s var(--douce); }
.figure__cadre > img { grid-area: 1 / 1; justify-self: stretch; align-self: stretch; }
.figure__cadre:hover img { transform: scale(1.06); }
.figure__vide { text-align: center; padding: 1.2rem; display: grid; gap: .55rem; place-items: center; }
.figure__vide svg { width: 34px; height: 34px; color: var(--texte-faible); }
.figure__vide code {
    font-size: .72rem;
    color: var(--violet-clair);
    background: rgba(168,85,247,.10);
    padding: .2rem .5rem;
    border-radius: 5px;
    word-break: break-all;
}
/* Pas de legende sous les captures : l'image illustre le paragraphe qui est
   juste a cote, et une phrase qui repete ce paragraphe n'apprend rien. Le
   texte alternatif reste, lui — il sert a qui n'a pas l'image. */

/* ==================== TABLEAUX ==================== */

.tableau-enveloppe {
    overflow-x: auto;
    max-width: 100%;
    border: 1px solid var(--bord);
    border-radius: var(--r-m);
    box-shadow: var(--biseau);
    background: var(--surface);
    /* Le defilement horizontal reste possible sur les ecrans intermediaires ;
       il s'arrete net au bord du tableau au lieu d'entrainer la page. */
    overscroll-behavior-x: contain;
}
table { width: 100%; border-collapse: collapse; font-size: .9rem; min-width: 460px; }
th, td { padding: .8rem 1.1rem; text-align: left; border-bottom: 1px solid var(--bord-clair); }
th {
    font-size: .72rem;
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--texte-faible);
    background: rgba(255,255,255,.025);
}
td { color: var(--texte-doux); }
tbody tr { transition: background .25s var(--net); }
tbody tr:hover { background: rgba(168,85,247,.06); }
tbody tr:last-child td { border-bottom: 0; }
td strong, th strong { color: var(--texte); }

/* ---------- LES TABLEAUX SUR TELEPHONE ----------
   Un tableau de trois colonnes ne tient PAS dans 360 px, et le faire defiler
   lateralement ne marche pas : rien n'indique qu'il y a une suite, l'en-tete
   part avec le reste, et le lecteur repart en croyant avoir tout lu — c'est
   exactement ce qui se passait sur l'echelle des sanctions, ou seules
   "Sanction" et "Effet" etaient visibles.

   En dessous de 620 px, chaque LIGNE devient donc une petite carte : la
   premiere cellule sert de titre, les suivantes s'affichent en "intitule /
   valeur". Rien n'est cache, rien ne deborde, et l'en-tete du tableau est
   repete a chaque ligne au lieu d'etre perdu tout en haut.

   Les intitules ne sont PAS ecrits dans le HTML : le module 18 de app.js les
   recopie depuis le <thead> vers `data-colonne`. Un tableau ajoute plus tard
   en herite sans qu'on ait rien a faire. */
@media (max-width: 620px) {
    .tableau-enveloppe {
        overflow-x: visible;
        border: 0;
        background: none;
        box-shadow: none;
        border-radius: 0;
    }
    .tableau-enveloppe table {
        display: block;
        min-width: 0;
        width: 100%;
        font-size: .88rem;
    }
    /* L'EN-TETE EST RETIRE, PAS SEULEMENT MASQUE.
       Il l'etait d'abord par la technique habituelle (1 px + clip-path), pour
       le laisser aux lecteurs d'ecran. Mais sa LIGNE, elle, gardait
       `display: table-row` et continuait donc a se mettre en page a sa largeur
       naturelle — 401 px pour le comparatif des grades, mesure dans un
       viewport de 375. Invisible a l'oeil parce que clippee, mais bien
       presente dans la mise en page.
       Et l'y garder n'apportait plus rien : une fois le tableau passe en
       `display: block`, les liens ligne/colonne sont de toute facon perdus
       pour les technologies d'assistance. C'est `data-colonne`, repete sur
       CHAQUE cellule, qui porte desormais l'information — et lui se lit. */
    .tableau-enveloppe thead { display: none; }
    .tableau-enveloppe tbody { display: block; }
    .tableau-enveloppe tbody tr {
        display: block;
        padding: .9rem 1.05rem;
        margin-bottom: .7rem;
        background: var(--surface);
        border: 1px solid var(--bord);
        border-radius: var(--r-m);
        box-shadow: var(--biseau);
    }
    .tableau-enveloppe tbody tr:last-child { margin-bottom: 0; }
    .tableau-enveloppe tbody tr:hover { background: var(--surface); }

    .tableau-enveloppe tbody td {
        display: grid;
        grid-template-columns: minmax(0, 7.5rem) minmax(0, 1fr);
        align-items: baseline;
        gap: .2rem .9rem;
        padding: .5rem 0 0;
        border-bottom: 0;
        overflow-wrap: anywhere;
    }
    .tableau-enveloppe tbody td::before {
        content: attr(data-colonne);
        font-size: .66rem;
        font-weight: 800;
        letter-spacing: .09em;
        text-transform: uppercase;
        color: var(--texte-faible);
    }
    /* La premiere cellule nomme la ligne : c'est le titre de la carte, pas
       une valeur parmi d'autres. */
    .tableau-enveloppe tbody td[data-premiere] {
        display: block;
        padding: 0 0 .55rem;
        margin-bottom: .3rem;
        font-size: 1rem;
        color: var(--texte);
        border-bottom: 1px solid var(--bord-clair);
    }
    .tableau-enveloppe tbody td[data-premiere]::before { content: none; }

    /* Tableau sans en-tete : on ne fabrique pas un intitule vide. */
    .tableau-enveloppe tbody td:not([data-colonne]) { display: block; }
    .tableau-enveloppe tbody td:not([data-colonne])::before { content: none; }

    /* Une matrice (elements du TeamSpell, comparatif des grades) a beaucoup
       de colonnes courtes : deux par ligne valent mieux qu'une pile. */
    .tableau-enveloppe table[data-matrice] tbody td {
        grid-template-columns: minmax(0, 1fr) auto;
    }
}

/* ==================== FORMULAIRES ==================== */

.champ { display: grid; gap: .45rem; margin-bottom: 1.1rem; }
.champ label { font-size: .82rem; font-weight: 650; color: var(--texte-doux); }
.champ input, .champ select, .champ textarea {
    width: 100%;
    padding: .82rem 1rem;
    font: inherit;
    font-size: .95rem;
    color: var(--texte);
    background: var(--noir);
    border: 1px solid var(--bord);
    border-radius: var(--r-m);
    box-shadow: var(--biseau);
    transition: border-color .3s, box-shadow .3s;
}
.champ input:focus, .champ select:focus, .champ textarea:focus {
    outline: 0;
    border-color: var(--violet);
    box-shadow: var(--biseau), 0 0 0 3px rgba(168,85,247,.18);
}
.champ input::placeholder { color: var(--texte-faible); }
.aide { font-size: .78rem; color: var(--texte-faible); }

.retour {
    display: none;
    align-items: center;
    gap: .7rem;
    padding: .85rem 1.1rem;
    font-size: .89rem;
    border-radius: var(--r-m);
    border: 1px solid;
    margin-top: 1rem;
}
.retour.visible { display: flex; animation: monte .45s var(--douce); }
.retour.ok  { color: var(--vert);  background: rgba(52,211,153,.09); border-color: rgba(52,211,153,.3); }
.retour.ko  { color: var(--rouge); background: rgba(240,91,107,.09); border-color: rgba(240,91,107,.3); }
.retour.info{ color: var(--violet-clair); background: rgba(168,85,247,.09); border-color: var(--bord-fort); }
.retour img { width: 34px; height: 34px; border-radius: 6px; image-rendering: pixelated; }

@keyframes monte {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}

/* ==================== DISCORD ====================
   Il tourne sur lui-meme et grossit vers le curseur, comme demande. Le
   bouton flottant est present sur TOUTES les pages : c'est le seul appel
   a l'action qui ne doit jamais demander de defiler pour etre trouve. */

.discord-flottant {
    position: fixed;
    right: clamp(1rem, 3vw, 2rem);
    bottom: clamp(1rem, 3vw, 2rem);
    z-index: 90;
    width: 60px; height: 60px;
    display: grid;
    place-items: center;
    border-radius: 20px;
    background: linear-gradient(150deg, #5865F2, #3C45C4);
    border: 1px solid rgba(255,255,255,.16);
    box-shadow: var(--biseau-fort), 0 14px 34px -10px rgba(88,101,242,.75);
    cursor: pointer;
    transition: box-shadow .5s var(--douce), border-radius .5s var(--douce);
    perspective: 600px;
}
/* IL RESPIRE EN PERMANENCE, meme sans souris. Un bouton parfaitement immobile
   dans un coin devient un element de decor : l'oeil cesse de le voir au bout
   de dix secondes.

   LA RESPIRATION EST SUR L'ICONE, PAS SUR LE BOUTON. Une animation @keyframes
   qui ecrit `scale` gagne toujours contre un `scale` pose en ligne — et c'est
   exactement ce que fait le champ magnetique sur ce bouton. Les deux se
   disputeraient la meme propriete, et le survol ne ferait plus rien.
   En placant la respiration un cran plus bas, les deux effets se cumulent :
   le bouton grossit vers la souris pendant que l'icone continue de battre. */
.discord-flottant svg {
    width: 32px; height: 32px;
    color: #fff;
    transition: transform .75s var(--ressort);
    animation: respiration-discord 3.4s ease-in-out infinite;
}
@keyframes respiration-discord {
    0%, 100% { scale: 1; }
    50%      { scale: 1.09; }
}
.discord-flottant:hover {
    border-radius: 28px;
    box-shadow: var(--biseau-fort), 0 26px 56px -12px rgba(88,101,242,1);
}
.discord-flottant:hover svg { transform: rotateY(360deg); }
.discord-flottant::after {
    content: 'Rejoins le Discord';
    position: absolute;
    right: calc(100% + 14px);
    top: 50%;
    transform: translateY(-50%) translateX(10px);
    padding: .5rem .9rem;
    font-size: .82rem;
    font-weight: 650;
    white-space: nowrap;
    color: var(--texte);
    background: var(--surface-2);
    border: 1px solid var(--bord);
    border-radius: var(--r-s);
    box-shadow: var(--biseau), var(--ombre);
    opacity: 0;
    pointer-events: none;
    transition: opacity .35s var(--net), transform .45s var(--douce);
}
.discord-flottant:hover::after { opacity: 1; transform: translateY(-50%) translateX(0); }

/* Onde qui part du bouton, une fois toutes les 4 s. Discrete, mais c'est
   elle qui fait remarquer le bouton sans le rendre agressif. */
.discord-flottant::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    border: 1.5px solid #5865F2;
    animation: onde-discord 4s ease-out infinite;
    pointer-events: none;
}
@keyframes onde-discord {
    0%       { transform: scale(1);   opacity: .8; }
    45%,100% { transform: scale(1.5); opacity: 0; }
}

.bloc-discord {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 2rem;
    padding: 2.4rem;
    background: linear-gradient(120deg, rgba(88,101,242,.16), var(--surface) 55%);
    border: 1px solid rgba(88,101,242,.32);
}
.bloc-discord__logo {
    width: 88px; height: 88px;
    display: grid; place-items: center;
    border-radius: 24px;
    background: linear-gradient(150deg, #5865F2, #3C45C4);
    box-shadow: var(--biseau-fort), 0 18px 40px -14px rgba(88,101,242,.8);
    animation: flotte 5.5s ease-in-out infinite;
    perspective: 600px;
    flex-shrink: 0;
}
.bloc-discord__logo svg { width: 48px; height: 48px; color: #fff; transition: transform .8s var(--ressort); }
.bloc-discord:hover .bloc-discord__logo svg { transform: rotateY(360deg); }

/* ==================== PIED DE PAGE ==================== */

.pied {
    margin-top: 4rem;
    padding: 4rem 0 2.5rem;
    border-top: 1px solid var(--bord);
    background: linear-gradient(var(--noir), var(--fond));
}
.pied__grille {
    display: grid;
    grid-template-columns: 1.5fr repeat(3, 1fr);
    gap: 2.5rem;
    margin-bottom: 3rem;
}
.pied h4 {
    font-size: .72rem;
    font-weight: 800;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--texte-faible);
    margin-bottom: 1.1rem;
}
.pied ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
.pied ul a { font-size: .9rem; color: var(--texte-doux); transition: color .25s, transform .25s var(--douce); display: inline-block; }
.pied ul a:hover { color: var(--violet-clair); transform: translateX(4px); }
.pied__bas {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    justify-content: space-between;
    align-items: center;
    padding-top: 2rem;
    border-top: 1px solid var(--bord-clair);
    font-size: .82rem;
    color: var(--texte-faible);
}

/* ==================== CHAMP MAGNETIQUE ("BULLES") ====================
   Tout element porteur de .bulle reagit a la PROXIMITE du curseur, sans
   qu'il ait besoin d'etre survole : il grossit et derive doucement vers la
   souris a mesure qu'elle approche, et revient exactement a sa taille des
   qu'elle s'eloigne. C'est ce qui donne la sensation que la page entiere
   respire autour du pointeur.

   ON N'ECRIT PAS `transform`, ET C'EST TOUT L'ENJEU.
   Les cartes utilisent deja `transform` pour leur inclinaison 3D, et les
   boutons pour leur survol. Ecrire `transform` en JS ecraserait les deux.
   On passe donc par `translate` et `scale`, les proprietes individuelles :
   le navigateur les compose AVEC `transform` au lieu de les remplacer.
   Trois effets sur le meme element, aucun conflit. */

/* LE PIVOT LUI-MEME EST DEFINI PLUS BAS, juste apres `.reveal`.
   Il DOIT y etre : `.reveal.vu { transform: none }` a deux classes, `.bulle`
   n'en a qu'une, et l'apparition au defilement est declaree plus loin dans
   cette feuille. Tant que la regle vivait ici, tout element portant a la fois
   `.reveal` et `.bulle` — les blocs de vote, la moitie des cartes — voyait son
   inclinaison 3D purement et simplement effacee. Voir "CHAMP MAGNETIQUE : LE
   PIVOT" a la fin du fichier. */

/* `will-change` UNIQUEMENT PENDANT L'EFFET, JAMAIS EN PERMANENCE.
   Cette propriete demande au navigateur de promouvoir l'element sur une
   couche GPU dediee. Posee sur `.bulle`, elle en creait SOIXANTE d'un coup,
   des le chargement, sur une page qui a deja un flou d'arriere-plan sur sa
   barre de navigation. Le compositeur saturait : la page se figeait
   plusieurs secondes sans qu'aucun script ne tourne — un blocage
   introuvable au debogueur, puisqu'il n'est pas dans le JavaScript.
   Ici, seuls les deux ou trois elements reellement sous le curseur sont
   promus, et ils reviennent au flux des que la souris s'eloigne. */
.bulle--proche { will-change: translate, scale, transform; }

/* ==================== CHARGEMENT ====================
   Le cristal du logo, en petit, qui tourne pendant qu'on attend. Il sert
   partout : verification d'un pseudo, passage d'une page a l'autre. */

.chargeur {
    display: inline-grid;
    place-items: center;
    width: 38px; height: 38px;
    perspective: 300px;
    flex-shrink: 0;
}
.chargeur__cristal {
    position: relative;
    width: 22px; height: 22px;
    transform-style: preserve-3d;
    animation: charge 1.5s linear infinite;
}
.chargeur__cristal i {
    position: absolute;
    inset: 0;
    background: linear-gradient(160deg, var(--violet-clair), var(--violet) 50%, var(--violet-nuit));
    clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
    opacity: .9;
}
.chargeur__cristal i:nth-child(1) { transform: rotateY(0deg)   translateZ(7px); }
.chargeur__cristal i:nth-child(2) { transform: rotateY(90deg)  translateZ(7px); }
.chargeur__cristal i:nth-child(3) { transform: rotateY(180deg) translateZ(7px); }
.chargeur__cristal i:nth-child(4) { transform: rotateY(270deg) translateZ(7px); }
@keyframes charge {
    from { transform: rotateX(-18deg) rotateY(0deg); }
    to   { transform: rotateX(-18deg) rotateY(360deg); }
}

/* Voile de transition entre deux pages. Il ne masque pas la page : il pose
   un cristal au centre pendant les quelques centaines de millisecondes de
   navigation, pour que le clic ait une reponse immediate. */
.voile {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: grid;
    place-items: center;
    background: rgba(6, 5, 16, .72);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    opacity: 0;
    pointer-events: none;
    transition: opacity .3s var(--net);
}
.voile.visible { opacity: 1; pointer-events: all; }
.voile .chargeur { width: 90px; height: 90px; perspective: 700px; }
.voile .chargeur__cristal { width: 52px; height: 52px; }
.voile .chargeur__cristal i:nth-child(1) { transform: rotateY(0deg)   translateZ(17px); }
.voile .chargeur__cristal i:nth-child(2) { transform: rotateY(90deg)  translateZ(17px); }
.voile .chargeur__cristal i:nth-child(3) { transform: rotateY(180deg) translateZ(17px); }
.voile .chargeur__cristal i:nth-child(4) { transform: rotateY(270deg) translateZ(17px); }

/* ==================== DIAPORAMA DES MODES ====================
   Trois images par mode, qui se succedent en fondu. Les images sont
   empilees au meme endroit et l'on ne change que l'opacite : aucun
   deplacement, donc aucun recalcul de mise en page a chaque transition. */

.diapo { position: relative; width: 100%; height: 100%; }
.diapo img {
    position: absolute;
    inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    opacity: 0;
    transform: scale(1.06);
    transition: opacity 1.1s var(--net), transform 5s linear;
}
.diapo img.active { opacity: 1; transform: scale(1); }

.diapo__points {
    position: absolute;
    bottom: .7rem; left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: .4rem;
    z-index: 2;
}
.diapo__points b {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: rgba(255,255,255,.3);
    transition: width .4s var(--douce), background .4s;
}
.diapo__points b.active { width: 18px; border-radius: 3px; background: var(--teinte, var(--violet)); }

/* ---- Le meme diaporama dans une figure du wiki ----------------------
   Quand plusieurs captures portent le meme nom suivi d'un numero, elles
   se relaient dans le cadre au lieu de n'en montrer qu'une. Le cadre est
   une grille centree : la diapo doit donc s'etirer explicitement, sinon
   elle se reduit a la taille de son contenu absolu, c'est-a-dire rien.
   Le leger zoom qui accompagne le fondu suffit a signaler que l'image
   change ; le survol n'en rajoute pas, il redoublerait le mouvement. */
.figure__cadre > .diapo--figure {
    grid-area: 1 / 1;
    justify-self: stretch;
    align-self: stretch;
}
.figure__cadre:hover .diapo--figure img.active { transform: scale(1); }

/* Les puces se lisent mal sur une capture claire : on les pose sur un
   fond assombri plutot que d'assombrir toute l'image. */
.diapo--figure .diapo__points {
    padding: .34rem .5rem;
    border-radius: 999px;
    background: rgba(9, 8, 18, .42);
    backdrop-filter: blur(6px);
}

/* Un compteur discret : cinq puces se comptent d'un coup d'oeil, pas dix.
   Le chiffre dit la quantite exacte sans allonger la rangee. */
.diapo__compteur {
    position: absolute;
    top: .6rem; right: .7rem;
    z-index: 2;
    font-size: .68rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: .04em;
    color: rgba(255, 255, 255, .82);
    background: rgba(9, 8, 18, .42);
    backdrop-filter: blur(6px);
    padding: .2rem .5rem;
    border-radius: 999px;
}

@media (prefers-reduced-motion: reduce) {
    .diapo img { transition: opacity .2s linear; transform: none; }
    .diapo img.active { transform: none; }
}

/* ==================== BANDEAU DE PROMOTION ====================
   Une seule bande, en tete de la boutique, et rien d'autre. Pas de fenetre a
   fermer ni de compte a rebours : le visiteur vient lire des prix, on lui dit
   l'essentiel la ou il regarde deja, et on le laisse tranquille.
   Le liseré dore anime sert de signal sans clignoter — un clignotement se
   fait ignorer en trois secondes, un mouvement lent se remarque. */

.promo-bandeau {
    position: relative;
    display: flex;
    align-items: center;
    gap: 1.1rem;
    flex-wrap: wrap;
    padding: 1.1rem 1.5rem;
    margin-bottom: clamp(2rem, 4vw, 3rem);
    border-radius: var(--r-l);
    background: linear-gradient(120deg,
        rgba(242,179,61,.16), rgba(168,85,247,.10) 55%, var(--surface) 100%);
    border: 1px solid rgba(242,179,61,.38);
    box-shadow: var(--biseau), 0 18px 44px -26px rgba(242,179,61,.7);
    overflow: hidden;
    isolation: isolate;
}
/* Le reflet qui balaie, une fois toutes les six secondes. */
.promo-bandeau::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(105deg, transparent 38%,
                rgba(255,255,255,.14) 50%, transparent 62%);
    transform: translateX(-130%);
    animation: promoReflet 6s var(--douce) infinite;
}
@keyframes promoReflet {
    0%, 62% { transform: translateX(-130%); }
    88%, 100% { transform: translateX(130%); }
}

.promo-bandeau__etiquette {
    flex-shrink: 0;
    padding: .4rem .9rem;
    font-family: 'Outfit', sans-serif;
    font-size: .78rem;
    font-weight: 800;
    letter-spacing: .12em;
    color: #1A1206;
    background: var(--or);
    border-radius: 999px;
    box-shadow: var(--biseau);
}
.promo-bandeau__texte { flex: 1; min-width: 15rem; font-size: .95rem; }
.promo-bandeau__texte strong { color: var(--texte); }
.promo-bandeau__taux {
    flex-shrink: 0;
    font-family: 'Outfit', sans-serif;
    font-size: clamp(1.4rem, 4vw, 1.9rem);
    font-weight: 800;
    letter-spacing: -.02em;
    color: var(--or);
    white-space: nowrap;
}

/* SUR TELEPHONE, LES TROIS BLOCS S'EMPILENT.
   En ligne, l'etiquette et le taux prennent 216 px des 340 disponibles : il
   ne restait que 64 px au texte, qui se retrouvait a un mot par ligne sur
   570 px de haut. `min-width: 0` ne fait qu'autoriser cet ecrasement — c'est
   la DIRECTION qu'il faut changer, pas la largeur minimale. */
@media (max-width: 620px) {
    .promo-bandeau {
        flex-direction: column;
        align-items: flex-start;
        padding: 1rem 1.1rem;
        gap: .55rem;
    }
    .promo-bandeau__texte { min-width: 0; flex: none; font-size: .9rem; }
    .promo-bandeau__taux { font-size: 1.45rem; }
}

/* ==================== SITES DE VOTE ====================
   Ces blocs etaient des cartes ordinaires : rien ne disait qu'on pouvait
   cliquer dessus. Un lien qui ne ressemble pas a un lien n'est pas clique.
   On ajoute donc les trois signaux qui manquaient : une fleche qui avance,
   un liseré qui s'allume, et un intitule d'action ecrit noir sur blanc. */

.site-vote {
    position: relative;
    display: block;
    padding: 1.6rem 1.8rem;
    background: linear-gradient(155deg, var(--surface), var(--fond-2));
    border: 1px solid var(--bord-fort);
    border-radius: var(--r-l);
    box-shadow: var(--biseau), var(--ombre-douce);
    overflow: hidden;
    isolation: isolate;
    /* PAS DE `transition` ICI. Ces blocs sont `.bulle` et `.reveal` : leurs
       transitions — deplacement, echelle, pivot, bordure et ombre — sont
       posees d'un seul tenant par la regle `.bulle, .reveal.bulle` en fin de
       feuille, qui pese assez lourd pour passer devant `.reveal.vu`. En
       redeclarer une ici la ferait gagner sur les seules proprietes citees et
       reintroduirait exactement le decalage qu'on vient de corriger. */
}
.site-vote::before {
    content: '';
    position: absolute;
    inset: -1px;
    z-index: -1;
    border-radius: inherit;
    background: radial-gradient(18rem 14rem at var(--mx, 50%) var(--my, 50%),
                rgba(242,179,61,.26), transparent 66%);
    opacity: 0;
    transition: opacity .4s var(--net);
}
.site-vote:hover::before { opacity: 1; }
.site-vote:hover {
    border-color: var(--or);
    box-shadow: var(--biseau-fort), 0 20px 46px -20px rgba(242,179,61,.75);
}

.site-vote__action {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    margin-top: 1.2rem;
    padding: .5rem 1rem;
    font-family: 'Outfit', sans-serif;
    font-size: .88rem;
    font-weight: 700;
    color: var(--or);
    background: rgba(242,179,61,.12);
    border: 1px solid rgba(242,179,61,.36);
    border-radius: var(--r-s);
    box-shadow: var(--biseau);
    transition: background .3s, color .3s, gap .35s var(--douce);
}
.site-vote:hover .site-vote__action {
    background: var(--or);
    color: #1A1206;
    gap: .85rem;
}
.site-vote__action svg { width: 15px; height: 15px; }

/* La petite icone "lien externe", en haut a droite : le signe universel
   qui dit "ceci ouvre un autre site". */
.site-vote__externe {
    position: absolute;
    top: 1.1rem; right: 1.2rem;
    width: 16px; height: 16px;
    color: var(--texte-faible);
    transition: color .3s, translate .4s var(--douce);
}
.site-vote:hover .site-vote__externe { color: var(--or); translate: 2px -2px; }

/* ==================== BOUTON DE SON ==================== */

.son {
    display: grid;
    place-items: center;
    width: 36px; height: 36px;
    background: rgba(255,255,255,.04);
    border: 1px solid var(--bord);
    border-radius: var(--r-s);
    box-shadow: var(--biseau);
    cursor: pointer;
    color: var(--texte-doux);
    padding: 0;
    transition: color .3s, border-color .3s, background .3s;
}
.son:hover { color: var(--violet-clair); border-color: var(--bord-fort); }
.son svg { width: 17px; height: 17px; }
.son .coupe { display: none; }
.son[aria-pressed="false"] { color: var(--texte-faible); }
.son[aria-pressed="false"] .actif { display: none; }
.son[aria-pressed="false"] .coupe { display: block; }

/* ==================== ETIQUETTE "CONFIGURABLE" ==================== */

.config-tag {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .22rem .6rem;
    font-size: .66rem;
    font-weight: 800;
    letter-spacing: .1em;
    color: var(--or);
    background: rgba(242,179,61,.13);
    border: 1px solid rgba(242,179,61,.3);
    border-radius: 5px;
    white-space: nowrap;
}
.config-tag svg { width: 11px; height: 11px; }

/* ==================== APPARITION AU DEFILEMENT ==================== */

/* L'ENTREE PASSE PAR UNE VARIABLE, ET NON PLUS PAR `transform` DIRECTEMENT.
   ------------------------------------------------------------------------
   `.reveal.vu { transform: none }` pese deux classes ; `.bulle`, qui porte
   l'inclinaison 3D, n'en pese qu'une et etait declaree plus haut. Le "none"
   gagnait donc sur les deux tableaux, et TOUT element a la fois `.reveal` et
   `.bulle` perdait son pivot : les trois blocs de vote (`class="site-vote
   reveal"`) restaient parfaitement plats pendant que les boutons voisins, eux,
   pivotaient. Le JS ecrivait bien --rx et --ry ; le CSS les jetait.

   Les deux effets ne se disputent plus la meme propriete : l'entree pose sa
   translation dans --entree, et le pivot la reprend telle quelle dans son
   propre `transform`. Ils se composent au lieu de s'ecraser. */
@property --entree {
    syntax: '<transform-list>';
    /* NE PAS HERITER : sans cette ligne, une grille `.reveal` transmettrait
       ses 26 px a chacune de ses cartes, qui descendraient donc deux fois. */
    inherits: false;
    initial-value: translateY(0);
}

.reveal {
    opacity: 0;
    --entree: translateY(26px);
    transform: var(--entree);
    transition: opacity .8s var(--douce), transform .8s var(--douce);
}
.reveal.vu { opacity: 1; --entree: translateY(0); }

/* ==================== CHAMP MAGNETIQUE : LE PIVOT ====================
   Declare APRES `.reveal.vu`, et avec autant de classes que lui : c'est la
   seule facon que l'inclinaison survive a l'apparition au defilement.
   L'ordre des fonctions compte : `perspective()` ne s'applique qu'a ce qui la
   SUIT, donc la translation d'entree reste plate et seules les rotations
   prennent le relief. */
.bulle,
.reveal.bulle {
    transform: var(--entree, translateY(0))
               perspective(1000px)
               rotateX(var(--rx, 0deg))
               rotateY(var(--ry, 0deg));
    /* LA LISTE EST LONGUE, ET CHAQUE LIGNE EST LA POUR UNE RAISON.
       `transition` est un raccourci : il REMPLACE la regle du composant au
       lieu de s'y ajouter. Comme ce bloc est declare en fin de feuille, il
       gagnait sur tout le monde et effacait au passage les transitions
       propres des elements aimantes — le fond du bouton principal, la
       couleur des liens du sommaire, l'ecartement de la fleche "Voter sur ce
       site". Elles changeaient donc d'un coup, sans fondu.
       On reprend ici l'union de ce que ces composants animent reellement.
       Un element qui ne touche pas a l'une de ces proprietes ne declenche
       simplement aucune transition dessus : la ligne ne lui coute rien. */
    transition: translate .5s var(--douce),
                scale .5s var(--douce),
                transform .5s var(--douce),
                opacity .8s var(--douce),
                color .3s var(--net),
                background-color .3s var(--net),
                border-color .35s var(--net),
                border-radius .5s var(--douce),
                box-shadow .45s var(--douce),
                gap .35s var(--douce);
}
/* La transition est plus lente au RETOUR qu'a l'aller : approcher est vif,
   s'eloigner se pose. C'est ce decalage qui fait "vivant" plutot que
   "mecanique". Seuls le deplacement et le pivot ralentissent ; les couleurs,
   elles, gardent leur rythme. Les durees suivent l'ordre ci-dessus. */
.bulle:not(.bulle--proche),
.reveal.bulle:not(.bulle--proche) {
    transition-duration: .85s, .85s, .85s, .8s,
                         .3s, .3s, .35s, .5s, .45s, .35s;
}

/* Barre de progression de lecture, en haut de page. */
.progression {
    position: fixed;
    top: 0; left: 0;
    height: 2px;
    width: 0;
    z-index: 101;
    background: linear-gradient(90deg, var(--violet), var(--bleu));
    box-shadow: 0 0 12px rgba(168,85,247,.8);
    transition: width .1s linear;
}

/* ==================== ADAPTATION AUX ECRANS ==================== */

@media (max-width: 1080px) {
    .hero__grille { grid-template-columns: 1fr; text-align: center; }
    .hero .lead, .hero .eyebrow { margin-inline: auto; }
    .hero__actions { justify-content: center; }
    .scene { order: -1; min-height: 300px; }
    .wiki { grid-template-columns: 1fr; }
    .wiki__menu { position: static; display: flex; flex-wrap: wrap; }
    .pied__grille { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 860px) {
    .liens {
        position: fixed;
        inset: var(--nav-h) 0 auto;
        flex-direction: column;
        align-items: stretch;
        gap: .3rem;
        padding: 1.2rem;
        background: rgba(9,8,20,.97);
        backdrop-filter: blur(20px);
        -webkit-backdrop-filter: blur(20px);
        border-bottom: 1px solid var(--bord);
        transform: translateY(-120%);
        transition: transform .45s var(--douce);
    }
    .liens.ouvert { transform: none; }
    .liens a { padding: .8rem 1rem; font-size: 1rem; }
    .liens a::after { display: none; }
    .burger { display: grid; }
    .mode__detail-inner { grid-template-columns: 1fr; gap: 1.4rem; }
    .repli__contenu { grid-template-columns: 1fr; }
    .bloc-discord { grid-template-columns: 1fr; text-align: center; justify-items: center; }
}

@media (max-width: 620px) {
    :root { --nav-h: 64px; }
    .mode { grid-template-columns: auto 1fr; padding: 1.2rem; gap: 1rem; }
    .mode__rang { display: none; }
    .mode__fleche { display: none; }
    .mode__icone { width: 58px; height: 58px; }
    .mode__icone svg { width: 32px; height: 32px; }
    .mode__detail-inner { padding: 1.3rem; }
    .pied__grille { grid-template-columns: 1fr; gap: 2rem; }
    .discord-flottant { width: 54px; height: 54px; }
    .discord-flottant::after { display: none; }
    .ip { font-size: .92rem; padding-left: 1rem; }
}

/* ==================== CAPTURES AGRANDISSABLES ====================
   Une capture fait 400 px de large dans la page : de quoi situer ce dont
   parle le texte, pas de quoi lire un menu Minecraft. Elle s'ouvre donc en
   grand — encore faut-il que le visiteur devine qu'il peut cliquer.

   Trois signaux, qui disent la meme chose de trois facons : le curseur
   devient une loupe, le cadre se souleve, et un bouton "Agrandir" monte
   dans le coin. Aucun ne suffit seul — le curseur n'existe pas au doigt,
   le soulevement se remarque a peine, le bouton seul ressemble a une
   legende. Ensemble, l'intention ne fait plus de doute. */

.agrandissable {
    cursor: zoom-in;
    -webkit-tap-highlight-color: transparent;
    transition: transform .4s var(--douce), box-shadow .4s var(--douce);
}
/* Le survol n'existe que la ou il y a une souris. Sur un ecran tactile,
   `:hover` reste colle apres le doigt : le cadre resterait souleve. */
@media (hover: hover) {
    .agrandissable:hover {
        transform: translateY(-4px);
        box-shadow: var(--biseau), 0 26px 52px -26px rgba(0, 0, 0, .9);
    }
    .agrandissable:hover .loupe { opacity: 1; transform: none; }
}
.agrandissable:focus-visible {
    outline: 2px solid var(--violet-clair);
    outline-offset: 4px;
    transform: translateY(-4px);
}
.agrandissable:focus-visible .loupe { opacity: 1; transform: none; }
/* L'enfoncement : un cadre qui cede sous le clic se comporte en bouton. */
.agrandissable:active {
    transform: translateY(-1px) scale(.994);
    transition-duration: .12s;
}

.loupe {
    position: absolute;
    right: .55rem; bottom: .55rem;
    z-index: 3;
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .34rem .6rem;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, .14);
    background: rgba(10, 9, 20, .60);
    backdrop-filter: blur(10px);
    color: rgba(255, 255, 255, .92);
    font-size: .68rem;
    font-weight: 600;
    letter-spacing: .03em;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transform: translateY(8px) scale(.94);
    transition: opacity .32s var(--douce), transform .32s var(--douce);
}
.loupe svg { width: 13px; height: 13px; flex: none; }
/* Au doigt, rien ne survole : le bouton reste visible en permanence. */
@media (hover: none) {
    .loupe { opacity: .92; transform: none; }
}

/* ==================== LA VISIONNEUSE ====================
   L'image ne surgit pas au centre de l'ecran : elle PART de son cadre et
   grandit jusqu'a sa taille finale, puis y retourne a la fermeture. L'oeil
   suit le mouvement, donc personne ne se demande d'ou l'image sort ni ou
   elle est repartie. Le vol lui-meme est calcule en JavaScript (on mesure
   l'arrivee, on la ramene au depart, on relache) ; ici on ne pose que la
   scene et les commandes. */

.visionneuse {
    position: fixed;
    inset: 0;
    z-index: 300;
    display: grid;
    /* Une seule case, qui occupe toute la couche. `minmax(0, 1fr)` plutot que
       `1fr` : sans le minimum a zero, une image trop grande pousserait la
       case au lieu d'etre reduite par elle. C'est cette case qui donne son
       sens au `max-height: 100%` de l'image. */
    grid-template: minmax(0, 1fr) / minmax(0, 1fr);
    place-items: center;
    /* Les encoches et barres gestuelles des telephones mangent les bords :
       on garde la marge la plus grande des deux, jamais la plus petite. */
    padding: max(clamp(.6rem, 4vw, 2.4rem), env(safe-area-inset-top))
             max(clamp(.6rem, 4vw, 2.4rem), env(safe-area-inset-right))
             max(clamp(.6rem, 4vw, 2.4rem), env(safe-area-inset-bottom))
             max(clamp(.6rem, 4vw, 2.4rem), env(safe-area-inset-left));
    overscroll-behavior: contain;
    /* Le curseur annonce la sortie partout ou un clic referme. */
    cursor: zoom-out;
}
.visionneuse[hidden] { display: none; }

.visionneuse__fond {
    position: absolute;
    inset: 0;
    background: rgba(6, 5, 14, .88);
    backdrop-filter: blur(16px) saturate(.85);
    opacity: 0;
    transition: opacity .38s var(--douce);
}
.visionneuse.ouverte .visionneuse__fond { opacity: 1; }

/* L'image est l'enfant DIRECT de la grille, sans emballage intermediaire.
   Ce n'est pas un detail de propriete : `max-height: 100%` ne veut rien dire
   dans un parent dont la hauteur depend de son contenu — le pourcentage n'a
   alors rien a quoi se rapporter, et l'image deborde de l'ecran. Rattachee
   a la case de la grille, qui a une taille connue, elle tient toujours. */
.visionneuse__image {
    position: relative;
    z-index: 1;
    display: block;
    grid-area: 1 / 1;
    max-width: 100%;
    max-height: 100%;
    width: auto;
    height: auto;
    object-fit: contain;
    border-radius: var(--r-m);
    border: 1px solid rgba(255, 255, 255, .10);
    box-shadow: 0 50px 110px -40px rgba(0, 0, 0, .95);
    background: #0A0913;
    /* Sur l'image, le clic ne fait rien : refermer par megarde en visant un
       detail serait la faute la plus agacante de tout le composant. */
    pointer-events: auto;
    cursor: default;
    transform-origin: center center;
}
.visionneuse__fermer,
.visionneuse__fleche {
    position: absolute;
    z-index: 2;
    display: grid;
    place-items: center;
    /* 44 px : la plus petite cible qu'un pouce touche sans viser. */
    width: 44px; height: 44px;
    padding: 0;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, .14);
    background: rgba(16, 14, 30, .62);
    backdrop-filter: blur(10px);
    color: #fff;
    cursor: pointer;
    opacity: 0;
    transition: opacity .3s var(--douce), background .25s, border-color .25s, scale .25s var(--douce);
}
.visionneuse.ouverte .visionneuse__fermer,
.visionneuse.ouverte .visionneuse__fleche { opacity: 1; }
.visionneuse__fermer svg,
.visionneuse__fleche svg { width: 20px; height: 20px; }
/* `scale` separe de `transform` : les fleches sont deja translatees pour
   se centrer, et ecraser leur `transform` au survol les ferait sauter. */
@media (hover: hover) {
    .visionneuse__fermer:hover,
    .visionneuse__fleche:hover {
        background: rgba(168, 85, 247, .3);
        border-color: rgba(201, 165, 255, .5);
        scale: 1.08;
    }
}
.visionneuse__fermer:focus-visible,
.visionneuse__fleche:focus-visible { outline: 2px solid var(--violet-clair); outline-offset: 3px; }
.visionneuse__fermer:active,
.visionneuse__fleche:active { scale: .94; transition-duration: .1s; }

/* Les commandes se posent par rapport a la boite de PADDING, donc au bord
   de l'ecran : le retrait et l'encoche se rattrapent ici, pas dans le
   padding de la couche. */
.visionneuse__fermer {
    top: max(.85rem, env(safe-area-inset-top));
    right: max(.85rem, env(safe-area-inset-right));
}
.visionneuse__fleche { top: 50%; transform: translateY(-50%); }
.visionneuse__fleche--prec { left: max(.6rem, env(safe-area-inset-left)); }
.visionneuse__fleche--suiv { right: max(.6rem, env(safe-area-inset-right)); }
.visionneuse__fleche[hidden] { display: none; }

.visionneuse__rang {
    position: absolute;
    top: max(.85rem, env(safe-area-inset-top));
    left: max(.85rem, env(safe-area-inset-left));
    z-index: 2;
    height: 44px;
    display: grid;
    place-items: center;
    padding: 0 .95rem;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, .12);
    background: rgba(16, 14, 30, .62);
    backdrop-filter: blur(10px);
    color: rgba(255, 255, 255, .88);
    font-size: .78rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: .05em;
    opacity: 0;
    transition: opacity .3s var(--douce);
}
.visionneuse.ouverte .visionneuse__rang { opacity: 1; }
.visionneuse__rang[hidden] { display: none; }

/* Sur telephone, les commandes se resserrent : c'est l'image qu'on est venu
   voir, pas les boutons autour. */
@media (max-width: 640px) {
    .visionneuse__fermer,
    .visionneuse__fleche { width: 42px; height: 42px; }
    .visionneuse__rang { height: 42px; font-size: .72rem; padding: 0 .8rem; }
}

/* ==================== MOUVEMENT REDUIT ====================
   Un visiteur qui a demande moins d'animations au systeme le fait pour une
   raison — vertiges, migraines, trouble vestibulaire. On coupe TOUT ce qui
   bouge, et l'on garde le site parfaitement utilisable : les elements
   reveles restent visibles, les panneaux s'ouvrent d'un coup. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
    }
    /* --entree remis a plat en meme temps que transform : depuis que l'entree
       passe par cette variable, l'annuler seule laisserait les elements
       decales de 26 px. */
    .reveal { opacity: 1; transform: none; --entree: translateY(0); }
    /* Le cadre agrandissable ne se souleve plus : il reste signale par le
       curseur, le bouton "Agrandir" et le liseré de focus. */
    .agrandissable:hover,
    .agrandissable:focus-visible,
    .agrandissable:active { transform: none; }
    .loupe { opacity: .92; transform: none; }
    /* Le champ magnetique est ce qui bouge le plus sur ce site : c'est la
       premiere chose a couper. */
    .bulle {
        translate: none !important;
        scale: none !important;
        transform: none !important;
    }
    .voile { display: none; }
}

/* Impression : le reglement et le wiki finissent parfois sur papier. */
@media print {
    body::before, body::after, .nav, .discord-flottant, .voxels, .pied { display: none !important; }
    body { background: #fff; color: #000; }
    .carte, .repli { border: 1px solid #ccc; box-shadow: none; background: #fff; }
    .repli__corps { grid-template-rows: 1fr !important; }
    * { color: #000 !important; }
}

/* ==================== SOUS-MENU DES PAGES LEGALES ====================
   Quatre textes qui se lisent ensemble : conditions de vente, remboursement,
   confidentialite, mentions legales. Un joueur qui cherche s'il peut se faire
   rembourser lira aussi la partie sur les mineurs.

   Le sous-menu les garde a un clic les unes des autres, et montre en
   permanence laquelle est ouverte. Il reprend le liseré de la barre du haut :
   un trait qui grandit depuis le centre, jamais depuis la gauche. */

.sous-menu {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    margin-bottom: clamp(2rem, 4vw, 3rem);
    padding: .45rem;
    background: linear-gradient(150deg, var(--surface), var(--fond-2));
    border: 1px solid var(--bord);
    border-radius: var(--r-l);
    box-shadow: var(--biseau);
}
.sous-menu a {
    position: relative;
    flex: 1 1 auto;
    padding: .7rem 1.1rem;
    font-size: .88rem;
    font-weight: 650;
    text-align: center;
    color: var(--texte-doux);
    border-radius: var(--r-m);
    transition: color .25s var(--net), background .3s var(--net);
}
.sous-menu a:hover { color: var(--texte); background: rgba(168,85,247,.10); }
.sous-menu a.actif {
    color: var(--texte);
    background: linear-gradient(135deg, rgba(168,85,247,.24), rgba(47,125,246,.14));
    box-shadow: var(--biseau), 0 12px 30px -18px rgba(168,85,247,.9);
}
.sous-menu a::after {
    content: '';
    position: absolute;
    left: 50%; bottom: .3rem;
    width: 0; height: 2px;
    background: linear-gradient(90deg, var(--violet), var(--bleu));
    border-radius: 2px;
    transform: translateX(-50%);
    transition: width .35s var(--douce);
}
.sous-menu a:hover::after, .sous-menu a.actif::after { width: 34%; }

@media (max-width: 720px) {
    .sous-menu { flex-direction: column; }
    .sous-menu a { text-align: left; }
    .sous-menu a::after { display: none; }
}