/* ════════════════════════════════════════════════════════════
   🏔️ ASCENSION — la tour, gravie chacun à son rythme
   ════════════════════════════════════════════════════════════ */

.asc {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 4.5rem clamp(1rem, 4vw, 3rem) 2rem;
    background:
        radial-gradient(62% 50% at 50% 40%, rgba(120, 170, 255, 0.05), transparent 72%),
        #08080b;
}

/* ══════════ Le décompte d'entrée ══════════ */
.asc-depart {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
}

.asc-depart-n {
    font-family: 'Bebas Neue', sans-serif;
    font-size: clamp(5rem, 18vh, 9rem);
    line-height: 0.85;
    color: #fff;
    text-shadow: 0 0 3rem rgba(140, 180, 255, 0.45);
    animation: ascBat 1s cubic-bezier(0.2, 1.1, 0.3, 1);
}

@keyframes ascBat {
    0%   { opacity: 0; transform: scale(1.5); }
    35%  { opacity: 1; transform: scale(0.96); }
    100% { opacity: 1; transform: scale(1); }
}

.asc-depart-l {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.3);
}

/* ══════════ L'en-tête : où j'en suis, et le temps ══════════ */
/* Le temps, en trait sur l'arête haute. Un nombre attire l'œil à chaque
   seconde ; un trait qui se vide se lit du coin de l'œil sans quitter le jeu. */
.asc-jauge {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 0.16rem;
    background: rgba(255, 255, 255, 0.05);
    z-index: 58;
}

/* Le fil se vide d'un seul geste. Piloté par la largeur, il avançait par
   pas de 250 ms : une animation le rend continu, et un délai négatif la
   place où il faut si l'on arrive en cours d'étage. */
.asc-jauge-fil {
    display: block;
    width: 100%;
    height: 100%;
    transform-origin: left center;
    background: linear-gradient(90deg, rgba(140, 180, 255, 0.5), rgba(140, 180, 255, 0.95));
    box-shadow: 0 0 0.7rem rgba(140, 180, 255, 0.4);
    animation-name: ascCoule;
    animation-timing-function: linear;
    animation-fill-mode: forwards;
    transition: background 0.4s ease, box-shadow 0.4s ease;
}

@keyframes ascCoule {
    from { transform: scaleX(1); }
    to   { transform: scaleX(0); }
}

.asc-jauge.urgence .asc-jauge-fil {
    background: linear-gradient(90deg, rgba(255, 100, 100, 0.6), #ff5a5a);
    box-shadow: 0 0 0.9rem rgba(255, 90, 90, 0.55);
}

/* L'étage dans son coin : c'est un repère, pas une annonce */
.asc-etage {
    position: fixed;
    top: clamp(1rem, 2.6vh, 1.7rem);
    left: clamp(1.1rem, 3vw, 2.2rem);
    display: flex;
    align-items: baseline;
    gap: 0.35rem;
    z-index: 55;
    font-family: 'Bebas Neue', sans-serif;
}

.asc-etage-ic {
    width: 1.25rem;
    height: 1.25rem;
    align-self: center;
    margin-right: 0.1rem;
    color: rgba(140, 180, 255, 0.6);
}

.asc-etage b { font-size: 2.2rem; font-weight: 400; color: #fff; }
.asc-etage i { font-style: normal; font-size: 1.2rem; color: rgba(255, 255, 255, 0.28); }

/* Le nom de l'épreuve, en grand : c'est la seule chose à lire en arrivant
   sur un étage, autant qu'elle se prenne d'un coup d'œil. */
/* Le nom de l'épreuve et, sous lui, ce qu'elle demande. Un filet les
   sépare sans les détacher : c'est un titre et sa légende, pas deux
   informations. */
.asc-entete {
    position: fixed;
    top: clamp(2.1rem, 5vh, 3.4rem);
    left: 50%;
    transform: translateX(-50%);
    z-index: 55;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.75rem;
    animation: ascTitre 0.5s cubic-bezier(0.2, 0.9, 0.25, 1) both;
}

@keyframes ascTitre {
    from { opacity: 0; transform: translateX(-50%) translateY(-0.6rem); }
    to   { opacity: 1; transform: translateX(-50%); }
}

.asc-titre {
    font-family: 'Bebas Neue', sans-serif;
    font-size: clamp(1.3rem, 2.7vw, 2rem);
    line-height: 1;
    letter-spacing: 0.06em;
    color: #fff;
    white-space: nowrap;
}

/* Le filet : une pastille au milieu d'un trait qui s'efface aux deux bouts */
.asc-filet {
    position: relative;
    width: clamp(5rem, 12vw, 9rem);
    height: 0.06rem;
    background: linear-gradient(90deg, transparent, rgba(140, 180, 255, 0.45), transparent);
}

.asc-filet::after {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    width: 0.28rem;
    height: 0.28rem;
    border-radius: 50%;
    background: rgba(140, 180, 255, 0.8);
    box-shadow: 0 0 0.5rem rgba(140, 180, 255, 0.6);
    transform: translate(-50%, -50%);
}

.asc-soustitre {
    margin-top: 0.3rem;
    font-size: 0.85rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-align: center;
    color: rgba(255, 255, 255, 0.42);
}

/* ══════════ L'épreuve ══════════ */
/* L'épreuve se centre sur l'écran entier. La tour est posée par-dessus,
   hors du flux : dans le flux elle poussait tout vers la gauche, et le jeu
   se jouait de travers. */
/* L'épreuve se centre sur l'écran entier, sans marge : c'est sa largeur
   maximale qui l'empêche de courir sous la tour, pas un décalage. */
.asc-epreuve {
    width: 100%;
    display: grid;
    place-items: center;
    min-width: 0;
}

.asc-attente,
.asc-brouillon {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.4rem;
    color: rgba(255, 255, 255, 0.3);
}

.asc-brouillon-t {
    font-family: 'Bebas Neue', sans-serif;
    font-size: 2.4rem;
    color: rgba(140, 180, 255, 0.6);
    letter-spacing: 0.08em;
}

.asc-brouillon-s {
    font-size: 0.66rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
}

/* ══════════ La tour ══════════
   Un fût plein, un palier par étage, et sur chacun le nombre de grimpeurs
   qui s'y trouvent. Des jetons nominatifs cessent de se lire au-delà d'une
   poignée de joueurs ; un compte tient à cinquante comme à trois. */
.asc-tour {
    --fut-x: 2.2rem;
    --fut-w: 2.6rem;
    /* Ce que le rembourrage retire en haut et en bas. Nul ici, pas sur
       téléphone — voir la note sur « .asc-cabines ». */
    --tour-haut: 0rem;
    --tour-bas: 0rem;

    position: absolute;
    top: 3.6rem;
    bottom: 1.4rem;
    right: 0.9rem;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    width: 4.9rem;
}

/* Le fût, en fond : il court d'un bout à l'autre de la colonne, les paliers
   se posant dessus. Sa base est plus dense que son sommet — on part du sol.
   Sa gouttière est décrite une seule fois, en variables sur « .asc-tour » :
   les cabines doivent tomber exactement dedans, et deux jeux de valeurs à
   tenir d'accord finissent toujours par diverger. */
.asc-fut {
    position: absolute;
    left: var(--fut-x);
    top: -0.2rem;
    bottom: -0.2rem;
    width: var(--fut-w);
    border-radius: 0.35rem 0.35rem 0 0;
    background: linear-gradient(180deg,
        rgba(140, 180, 255, 0.03),
        rgba(140, 180, 255, 0.09));
    box-shadow: inset 0 0 0 0.06rem rgba(255, 255, 255, 0.07);
}

/* La pastille : sur téléphone la tour se replie dedans, et elle porte en
   permanence l'étage où l'on est. Sur grand écran la tour a sa colonne, la
   pastille n'a rien à dire. */
.asc-pastille,
.asc-voile { display: none; }

.asc-etg {
    position: relative;
    flex: 1;
    min-height: 0;
    display: flex;
    align-items: center;
}

/* Le numéro, dehors et discret : c'est une graduation, pas une information */
.asc-etg-n {
    position: absolute;
    left: 0;
    /* Plus court que la marge : le reste sert d'écart entre le numéro et la
       pointe, qui se collaient l'un à l'autre. */
    width: 1.25rem;
    text-align: right;
    font-family: 'Bebas Neue', sans-serif;
    font-size: 0.78rem;
    color: rgba(255, 255, 255, 0.16);
    font-variant-numeric: tabular-nums;
    transition: color 0.3s ease;
}

/* Le plancher du palier */
.asc-etg-trait {
    position: absolute;
    left: 2.2rem;
    width: 2.6rem;
    bottom: 0;
    height: 0.06rem;
    background: rgba(255, 255, 255, 0.08);
    transition: background 0.3s ease;
}

/* ── Les cabines ──
   Combien de monde à chaque palier. Une pastille ovale par palier
   renseignait, mais elle naissait et mourait sur place : on ne voyait jamais
   personne monter, seulement deux états. La cabine, elle, REMONTE le fût.
   C'est le seul endroit du jeu où la progression est un geste.

   ⚠️ Une boîte absolue se place sur la BOÎTE DE REMBOURRAGE de son parent, pas
   sur son contenu. Sur téléphone la tour porte 3,2 rem en haut et 4,6 en bas
   pour dégager les commandes : sans « --tour-haut » et « --tour-bas », les
   cabines glisseraient d'autant par rapport aux paliers qu'elles désignent. */
.asc-cabines {
    position: absolute;
    left: var(--fut-x);
    width: var(--fut-w);
    top: var(--tour-haut);
    bottom: var(--tour-bas);
    pointer-events: none;
}

/* 🖼️ Un jeton rond par grimpeur, décalé de son voisin pour qu ils se
   chevauchent. La largeur vient de « gauche », calculé dans ascCabines :
   le groupe reste centré dans la gouttière quel que soit l effectif. */
.asc-cabine {
    position: absolute;
    width: 1.05rem;
    height: 1.05rem;
    /* La moitié de sa hauteur : le « top » vise le milieu du palier. */
    margin-top: -0.525rem;
    display: grid;
    place-items: center;
    overflow: hidden;
    border-radius: 50%;
    /* Le cerne sépare deux jetons qui se recouvrent : sans lui ils se
       fondent en une seule tache. Il reprend le fond de la tour. */
    box-shadow: 0 0 0 1.5px #0b0b0e, 0 0.1rem 0.35rem rgba(0, 0, 0, 0.55);
    background: rgba(140, 180, 255, 0.9);
    transition: top 0.6s cubic-bezier(0.3, 0.9, 0.25, 1);
    animation: ascCabine 0.4s cubic-bezier(0.2, 1.3, 0.35, 1) both;
}

.asc-cabine img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* La pastille de reste : un compte, pas un visage. */
.asc-cabine.plus {
    background: rgba(255, 255, 255, 0.16);
    backdrop-filter: blur(2px);
}

.asc-cabine.plus b {
    font-family: 'Bebas Neue', sans-serif;
    font-size: 0.62rem;
    line-height: 1;
    color: rgba(255, 255, 255, 0.9);
}

/* Moi : je passe devant les autres et je porte la couleur de la tour. */
.asc-cabine.moi {
    z-index: 3;
    box-shadow: 0 0 0 1.5px var(--neon-primary, #ffd700),
                0 0.1rem 0.35rem rgba(0, 0, 0, 0.55),
                0 0 0.7rem rgba(255, 215, 0, 0.45);
}

@keyframes ascCabine {
    from { opacity: 0; transform: scale(0.5); }
    to   { opacity: 1; transform: scale(1); }
}

/* Le sommet : c'est là qu'on va, autant que ça se voie */
.asc-etg.sommet .asc-etg-trait { background: rgba(255, 215, 0, 0.4); }
.asc-etg.sommet .asc-etg-n { color: rgba(255, 215, 0, 0.55); }

/* Ma place. Le seul repère qui compte quand on grimpe vite : elle porte un
   mot plutôt qu'une couleur, pour qu'on la trouve sans avoir à chercher. */
.asc-etg.moi .asc-etg-n { color: #fff; }

.asc-etg.moi .asc-etg-trait {
    height: 0.11rem;
    background: var(--neon-primary, #ffd700);
    box-shadow: 0 0 0.7rem rgba(255, 215, 0, 0.5);
}



/* Ma place se marque d'une pointe dans la marge des numéros, et non d'un
   mot : « Vous » forçait la colonne à réserver sa largeur, ce qui poussait
   tout le jeu vers la gauche. La pointe ne coûte rien. */
.asc-etg.moi::before {
    content: '';
    position: absolute;
    left: 1.9rem;
    top: 50%;
    width: 0;
    height: 0;
    border-top: 0.3rem solid transparent;
    border-bottom: 0.3rem solid transparent;
    border-left: 0.36rem solid var(--neon-primary, #ffd700);
    transform: translateY(-50%);
    filter: drop-shadow(0 0 0.35rem rgba(255, 215, 0, 0.6));
    animation: ascRepere 0.45s cubic-bezier(0.2, 1.3, 0.35, 1) both;
}

@keyframes ascRepere {
    from { opacity: 0; transform: translateY(-50%) translateX(-0.35rem); }
    to   { opacity: 1; transform: translateY(-50%); }
}

/* ══════════ Les épreuves ══════════
   Un cadre commun : la consigne, l'indice, puis le plateau. Chaque type
   remplit le plateau à sa façon, mais l'entrée en matière est la même —
   à quinze étages, changer de repères à chaque fois serait épuisant. */
.asc-jeu {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: clamp(0.8rem, 2.5vh, 1.5rem);
    width: 100%;
    max-width: 46rem;
}

.asc-quoi {
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    text-align: center;
    color: rgba(255, 255, 255, 0.4);
}

.asc-indice {
    margin-top: -0.6rem;
    font-size: 0.8rem;
    font-weight: 600;
    color: rgba(140, 180, 255, 0.85);
}

.asc-jeu.ko { animation: ascSecoue 0.4s ease; }

@keyframes ascSecoue {
    0%, 100% { transform: translateX(0); }
    20%      { transform: translateX(-0.5rem); }
    45%      { transform: translateX(0.45rem); }
    70%      { transform: translateX(-0.22rem); }
}

/* ── La case à lettre, commune aux deux épreuves ── */
.asc-case {
    display: grid;
    place-items: center;
    width: clamp(2rem, 5.5vw, 3rem);
    height: clamp(2.4rem, 6.5vw, 3.4rem);
    font-family: 'Bebas Neue', sans-serif;
    font-size: clamp(1.2rem, 3vw, 1.8rem);
    color: rgba(255, 255, 255, 0.9);
    background: rgba(255, 255, 255, 0.035);
    border: 0.09rem solid rgba(255, 255, 255, 0.1);
    border-radius: 0.4rem;
    padding: 0;
    transition: border-color 0.2s ease, background 0.2s ease, transform 0.2s ease;
}

.asc-case.pleine { border-color: rgba(255, 255, 255, 0.28); }

/* Une coupure marque la fin d'une partie du mot, sans rien changer au mot */
.asc-case.coupe { margin-right: clamp(0.5rem, 2vw, 1.1rem); }

/* ── Anagramme ── */
.asc-fentes,
.asc-reserve,
.asc-ligne {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.35rem;
}

.asc-fentes .asc-case { cursor: pointer; }

/* Une lettre que le serveur a déclarée bien placée ne se reprend plus :
   sans ce verrou, on défaisait par mégarde ce qu'on venait de trouver. */
.asc-fentes .asc-case.figee {
    color: #6ee7b7;
    background: rgba(110, 231, 183, 0.1);
    border-color: rgba(110, 231, 183, 0.5);
    cursor: default;
}

/* La réserve garde sa hauteur une fois vidée : sans cela, le plateau étant
   centré, les cases posées redescendaient au moment même où l'on finissait
   — juste sous les yeux, à l'instant le plus mal choisi. */
.asc-reserve {
    margin-top: 0.3rem;
    min-height: clamp(2.4rem, 6.5vw, 3.4rem);
}

.asc-lettre {
    display: grid;
    place-items: center;
    width: clamp(2rem, 5.5vw, 3rem);
    height: clamp(2.4rem, 6.5vw, 3.4rem);
    font-family: 'Bebas Neue', sans-serif;
    font-size: clamp(1.2rem, 3vw, 1.8rem);
    color: rgba(255, 250, 235, 0.95);
    background: rgba(140, 180, 255, 0.12);
    border: 0.09rem solid rgba(140, 180, 255, 0.3);
    border-radius: 0.4rem;
    cursor: pointer;
    transition: background 0.18s ease, transform 0.18s ease;
}

.asc-lettre:hover { background: rgba(140, 180, 255, 0.24); transform: translateY(-0.12rem); }
.asc-lettre:active { transform: translateY(0); }

/* ── Wordle ── */
.asc-case.green  { color: #0b0b0e; background: #6ee7b7; border-color: #6ee7b7; }
.asc-case.yellow { color: #0b0b0e; background: #fcd34d; border-color: #fcd34d; }
.asc-case.red    { color: rgba(255, 255, 255, 0.35); background: rgba(255, 255, 255, 0.02); }

/* Les essais tiennent dans un cadre de hauteur fixe. Sans lui, le bloc
   grandissait à chaque tentative et, comme il est centré, la ligne qu'on
   remplit remontait sous les doigts. */
.asc-essais {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    width: 100%;
    height: clamp(12rem, 46vh, 26rem);
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: rgba(140, 180, 255, 0.3) transparent;
}

.asc-ligne.vive { cursor: text; }

/* Le curseur : la case qui attend la prochaine lettre */
.asc-ligne.vive .asc-case.active {
    border-color: var(--neon-primary, #ffd700);
    box-shadow: 0 0 0.8rem rgba(255, 215, 0, 0.2);
}

/* Le champ existe vraiment mais ne se voit pas : c'est lui qui ouvre le
   clavier des téléphones, qu'aucun écouteur de touches ne ferait paraître. */
.asc-champ {
    position: absolute;
    opacity: 0;
    pointer-events: none;
    width: 1px;
    height: 1px;
}

.asc-aide {
    font-size: 0.64rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.22);
}

/* ══════════ Téléphone ══════════
   La colonne se couche au-dessus de l'épreuve : debout, elle prendrait la
   moitié de la largeur et il ne resterait rien pour jouer. Couchée, elle
   garde ce qui compte — le nombre par étage, et où l'on se trouve. */
@media (max-width: 900px) {
    /* Le jeu retrouve le milieu de l'écran : la tour ne lui prend plus une
       bande en haut. Il déborde parfois — vingt-quatre vignettes, dix lettres
       — alors on le laisse défiler plutôt que de le rogner. */
    .asc {
        flex-direction: column;
        padding: calc(6.4rem + env(safe-area-inset-top, 0px)) 0.7rem 4.5rem;
        overflow-y: auto;
        overflow-x: hidden;
        /* « safe » et non « center » tout court : quand le plateau dépasse la
           hauteur, un centrage ordinaire coupe le haut et le rend inatteignable
           au défilement. Le mot-clé bascule alors sur un alignement en tête. */
        justify-content: center;
        justify-content: safe center;
    }

    .asc-epreuve { padding: 0; }
    /* ── Le temps qui s ecoule ──
       Deux pixels et demi d un rail a cinq pour cent d opacite, au bord extreme
       de l ecran : sur telephone on ne le voyait tout simplement pas, et une
       fois la barre a moitie vidange il ne restait rien a remarquer. Elle
       triple d epaisseur, son rail devient perceptible meme vide, et elle
       descend sous l encoche — sous laquelle « viewport-fit=cover » la fait
       maintenant passer. */
    .asc-jauge {
        top: env(safe-area-inset-top, 0px);
        height: 0.55rem;
        background: rgba(255, 255, 255, 0.12);
        box-shadow: inset 0 -0.06rem 0 rgba(0, 0, 0, 0.4);
    }

    /* Franc et opaque : degrade a demi transparent, le trait paraissait brumeux
       sur le noir de la page. */
    .asc-jauge-fil {
        border-radius: 0 0.28rem 0.28rem 0;
        background: linear-gradient(90deg, #7fb4ff, #c2d9ff);
        box-shadow: 0 0 1.1rem rgba(140, 180, 255, 0.85);
    }

    .asc-jauge.urgence .asc-jauge-fil {
        background: linear-gradient(90deg, rgba(255, 110, 110, 0.9), #ff7676);
        box-shadow: 0 0 1.1rem rgba(255, 90, 90, 0.8);
    }

    /* L en-tete se tasse et recule d un demi-centimetre : colle sous la jauge,
       il la faisait passer pour de la chrome systeme — a cote de l encoche, on
       ne la lisait plus comme un element de la page. */
    .asc-etage {
        top: calc(1.5rem + env(safe-area-inset-top, 0px));
        left: 0.8rem;
    }
    .asc-etage b { font-size: 1.1rem; }
    .asc-etage i { font-size: 0.85rem; }
    .asc-etage-ic { width: 1rem; height: 1rem; }
    .asc-entete { top: calc(2.8rem + env(safe-area-inset-top, 0px)); gap: 0.4rem; }
    .asc-titre { font-size: 1.12rem; }
    .asc-filet { width: 4rem; }
    .asc-soustitre { margin-top: 0.15rem; font-size: 0.72rem; max-width: 88vw; }

    /* ── La tour se replie dans un coin ──
       Couchée en rail au-dessus du jeu, elle mangeait une bande entière pour
       une information qu'on ne consulte qu'entre deux étages. Elle devient une
       pastille — qui garde l'étage sous les yeux — et se déplie par-dessus le
       jeu, en transparence, le temps qu'on la regarde. */
    .asc-pastille {
        display: flex;
        position: fixed;
        right: 0.9rem;
        bottom: 1rem;
        z-index: 60;
        align-items: center;
        justify-content: center;
        width: 3rem;
        height: 3rem;
        padding: 0;
        color: rgba(140, 180, 255, 0.75);
        background: rgba(18, 20, 28, 0.92);
        border: 0.09rem solid rgba(140, 180, 255, 0.28);
        border-radius: 50%;
        box-shadow: 0 0.4rem 1.2rem rgba(0, 0, 0, 0.5);
        transition: transform 0.2s ease, border-color 0.2s ease, color 0.2s ease;
    }

    .asc-pastille.ouverte {
        color: var(--neon-primary, #ffd700);
        border-color: rgba(255, 215, 0, 0.6);
        transform: scale(1.06);
    }

    /* La tour en filigrane, le nombre par-dessus : l'icône dit de quoi il
       s'agit, le nombre dit où l'on en est. */
    .asc-pastille-ic {
        position: absolute;
        width: 1.9rem;
        height: 1.9rem;
        fill: none;
        stroke: currentColor;
        stroke-width: 1.4;
        stroke-linecap: round;
        stroke-linejoin: round;
        opacity: 0.28;
    }

    .asc-pastille-n {
        position: relative;
        display: flex;
        align-items: baseline;
        font-family: 'Bebas Neue', sans-serif;
        font-size: 1.35rem;
        line-height: 1;
        color: #fff;
    }

    .asc-pastille-n b { font-weight: 400; }
    .asc-pastille-n i { font-style: normal; font-size: 0.7rem; color: rgba(255, 255, 255, 0.4); }

    .asc-voile {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 56;
        background: rgba(6, 6, 9, 0.5);
    }

    /* Dressée comme sur grand écran, mais posée par-dessus et retirée tant
       qu'on ne la demande pas. */
    .asc-tour {
        /* Repris par « .asc-cabines » : elles doivent s'arrêter là où les
           paliers s'arrêtent, et non là où la tour s'arrête. */
        --tour-haut: 3.2rem;
        --tour-bas: 4.6rem;

        position: fixed;
        top: 0;
        bottom: 0;
        right: 0;
        z-index: 58;
        width: 5.4rem;
        padding: var(--tour-haut) 0.4rem var(--tour-bas);
        background: rgba(10, 12, 18, 0.82);
        backdrop-filter: blur(0.4rem);
        border-left: 0.06rem solid rgba(255, 255, 255, 0.07);
        opacity: 0;
        pointer-events: none;
        transform: translateX(100%);
        transition: transform 0.28s cubic-bezier(0.2, 0.9, 0.25, 1), opacity 0.2s ease;
    }

    .asc-tour.ouverte {
        opacity: 1;
        pointer-events: auto;
        transform: none;
    }
}

/* ══════════ L'arrivée sur un étage ══════════
   Le fil du temps part le premier — c'est lui qui commande —, puis le nom,
   puis le plateau. Trois temps très courts : on doit sentir l'ordre sans
   attendre quoi que ce soit. */
/* ⚠️ Pas d'animation ici. Le plan est enveloppé d'une « transition », qui
   l'anime déjà à chaque changement d'étage. En posant une animation
   permanente en plus, les deux se jouaient ensemble et le jeu semblait
   glisser deux fois — c'était le « double rafraîchissement ». */
.asc-plan {
    display: grid;
    place-items: center;
    width: 100%;
}

@keyframes ascPlan {
    from { opacity: 0; transform: translateY(1.1rem) scale(0.985); }
    to   { opacity: 1; transform: none; }
}

/* Le passage d'un étage au suivant : le plan sortant descend — on vient de
   le quitter par le haut —, le suivant monte à sa place. */
.asc-pas-enter-active { animation: ascPasEntre 0.4s cubic-bezier(0.2, 0.95, 0.25, 1) both; }
.asc-pas-leave-active { animation: ascPasSort 0.22s ease both; }

@keyframes ascPasEntre {
    from { opacity: 0; transform: translateY(2.2rem); }
    to   { opacity: 1; transform: none; }
}

@keyframes ascPasSort {
    from { opacity: 1; transform: none; }
    to   { opacity: 0; transform: translateY(-1.6rem); }
}

/* Les éléments d'une épreuve entrent l'un après l'autre, derrière le plan */
.asc-jeu > * { animation: ascPlan 0.4s cubic-bezier(0.2, 0.95, 0.25, 1) both; }
.asc-jeu > *:nth-child(1) { animation-delay: 0.20s; }
.asc-jeu > *:nth-child(2) { animation-delay: 0.27s; }
.asc-jeu > *:nth-child(3) { animation-delay: 0.34s; }
.asc-jeu > *:nth-child(4) { animation-delay: 0.41s; }
.asc-jeu > *:nth-child(n + 5) { animation-delay: 0.46s; }

/* ── L'étage tombe ──
   Un éclat vert sur tout ce qu'on vient de remplir, l'instant d'avant de
   passer au suivant. Sans lui, l'écran changeait sans qu'on sache si l'on
   avait réussi ou si le temps s'était écoulé. Il tient dans le demi-battement
   que le serveur laisse avant d'avancer : plus long, il serait coupé. */
@keyframes ascValide {
    0%   { outline-color: rgba(110, 231, 183, 0); }
    28%  { outline-color: #6ee7b7; }
    100% { outline-color: rgba(110, 231, 183, 0); }
}

.asc-plan.reussi .asc-vitre,
.asc-plan.reussi .asc-vignette,
.asc-plan.reussi .asc-carte-arc,
.asc-plan.reussi .asc-noeud,
.asc-plan.reussi .asc-fentes .asc-case,
.asc-plan.reussi .asc-ligne.vive .asc-case {
    outline: 0.15rem solid transparent;
    outline-offset: 0.18rem;
    /* Sans « both » : hors du passage, chacun garde ses propres couleurs —
       autrement l'animation figeait un contour transparent par-dessus. */
    animation: ascValide 0.34s cubic-bezier(0.3, 0.9, 0.3, 1);
}

/* Le plateau ne doit pas courir sous la tour : c'est sa largeur qui l'en
   empêche, non un décalage — l'épreuve reste ainsi centrée sur l'écran. */
/* La largeur est bornée pour que le plateau ne coure pas sous la tour —
   c'est elle qui garde l'épreuve centrée, sans décalage. Les grilles de
   vingt-quatre en réclament plus que cinq portraits : à 46 rem, leurs
   vignettes tombaient sous les cinquante pixels. */
.asc-jeu { max-width: min(46rem, 100vw - 20rem); }
/* Le compteur est posé à droite plutôt qu'en dessous : la rangée de
   portraits récupère la hauteur, et le bloc reste centré sur l'écran —
   c'est le portrait du milieu qui tombe sur l'axe, pas le tout. */
.asc-jeu.asc-devine {
    position: relative;
    max-width: min(72rem, 100vw - 14rem);
}

.asc-aide.asc-cote {
    position: absolute;
    left: calc(100% + 1.4rem);
    top: 50%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.1rem;
    transform: translateY(-50%);
    font-family: 'Bebas Neue', sans-serif;
    font-size: 2.4rem;
    line-height: 1;
    letter-spacing: 0;
    text-transform: none;
    color: rgba(140, 180, 255, 0.85);
}

.asc-aide.asc-cote em {
    font-family: 'Inter', sans-serif;
    font-style: normal;
    font-size: 0.55rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.28);
}
/* Quatre rangées de vingt-quatre : c'est la hauteur qui contraint, pas la
   largeur. On la borne donc par la fenêtre — sans quoi la grille passait
   sous le bord bas et remontait par-dessus le titre. Le facteur 0,92 tient
   compte du format des portraits et des gouttières. */
.asc-jeu.asc-cible,
.asc-jeu.asc-intrus {
    max-width: min(62rem, 100vw - 30rem, calc((100vh - 9rem) * 0.92));
}

@media (max-width: 900px) {
    .asc-jeu { max-width: 100%; }
}

/* La tour entre avec le reste, une fois le décompte fini. Elle paraît en
   dernier — c'est le décor de la course, pas ce qu'on vient y faire. */
.asc-tour { animation: ascTour 0.5s cubic-bezier(0.2, 0.95, 0.25, 1) 0.28s both; }

@keyframes ascTour {
    from { opacity: 0; transform: translateX(1.2rem); }
    to   { opacity: 1; transform: none; }
}

@media (max-width: 900px) {
    /* Repliée dans sa pastille, la tour n'a pas d'entrée à jouer. Et cette
       animation-là, figée sur « opacity: 1 » par son mode de remplissage,
       l'emportait sur le retrait du panneau : la tour restait dépliée sans
       qu'on l'ait demandée. Une animation bat toujours une déclaration. */
    .asc-tour { animation: none; }
}

@media (max-width: 900px) {
    @keyframes ascTour {
        from { opacity: 0; transform: translateY(-0.9rem); }
        to   { opacity: 1; transform: none; }
    }
}

/* ══════════ Les trois épreuves à portraits ══════════
   Elles partagent leur vocabulaire : une vignette, un état trouvé, un état
   raté. Ce qui change, c'est ce qu'on en fait — nommer, viser, débusquer. */

/* ── Devine le perso ── */
/* Les cinq sur une seule ligne : sur deux rangées, on perdait le fil de ce
   qu'on avait déjà nommé. */
.asc-portraits {
    display: flex;
    flex-wrap: nowrap;
    justify-content: center;
    gap: clamp(0.8rem, 2.2vw, 1.6rem);
    width: 100%;
}

.asc-portrait {
    position: relative;
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    max-width: 13.5rem;
}

/* ── L'ampoule ──
   Un joker par étage, coiffant chaque portrait. Elle se remplit d'un tiers à
   chaque nom trouvé ; pleine, elle livre celui qu'elle coiffe. Elle déborde du
   cadre plutôt que de prendre une rangée : autrement la grille sautait d'un
   cran quand toutes s'éteignent. */
.asc-ampoule {
    position: absolute;
    top: -3.2rem;
    left: 50%;
    z-index: 2;
    width: 1.65rem;
    height: 2.4rem;
    padding: 0;
    background: none;
    border: none;
    cursor: default;
    opacity: 0.5;
    transform: translateX(-50%);
    transition: opacity 0.3s ease, transform 0.25s ease;
}

.asc-ampoule svg { width: 100%; height: 100%; overflow: visible; }

.asc-ampoule-verre {
    fill: none;
    stroke: rgba(255, 255, 255, 0.3);
    stroke-width: 3;
}

/* Le liquide monte par paliers, mais il monte : d'un palier à l'autre on voit
   la charge grimper, ce qu'un saut sec n'aurait pas dit. */
.asc-ampoule-jus {
    fill: rgba(255, 215, 0, 0.5);
    transition: y 0.45s cubic-bezier(0.3, 0.9, 0.3, 1),
                height 0.45s cubic-bezier(0.3, 0.9, 0.3, 1),
                fill 0.3s ease;
}

.asc-ampoule-fil {
    fill: none;
    stroke: rgba(255, 255, 255, 0.32);
    stroke-width: 2.5;
    stroke-linecap: round;
}

.asc-ampoule-culot {
    stroke: rgba(255, 255, 255, 0.28);
    stroke-width: 3;
    stroke-linecap: round;
}

/* Pleine : elle s'allume et respire — c'est le moment de s'en servir */
.asc-ampoule.prete {
    cursor: pointer;
    opacity: 1;
    animation: ascAmpoule 1.9s ease-in-out infinite;
}

.asc-ampoule.prete .asc-ampoule-jus { fill: #ffd700; }
.asc-ampoule.prete .asc-ampoule-verre { stroke: rgba(255, 215, 0, 0.85); }
.asc-ampoule.prete .asc-ampoule-fil { stroke: rgba(70, 50, 0, 0.6); }
.asc-ampoule.prete svg { filter: drop-shadow(0 0 0.4rem rgba(255, 215, 0, 0.6)); }
.asc-ampoule.prete:hover { transform: translateX(-50%) scale(1.2); }

@keyframes ascAmpoule {
    0%, 100% { filter: brightness(0.9); }
    50%      { filter: brightness(1.25); }
}

/* On s'en sert : un éclat, puis toutes quittent la scène. C'est la seule fois
   du jeu où l'on gagne quelque chose sans l'avoir trouvé — autant que ça se
   voie. Placée après « prete » : à égalité de poids, c'est l'ordre qui tranche. */
.asc-ampoule.casse {
    pointer-events: none;
    animation: ascAmpouleCasse 0.62s cubic-bezier(0.2, 0.9, 0.3, 1) forwards;
}

@keyframes ascAmpouleCasse {
    0%   { opacity: 1; transform: translateX(-50%) scale(1); }
    20%  { opacity: 1; transform: translateX(-50%) scale(1.4); filter: brightness(2.6); }
    100% { opacity: 0; transform: translateX(-50%) scale(0.45) rotate(16deg); }
}

.asc-portrait img {
    width: 100%;
    aspect-ratio: 225 / 350;
    object-fit: cover;
    border-radius: 0.4rem;
    background: rgba(255, 255, 255, 0.03);
    box-shadow: 0 0 0 0.06rem rgba(255, 255, 255, 0.08);
    transition: box-shadow 0.3s ease, filter 0.3s ease;
}

/* Trouvé : le portrait s'éteint doucement, sa saisie se verrouille. Il reste
   visible — on veut voir ce qu'on a déjà fait, pas le faire disparaître. */
.asc-portrait.trouve img {
    box-shadow: 0 0 0 0.11rem rgba(110, 231, 183, 0.7);
    filter: saturate(0.55) brightness(0.75);
}

.asc-portrait.rate img {
    box-shadow: 0 0 0 0.11rem rgba(255, 100, 100, 0.75);
    animation: ascSecoue 0.4s ease;
}

.asc-nom {
    width: 100%;
    padding: 0.75rem 0.4rem;
    font-family: inherit;
    font-size: 1.05rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    /* Saisie en capitales : le nom se lit d'un bloc, et la casse n'a de
       toute façon aucune importance pour le serveur. */
    text-transform: uppercase;
    text-align: center;
    color: rgba(255, 250, 235, 0.95);
    background: rgba(255, 255, 255, 0.04);
    border: none;
    border-bottom: 0.11rem solid rgba(140, 180, 255, 0.35);
    border-radius: 0.3rem 0.3rem 0 0;
    outline: none;
    transition: background 0.2s ease, border-color 0.2s ease;
}

.asc-nom:focus { background: rgba(255, 255, 255, 0.07); border-bottom-color: rgba(140, 180, 255, 0.9); }

.asc-nom:disabled {
    color: #6ee7b7;
    background: rgba(110, 231, 183, 0.08);
    border-bottom-color: rgba(110, 231, 183, 0.5);
}

/* ── La grille des vingt-quatre ── */
/* Six colonnes plutôt que huit : vingt-quatre portraits tiennent en quatre
   rangées, et chaque vignette gagne un tiers de largeur. */
/* La sortie du blocage se fond : sans transition, l'ombre et le voile
   sautaient d'un coup, et l'œil lisait ce saut comme un clignotement. */
.asc-grille {
    transition: box-shadow 0.35s ease;
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: clamp(0.35rem, 0.9vw, 0.6rem);
    width: 100%;
}

.asc-vignette {
    padding: 0;
    border: none;
    background: none;
    border-radius: 0.35rem;
    cursor: pointer;
    overflow: hidden;
    transition: transform 0.18s ease;
}

.asc-vignette img {
    display: block;
    width: 100%;
    aspect-ratio: 225 / 350;
    object-fit: cover;
    border-radius: 0.35rem;
    box-shadow: 0 0 0 0.06rem rgba(255, 255, 255, 0.07);
    transition: box-shadow 0.25s ease, filter 0.25s ease;
}

.asc-vignette:hover:not(:disabled) { transform: translateY(-0.2rem); }
.asc-vignette:hover:not(:disabled) img { box-shadow: 0 0 0 0.09rem rgba(140, 180, 255, 0.55); }

/* Le clic s'enfonce vraiment : sans ce creux, on doutait d'avoir cliqué */
.asc-vignette:active:not(:disabled) { transform: scale(0.93); }

/* Trouvé : la vignette encaisse le coup puis s'éteint. Le sursaut n'est pas
   décoratif — c'est lui qui dit « c'est bien celle-là » sans un mot. */
.asc-vignette.trouve { animation: ascPris 0.45s cubic-bezier(0.2, 1.5, 0.35, 1); }

.asc-vignette.trouve img {
    box-shadow: 0 0 0 0.16rem rgba(110, 231, 183, 0.9),
                0 0 1.4rem rgba(110, 231, 183, 0.35);
    filter: saturate(0.5) brightness(0.65);
}

@keyframes ascPris {
    0%   { transform: scale(0.9); }
    45%  { transform: scale(1.08); }
    100% { transform: scale(1); }
}

.asc-vignette.rate img {
    box-shadow: 0 0 0 0.16rem rgba(255, 100, 100, 0.9),
                0 0 1.4rem rgba(255, 90, 90, 0.3);
    animation: ascSecoue 0.4s ease;
}

.asc-vignette:disabled { cursor: default; }

/* ── Cible : ce qu'il faut viser, et où l'on en est ── */
/* La consigne se pose à droite de la grille, hors du flux : au-dessus, elle
   lui prenait une bande de hauteur — et c'est la hauteur qui contraint une
   grille de quatre rangées. La grille reste centrée sur l'écran. */
.asc-jeu.asc-cible,
.asc-jeu.asc-intrus { position: relative; }

.asc-vise {
    position: absolute;
    left: calc(100% + 5.5rem);
    top: 50%;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.35rem;
    transform: translateY(-50%);
    white-space: nowrap;
}

/* Le nom change à chaque réussite : il se renouvelle d'un mouvement, sinon
   on ne verrait pas qu'on est passé au suivant. */
.asc-vise-n {
    font-family: 'Bebas Neue', sans-serif;
    font-size: clamp(1.4rem, 3vw, 2.1rem);
    color: var(--neon-primary, #ffd700);
    letter-spacing: 0.04em;
    animation: ascVise 0.35s cubic-bezier(0.2, 1.2, 0.35, 1) both;
}

@keyframes ascVise {
    from { opacity: 0; transform: translateY(0.5rem) scale(0.94); }
    to   { opacity: 1; transform: none; }
}

.asc-vise-pts { display: flex; gap: 0.32rem; margin-top: 0.5rem; }

.asc-vise-pts i {
    width: 0.42rem;
    height: 0.42rem;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.14);
    transition: background 0.25s ease, box-shadow 0.25s ease;
}

.asc-vise-pts i.on {
    background: #6ee7b7;
    box-shadow: 0 0 0.45rem rgba(110, 231, 183, 0.6);
}

/* ── Intrus : le compte de ce qu'on a débusqué ── */
.asc-compte {
    position: absolute;
    left: calc(100% + 5.5rem);
    top: 50%;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.2rem;
    transform: translateY(-50%);
    white-space: nowrap;
}

.asc-compte-n {
    font-family: 'Bebas Neue', sans-serif;
    font-size: clamp(1.4rem, 3vw, 2.1rem);
    line-height: 1;
    letter-spacing: 0.04em;
    color: var(--neon-primary, #ffd700);
}

.asc-compte-c {
    display: flex;
    align-items: baseline;
    gap: 0.3rem;
    margin-top: 0.45rem;
    font-family: 'Bebas Neue', sans-serif;
}

.asc-compte-c em {
    font-family: 'Inter', sans-serif;
    font-style: normal;
    font-size: 0.6rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.3);
}

.asc-compte b { font-size: 1.8rem; font-weight: 400; color: #6ee7b7; }
.asc-compte i { font-style: normal; font-size: 1rem; color: rgba(255, 255, 255, 0.3); }

@media (max-width: 900px) {
    /* Quatre colonnes donnaient des vignettes de la taille d'une carte à
       jouer : six rangs de portraits qu'il fallait faire défiler. À six, la
       grille tient d'un coup d'œil. */
    .asc-grille { grid-template-columns: repeat(6, 1fr); gap: 0.28rem; }
    /* Pas de marge latérale sur téléphone : consigne et compte reviennent
       au-dessus. Mais sur une seule ligne, « ONE PIECE 0/5 intrus » se lisait
       comme une phrase et non comme un titre et son compte : les deux lignes
       restent, et la grille prend ses distances. */
    .asc-vise, .asc-compte {
        position: static;
        transform: none;
        align-items: center;
        margin-bottom: 1.5rem;
    }

    .asc-compte-c { margin-top: 0.2rem; }
    .asc-vise-pts { margin-top: 0.3rem; }
    .asc-portraits { flex-wrap: wrap; }
    .asc-portrait { flex: 0 0 auto; width: clamp(4.4rem, 27vw, 6.5rem); }
    /* Pas de marge à droite sur téléphone : le compteur revient dessous */
    .asc-aide.asc-cote {
        position: static;
        flex-direction: row;
        gap: 0.4rem;
        align-items: baseline;
        transform: none;
        font-size: 1.4rem;
    }
    .asc-jeu.asc-devine { max-width: 100%; }
    .asc-jeu.asc-cible,
    .asc-jeu.asc-intrus { max-width: 100%; }
    .asc-portrait { width: clamp(4.2rem, 26vw, 6rem); }
    .asc-ampoule { top: -2.3rem; width: 1rem; height: 1.5rem; }
    .asc-vise-n { font-size: 1.3rem; }

    /* Les cinq portraits se rangent d'eux-mêmes sur deux lignes : on les
       centre pour que la seconde ne parte pas d'un bord. */
    /* La seconde rangée doit laisser passer ses ampoules au-dessus des
       saisies de la première : sans cet écart, elles se chevauchaient. */
    .asc-portraits { justify-content: center; align-items: flex-start; row-gap: 3.6rem; }
}

/* ══════════ Ordre chronologique ══════════
   Les arcs sur une seule ligne, séparés de flèches : la suite se lit de
   gauche à droite, et c'est elle qu'on est en train de fabriquer. On échange
   deux arcs en glissant l'un sur l'autre — l'échange, et non l'insertion,
   pour que la ligne garde sa longueur et que rien ne se dérobe. */
.asc-jeu.asc-ordre { max-width: min(104rem, 100vw - 3rem); }

.asc-suite {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: clamp(0.3rem, 1vw, 0.8rem);
    width: 100%;
}

.asc-carte-arc {
    flex: 1 1 0;
    min-width: 0;
    max-width: 23rem;
    display: flex;
    flex-direction: column;
    /* L'ecart entre l'image et le titre, et la marge sous le titre. Les deux
       ensemble font l'air autour du texte : le regler d'un seul cote decale la
       carte au lieu de la respirer. */
    gap: 1rem;
    padding: 0.55rem 0.55rem 1.2rem;
    background: rgba(140, 180, 255, 0.07);
    border: 0.09rem solid rgba(140, 180, 255, 0.24);
    border-radius: 0.5rem;
    cursor: grab;
    transition: transform 0.18s ease, border-color 0.2s ease,
                background 0.2s ease, opacity 0.2s ease;
}

.asc-carte-arc:hover { border-color: rgba(140, 180, 255, 0.5); transform: translateY(-0.2rem); }

/* L'arc en main s'efface : on suit celui qu'on déplace, pas celui qu'on quitte */
.asc-carte-arc.prise { opacity: 0.35; cursor: grabbing; transform: scale(0.96); }

/* Celui qu'on survole annonce l'échange */
.asc-carte-arc.vise {
    border-color: var(--neon-primary, #ffd700);
    background: rgba(255, 215, 0, 0.12);
    transform: translateY(-0.35rem) scale(1.03);
    box-shadow: 0 0.6rem 1.6rem rgba(0, 0, 0, 0.45);
}

.asc-carte-arc img {
    width: 100%;
    aspect-ratio: 16 / 10;
    object-fit: cover;
    border-radius: 0.35rem;
    pointer-events: none;
}

.asc-carte-arc-t {
    font-size: 0.92rem;
    font-weight: 600;
    line-height: 1.2;
    text-align: center;
    color: rgba(255, 255, 255, 0.78);
    pointer-events: none;
}

/* La flèche : elle dit le sens de lecture, du plus ancien au plus récent */
.asc-fleche {
    flex: 0 0 auto;
    width: clamp(1rem, 2.4vw, 1.8rem);
    display: grid;
    place-items: center;
}

.asc-fleche svg {
    width: 100%;
    height: 0.75rem;
    fill: none;
    stroke: rgba(140, 180, 255, 0.4);
    stroke-width: 1.4;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* ══════════ Liaison ══════════
   Deux rangées reliées par de vrais traits, tracés d'un bout à l'autre. La
   courbe n'est pas une coquetterie : deux droites voisines se confondraient,
   alors que deux courbes se suivent des yeux. Les coordonnées se mesurent
   après chaque rendu — le gabarit seul ne peut pas les deviner.

   Couchée et non dressée : en colonnes, cinq éléments empilés deux fois ne
   tiennent pas en hauteur. Les portraits rétrécissaient jusqu'à ce qu'on ne
   distingue plus les visages, les valeurs s'étiraient en bandelettes sur
   toute la largeur — et les deux tiers droits de l'écran restaient vides. */
.asc-jeu.asc-liaison { max-width: min(90rem, 100vw - 16rem); }

.asc-colonnes {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: clamp(2.5rem, 9vh, 5.5rem);
    width: 100%;
    /* On tire un fil : au doigt, le geste ferait défiler la page, et à la
       souris il sélectionnerait les noms des nœuds au passage. */
    touch-action: none;
    user-select: none;
}

/* Les traits passent derrière les nœuds : ils s'ancrent à leur bord */
.asc-traits {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    overflow: visible;
}

.asc-traits path {
    fill: none;
    stroke: #6ee7b7;
    stroke-width: 2.2;
    stroke-linecap: round;
    filter: drop-shadow(0 0 0.3rem rgba(110, 231, 183, 0.5));
    /* Le trait se dessine de gauche à droite : on voit le lien se faire */
    stroke-dasharray: 1000;
    animation: ascTrait 0.45s cubic-bezier(0.3, 0.9, 0.3, 1) both;
}

.asc-traits path.faux {
    stroke: #ff6b6b;
    filter: drop-shadow(0 0 0.3rem rgba(255, 90, 90, 0.5));
}

/* Le fil qu'on tire : violet, pour se distinguer des liens posés. Il ne se
   dessine pas progressivement — il suit le curseur, et l'animation de tracé
   le ferait clignoter à chaque déplacement. */
.asc-traits path.asc-fil {
    stroke: #a78bfa;
    stroke-dasharray: none;
    animation: none;
    filter: drop-shadow(0 0 0.4rem rgba(167, 139, 250, 0.6));
}

@keyframes ascTrait {
    from { stroke-dashoffset: 1000; }
    to   { stroke-dashoffset: 0; }
}

/* Une rangée : les éléments se serrent au centre au lieu de se répartir sur
   toute la largeur. C'est cet étalement qui faisait les bandelettes. */
.asc-col {
    flex: 0 0 auto;
    min-width: 0;
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: stretch;
    gap: clamp(0.5rem, 2vw, 1.6rem);
}

/* Un nœud ne s'étend jamais : à portrait il se réduit à son image, en toutes
   lettres il tient une largeur fixe pour que la rangée reste régulière. */
.asc-noeud {
    position: relative;
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.4rem;
    background: rgba(255, 255, 255, 0.03);
    border: 0.09rem solid rgba(255, 255, 255, 0.09);
    border-radius: 0.5rem;
    cursor: pointer;
    transition: transform 0.18s ease, border-color 0.2s ease, background 0.2s ease;
}

/* Le portrait se règle sur la hauteur : c'est elle qui manque, la largeur
   suit par le rapport de l'image. */
.asc-noeud img {
    flex: 0 0 auto;
    height: clamp(6rem, 24vh, 13rem);
    width: auto;
    aspect-ratio: 3 / 4;
    object-fit: cover;
    border-radius: 0.3rem;
}

.asc-noeud:not(.avecImage) {
    width: clamp(7rem, 15vw, 12rem);
    min-height: 3.6rem;
    padding: 0.8rem 0.9rem;
}

/* Le texte va à la ligne plutôt que de pousser les murs : un titre long
   tenait sur une seule ligne et emmenait la carte avec lui. */
.asc-noeud-t {
    min-width: 0;
    font-size: clamp(0.85rem, 1.5vw, 1.05rem);
    font-weight: 600;
    line-height: 1.25;
    text-align: center;
    color: rgba(255, 255, 255, 0.82);
    overflow-wrap: anywhere;
}

/* Aucun portrait nulle part : rien n'encombre. Une carte sans image ne tire
   aucune hauteur de son contenu — elle restait une fente, quand celles d'en
   face font treize rem. On lui en donne, et les deux rangées s'écartent
   d'autant : les fils ont alors de quoi se lire. */
.asc-colonnes.sansImage { gap: clamp(4rem, 13vh, 8rem); }
.asc-colonnes.sansImage .asc-noeud-t { font-size: clamp(0.95rem, 1.8vw, 1.25rem); }

.asc-colonnes.sansImage .asc-noeud:not(.avecImage) {
    min-height: 5.4rem;
    padding: 1.15rem 1rem;
}

/* ── Portraits en face de texte ──
   Techniques, armes, studio, année, auteur, perso→anime : une rangée de
   treize rem fait face à une rangée qui ne tire sa hauteur que de son texte.
   Les cartes du bas passaient pour des fentes sous les vignettes, et les deux
   rangées se serraient au point que les fils n'avaient plus de course.
   L'écart et la hauteur ne sont repris QUE dans ce cas : portrait contre
   portrait, les deux rangées sont déjà hautes, et les écarter davantage ferait
   déborder l'épreuve hors de l'écran. */
.asc-colonnes.mixte { gap: clamp(3.4rem, 12vh, 7rem); }

.asc-colonnes.mixte .asc-noeud:not(.avecImage) {
    min-height: 4.8rem;
    padding: 1.1rem 1rem;
}

/* Le point d'attache : c'est de lui que part le trait. Il pend sous la carte
   du haut et coiffe celle du bas — c'est par là que le fil descend. */
.asc-point {
    --dec: translateX(-50%);
    position: absolute;
    left: 50%;
    bottom: -0.25rem;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.18);
    transform: var(--dec);
    transition: background 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}

.asc-col.droite .asc-point { bottom: auto; top: -0.25rem; }

.asc-noeud:hover { border-color: rgba(140, 180, 255, 0.45); }
.asc-noeud:hover .asc-point { background: rgba(140, 180, 255, 0.7); }
.asc-noeud:active { transform: scale(0.98); }

/* En main : ce nœud tient le fil. Il se soulève au lieu de s'enfoncer —
   « :active » reste vrai tout le temps qu'on tire, et le creusait. */
.asc-noeud.pris {
    border-color: var(--neon-primary, #ffd700);
    background: rgba(255, 215, 0, 0.1);
    transform: scale(1.03);
}

.asc-noeud.pris .asc-point {
    background: var(--neon-primary, #ffd700);
    box-shadow: 0 0 0.7rem rgba(255, 215, 0, 0.7);
    transform: var(--dec) scale(1.4);
}

.asc-noeud.lie { border-color: rgba(110, 231, 183, 0.45); }
.asc-noeud.lie .asc-point { background: #6ee7b7; box-shadow: 0 0 0.5rem rgba(110, 231, 183, 0.6); }

/* L'aimant : la borne la plus proche du fil s'allume et se soulève. C'est
   elle qui prendra le fil si on lâche maintenant — il n'y a donc pas à viser. */
.asc-noeud.vise {
    border-color: #a78bfa;
    background: rgba(167, 139, 250, 0.12);
    transform: scale(1.04);
}

.asc-noeud.vise .asc-point {
    background: #a78bfa;
    box-shadow: 0 0 0.7rem rgba(167, 139, 250, 0.8);
    transform: var(--dec) scale(1.4);
}

/* La main dit ce qu'on peut faire : tirer en haut, recevoir en bas */
.asc-col:not(.droite) .asc-noeud { cursor: grab; }
.asc-col:not(.droite) .asc-noeud.pris { cursor: grabbing; }
.asc-col.droite .asc-noeud { cursor: default; }

.asc-noeud.rate {
    border-color: rgba(255, 100, 100, 0.6);
    background: rgba(255, 100, 100, 0.07);
}

@media (max-width: 900px) {
    .asc-jeu.asc-liaison, .asc-jeu.asc-ordre { max-width: 100%; }

    /* ── La liaison se redresse ──
       Couchée, cinq cartes de front tombaient sous la taille d'une vignette et
       les noms se coupaient en deux. Sur téléphone c'est la hauteur qui est
       libre, pas la largeur : on revient aux deux colonnes, et les fils
       traversent de nouveau. « ascLignes » suit le même seuil côté script. */
    /* ⚠️ Les trois dispositions doivent être nommées. « .asc-colonnes.mixte »
       et « .asc-colonnes.sansImage » pèsent deux classes contre une : listées
       seules, les règles de grand écran l'emporteraient ici par spécificité et
       le téléphone garderait un écart réglé en vh alors qu'il est couché. */
    .asc-colonnes,
    .asc-colonnes.sansImage,
    .asc-colonnes.mixte {
        flex-direction: row;
        align-items: stretch;
        gap: clamp(2.4rem, 12vw, 4.5rem);
    }

    .asc-col {
        flex: 1 1 0;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        gap: 0.45rem;
    }

    .asc-noeud { padding: 0.25rem; }
    .asc-noeud.avecImage { width: auto; }
    .asc-noeud img { height: clamp(3.6rem, 12vh, 6rem); width: auto; }

    .asc-noeud:not(.avecImage) {
        width: 100%;
        min-height: 2.6rem;
        padding: 0.45rem 0.35rem;
    }

    /* Même raison, et le « padding » aussi : il ne reprenait que la hauteur,
       si bien qu'une carte de téléphone gardait le rembourrage du grand écran. */
    .asc-colonnes.sansImage .asc-noeud:not(.avecImage),
    .asc-colonnes.mixte .asc-noeud:not(.avecImage) {
        min-height: 2.9rem;
        padding: 0.45rem 0.35rem;
    }
    .asc-noeud-t { font-size: clamp(0.62rem, 2.5vw, 0.95rem); }

    /* Les bornes reviennent sur les flancs : c'est par là que le fil passe */
    .asc-col .asc-point {
        --dec: translateY(-50%);
        top: 50%;
        bottom: auto;
    }

    .asc-col:not(.droite) .asc-point { left: auto; right: -0.22rem; }
    .asc-col.droite .asc-point { right: auto; left: -0.22rem; top: 50%; bottom: auto; }

    /* ── L'ordre chronologique se met debout ──
       Cinq arcs de front débordaient de l'écran, et l'on ne voyait pas la fin
       de la suite qu'on est justement en train de fabriquer. Debout, chaque
       arc tient sur une ligne : vignette à gauche, titre à droite. */
    .asc-suite { flex-direction: column; align-items: stretch; gap: 0.35rem; }

    .asc-carte-arc {
        flex: 0 0 auto;
        flex-direction: row;
        align-items: center;
        gap: 0.7rem;
        max-width: 100%;
        padding: 0.4rem 0.6rem 0.4rem 0.4rem;
    }

    .asc-carte-arc img { width: 4.6rem; flex: 0 0 auto; }
    .asc-carte-arc-t { flex: 1; min-width: 0; font-size: 0.85rem; text-align: left; }

    /* La flèche pivote avec la suite : elle montre toujours le sens de lecture */
    .asc-fleche { width: 1.1rem; height: 0.9rem; transform: rotate(90deg); }

    /* Pris du doigt plutôt que glissé : sans repère visible, on ne saurait pas
       quel arc on tient entre les deux tapes. */
    .asc-carte-arc.prise {
        opacity: 1;
        transform: none;
        border-color: var(--neon-primary, #ffd700);
        background: rgba(255, 215, 0, 0.14);
    }
}

/* ══════════ Le tampon de « Devine le perso » ══════════
   Il claque sur le portrait à l'instant où le nom tombe, et porte ce qu'on
   a répondu. Le geste est bref exprès : c'est une récompense, pas une
   annonce — on enchaîne déjà sur le portrait suivant. */
.asc-vitre {
    position: relative;
    display: block;
}

.asc-tampon {
    position: absolute;
    left: 50%;
    top: 50%;
    max-width: 92%;
    padding: 0.3rem 0.6rem;
    font-family: 'Bebas Neue', sans-serif;
    font-size: clamp(1rem, 2.4vw, 1.6rem);
    line-height: 1.1;
    letter-spacing: 0.06em;
    text-align: center;
    text-transform: uppercase;
    color: #6ee7b7;
    border: 0.16rem solid #6ee7b7;
    border-radius: 0.3rem;
    background: rgba(10, 20, 16, 0.55);
    box-shadow: 0 0 1.4rem rgba(110, 231, 183, 0.3);
    pointer-events: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    animation: ascTampon 0.5s cubic-bezier(0.2, 1.6, 0.35, 1) both;
}

/* Il arrive de loin, de travers, et se pose net : la rotation finale reste
   légère pour qu'on lise le nom sans pencher la tête. */
@keyframes ascTampon {
    0%   { opacity: 0; transform: translate(-50%, -50%) rotate(-24deg) scale(2.6); }
    55%  { opacity: 1; transform: translate(-50%, -50%) rotate(-9deg) scale(0.94); }
    75%  { transform: translate(-50%, -50%) rotate(-7deg) scale(1.04); }
    100% { opacity: 1; transform: translate(-50%, -50%) rotate(-7deg) scale(1); }
}

/* ══════════ La grille fermée après une erreur ══════════
   Cliquer les vingt-quatre cartes d'affilée battait le jeu. Une erreur ferme
   donc la grille deux secondes. Le verrou est tenu par le serveur — ce qui
   suit n'en est que la mise en scène, pour qu'on comprenne pourquoi les clics
   ne répondent plus. */
.asc-grille.bloquee {
    pointer-events: none;
    border-radius: 0.6rem;
    animation: ascFerme 0.5s ease-in-out infinite alternate;
}

/* Le contour part avec la même douceur qu'il est venu */
.asc-grille:not(.bloquee) { animation: none; }

.asc-vignette img { transition: box-shadow 0.25s ease, filter 0.35s ease; }
.asc-grille.bloquee .asc-vignette img { filter: saturate(0.15) brightness(0.4); }

/* La pulsation reste lente : un clignotement rapide punirait l'œil en plus
   de la main, alors qu'on a seulement voulu aller trop vite. */
@keyframes ascFerme {
    from {
        box-shadow: 0 0 0 0.14rem rgba(255, 90, 90, 0.35),
                    0 0 1.4rem rgba(255, 90, 90, 0.12);
    }
    to {
        box-shadow: 0 0 0 0.14rem rgba(255, 90, 90, 0.85),
                    0 0 2.4rem rgba(255, 90, 90, 0.32);
    }
}

/* ══════════ Le classement final ══════════
   Ce n'est pas un score qu'on lit ici mais une altitude : chacun a grimpé
   seul, et ce qui les sépare c'est l'étage où ils se sont arrêtés. La liste
   se dévoile du dernier au premier, comme au Rush — même geste, même attente. */
.asc-fin {
    position: fixed;
    inset: 0;
    z-index: 70;
    display: grid;
    place-items: center;
    padding: 1.2rem;
    background: radial-gradient(60% 50% at 50% 40%, rgba(120, 170, 255, 0.06), transparent 70%), #08080b;
}

.asc-fin-boite {
    width: min(27rem, 92vw);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.2rem;
}

.asc-fin-titre {
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.32);
}

.asc-fin-liste {
    width: 100%;
    margin: 0;
    padding: 0;
    list-style: none;
}

/* Chaque place attend son tour, mais occupe sa hauteur dès le départ : la
   boîte garde la même taille du premier au dernier dévoilement. */
.asc-fin-liste li {
    position: relative;
    display: flex;
    align-items: baseline;
    gap: 0.7rem;
    padding: 0.7rem 0.2rem;
    border-bottom: 0.06rem solid rgba(255, 255, 255, 0.05);
    opacity: 0;
    transform: translateY(0.55rem);
    transition: opacity 0.4s ease, transform 0.4s cubic-bezier(0.2, 0.9, 0.25, 1);
}

.asc-fin-liste li.vu { opacity: 1; transform: none; }

/* Un trait de lumière balaie la ligne au moment où elle paraît */
.asc-fin-sweep {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.09), transparent);
    opacity: 0;
}

.asc-fin-liste li.vu .asc-fin-sweep {
    animation: ascFinSweep 0.85s cubic-bezier(0.3, 0.8, 0.2, 1) 0.1s both;
}

@keyframes ascFinSweep {
    0%   { opacity: 0; transform: translateX(-100%); }
    30%  { opacity: 1; }
    100% { opacity: 0; transform: translateX(100%); }
}

.asc-fin-liste .rang {
    width: 1.2rem;
    font-family: 'Bebas Neue', sans-serif;
    font-size: 1.1rem;
    color: rgba(255, 255, 255, 0.28);
}

.asc-fin-liste .nom {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.75);
}

/* « Sommet » ne s'affiche que pour ceux qui l'ont atteint : c'est la seule
   distinction qui compte au-dessus de l'étage, tout le reste est une altitude. */
.asc-fin-liste .detail {
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: rgba(110, 231, 183, 0.75);
}

/* L'étage atteint sur le total : c'est le score de ce mode */
.asc-fin-liste .etage {
    display: flex;
    align-items: baseline;
    gap: 0.12rem;
    font-family: 'Bebas Neue', sans-serif;
    font-variant-numeric: tabular-nums;
}

.asc-fin-liste .etage b { font-size: 1.5rem; font-weight: 400; color: rgba(255, 255, 255, 0.6); }
.asc-fin-liste .etage i { font-style: normal; font-size: 0.85rem; color: rgba(255, 255, 255, 0.22); }

/* La première place, dévoilée en dernier, tombe plus lourdement que les autres */
.asc-fin-liste li.premier { padding: 0.95rem 0.2rem; }
.asc-fin-liste li.premier .rang {
    font-size: 1.7rem;
    color: var(--neon-primary, #ffd700);
    text-shadow: 0 0 0.9rem rgba(255, 215, 0, 0.45);
}
.asc-fin-liste li.premier .nom { color: #fff; font-size: 1.1rem; }
.asc-fin-liste li.premier .etage b { font-size: 2.1rem; color: var(--neon-primary, #ffd700); }
.asc-fin-liste li.premier .etage i { font-size: 1rem; }
.asc-fin-liste li.moi .nom { color: var(--neon-primary, #ffd700); }

.asc-fin-liste li.premier.vu { animation: ascFinChoc 0.7s cubic-bezier(0.2, 1.4, 0.35, 1) both; }

@keyframes ascFinChoc {
    0%   { opacity: 0; transform: scale(0.9); }
    60%  { opacity: 1; transform: scale(1.03); }
    100% { opacity: 1; transform: scale(1); }
}

.asc-fin-liste li.premier .asc-fin-sweep {
    background: linear-gradient(90deg, transparent, rgba(255, 215, 0, 0.3), transparent);
}

/* Au-delà de la sixième place on ne se voit pas : ce rappel arrive avec les boutons */
.asc-fin-moi {
    font-size: 0.85rem;
    color: rgba(255, 255, 255, 0.4);
}

.asc-fin-moi b { color: var(--neon-primary, #ffd700); font-weight: 700; }
.asc-fin-moi sup { font-size: 0.6em; }

/* Côte à côte, comme sur tous les podiums du site — voir la note dans
   home-rush.css. */
.asc-fin-actions {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.7rem;
    margin-top: 0.4rem;
}

/* Rien ne s'offre au clic tant que le classement n'est pas entièrement dit */
.asc-fin-moi,
.asc-fin-actions {
    opacity: 0;
    transform: translateY(0.5rem);
    pointer-events: none;
    transition: opacity 0.45s ease, transform 0.45s cubic-bezier(0.2, 0.9, 0.25, 1);
}

.asc-fin-moi.vu,
.asc-fin-actions.vu {
    opacity: 1;
    transform: none;
    pointer-events: auto;
}

/* ══════════ Les portraits paraissent au lieu de surgir ══════════
   Un portrait pèse deux cents kilo-octets : sur téléphone, les vingt-quatre
   d'une grille n'arrivent pas ensemble, et les cartes semblaient se fabriquer
   une à une sous les yeux. Chacune se découvre donc en fondu dès qu'elle est
   là, sur un fond neutre qui tient sa place en attendant.

   Le filet : si l'événement de chargement ne venait jamais — image en cache
   attachée trop tôt, requête perdue —, l'animation la révèle au bout de trois
   secondes. Mieux vaut une image en retard qu'une image jamais montrée. */
.asc img {
    opacity: 0;
    background: rgba(255, 255, 255, 0.035);
    animation: ascFiletImage 0s linear 3s forwards;
}

.asc img.vue {
    opacity: 1;
    animation: none;
    transition: opacity 0.32s ease;
}

@keyframes ascFiletImage {
    to { opacity: 1; }
}

/* ══════════ Le double-tape ne doit pas zoomer ══════════
   Sur un jeu de clics rapides, deux appuis rapprochés sont une cadence, pas une
   demande d'agrandissement. « manipulation » supprime l'attente de trois cents
   millisecondes du double-tape sans rien enlever au défilement ni au pincer. */
.asc {
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}

/* Le clavier ne doit pas pousser la page : l'épreuve tient déjà tout l'écran,
   il n'y a rien à révéler en la faisant monter. */
body.game-active:has(.asc) {
    overflow: hidden;
    overscroll-behavior: none;
}

/* ══════════ Les grands écrans ══════════
   Toutes les bornes hautes étaient en rem, donc en pixels fixes : sur un 2K,
   le plateau gardait exactement la taille qu'il a en 1080p et flottait au
   milieu du vide. Seule la grille de vingt-quatre grandissait, parce que
   c'est la hauteur qui la contraint et non la largeur.

   ⚠️ La racine grandit DEJA au-dela de 2000 px — « html { font-size: clamp(16px,
   0.83vw, 26px) } » dans home.css — si bien que tout ce qui est en rem suit
   sans qu on y touche. Ce palier-ci s ajoute par-dessus : il double donc la
   croissance, ce qui va bien pour les plateaux mais avait fait passer l ordre
   chronologique sous la tour. A n augmenter qu avec cette mise en garde en tete.

   Le seuil est à 2000 px, et non à 1600 : il doit passer juste au-dessus du
   1920 de large d'un écran ordinaire, sur lequel tous ces réglages ont été
   faits à l'œil. Un seuil plus bas les aurait défaits sans prévenir.

   Les bornes de fenêtre (100vw, 100vh) sont laissées telles quelles : ce sont
   elles qui empêchent le plateau de courir sous la tour, et elles se règlent
   seules. Sur un écran large mais court — un 2560x1080 — c'est donc toujours
   la hauteur qui commande la grille de vingt-quatre. */
@media (min-width: 2000px) {
    .asc-jeu { max-width: min(58rem, 100vw - 20rem); }
    .asc-jeu.asc-devine { max-width: min(90rem, 100vw - 14rem); }

    .asc-jeu.asc-cible,
    .asc-jeu.asc-intrus {
        max-width: min(78rem, 100vw - 30rem, calc((100vh - 9rem) * 0.92));
    }

    .asc-jeu.asc-liaison { max-width: min(110rem, 100vw - 16rem); }
    /* La borne de fenetre valait « 100vw - 3rem » : la plus permissive de
       toutes les epreuves, seule a ne pas reserver la place de la tour. Sur un
       2K, ou la racine grandit deja, le plateau passait donc dessous. Il garde
       ses 104 rem — qui valent 2210 px a cette taille — et laisse la marge que
       les autres epreuves laissent. */
    .asc-jeu.asc-ordre { max-width: min(104rem, 100vw - 16rem); }

    /* Les portraits suivent le plateau, sinon ils s espaceraient au lieu de
       grandir — cinq vignettes de deux cents pixels perdues dans un mètre. */
    .asc-portrait { max-width: 17rem; }
    .asc-colonnes .asc-noeud img { height: clamp(6rem, 24vh, 16rem); }

    /* L en-tete se met a l echelle du reste : a taille fixe, il paraissait
       chuchote au-dessus d un plateau qui, lui, avait grandi. */
    .asc-titre { font-size: 2.4rem; }
    .asc-soustitre { font-size: 1rem; }
    .asc-etage b { font-size: 2.6rem; }
    .asc-etage i { font-size: 1.4rem; }

    /* La tour gagne de quoi rester lisible a cote d un plateau plus large */
    /* La gouttière s'élargit en un seul endroit : le fût et les cabines la
       lisent tous les deux. */
    .asc-tour { width: 6rem; --fut-x: 2.7rem; --fut-w: 3.1rem; }
}

/* ═══════════════════════════════════════════════════════════
   🧠 L'ŒIL — cinq portraits, un clignotement, un retournement
   ═══════════════════════════════════════════════════════════
   Le seul etage de memoire de la tour. Tout s y joue sur le TEMPS :
   ce qu on voit, combien de temps, et a quel instant les commandes
   s ouvrent. La phase est portee par la classe du conteneur et pilotee
   depuis app.js — une animation CSS seule ne saurait pas dire au
   serveur quand le joueur a le droit de repondre. */
.asc-oeil {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    /* La rangee et la consigne ne se touchent pas : passee SOUS les cartes,
       il lui faut de l air pour ne pas se lire comme une legende collee a la
       derniere. A 2,8 rem elle en etait encore trop pres. */
    gap: 6.2rem;
    width: 100%;
    /* ⚠️ Au-dela du plafond de « .asc-jeu » (46 rem), et volontairement : six
       cartes de 9,2 rem et cinq ecarts de 1,1 font 60,7 rem. Cet etage n a
       qu une rangee, il peut se permettre la largeur que les grilles n ont pas. */
    max-width: 62rem;
    /* Le parent centre son contenu ; avec une seule rangee, le bloc
       tombait au milieu exact et paraissait bas. Ce rembourrage le
       remonte sans toucher au centrage. */
    padding-bottom: 9rem;
}

.asc-oeil-consigne {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    min-height: 2.6rem;
}

/* ⚠️ Bebas, comme TOUT le reste de la tour — le titre de l etage, les
   altitudes, le nom vise de la Cible. Sans font-family elle retombait sur
   la police du corps de page, et cette ligne-la jurait avec son voisinage. */
.asc-oeil-q {
    font-family: 'Bebas Neue', sans-serif;
    font-size: 0.95rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.4);
}

.asc-oeil-n {
    font-family: 'Bebas Neue', 'Russo One', sans-serif;
    font-size: 1.9rem;
    line-height: 1;
    color: var(--neon-primary, #ffd700);
}

/* Pendant qu on regarde, la ligne ne porte qu un mot : elle garde sa
   hauteur pour que les cartes ne sautent pas au retournement.
   ⚠️ SEULE la couleur le distingue de « Trouve » : meme corps, meme
   interlettrage, meme police. Il a porte un moment un halo et deux points
   de plus, et c est ce qui le faisait passer pour une AUTRE police — a
   cette taille l ombre epaissit le trait et Bebas ne se reconnait plus.
   Le blanc franc suffit a le detacher du gris de veille. */
.asc-oeil-consigne.vide .asc-oeil-q { color: #fff; }

.asc-oeil-pts { display: flex; gap: 0.3rem; margin-left: 0.4rem; }
.asc-oeil-pts i {
    width: 0.42rem;
    height: 0.42rem;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.16);
    transition: background 0.2s ease;
}
.asc-oeil-pts i.on { background: var(--neon-primary, #ffd700); }

.asc-oeil-rang {
    display: flex;
    gap: 1.1rem;
    perspective: 1100px;
}
/* Le blocage se MONTRE, comme sur la grille de la Cible : sans mise en scene,
   les clics cessent de repondre et l on croit a une panne. Le verrou est tenu
   par le serveur — ceci n en est que la traduction a l ecran. */
.asc-oeil-rang.bloquee { pointer-events: none; }

.asc-oeil-rang.bloquee .asc-oeil-dos {
    animation: ascOeilFerme 0.55s ease-in-out infinite alternate;
}

/* La carte fautive reste lisible — c est tout l interet de la laisser
   retournee — mais son liseré passe du dore au rouge : on voit du meme coup
   QUI etait la et qu on s est trompe. Griser sa face, comme le fait la Cible
   sur sa grille, l aurait rendue illisible au seul moment ou elle compte. */
.asc-oeil-rang.bloquee .asc-oeil-carte.vue .asc-oeil-face {
    border-color: rgba(255, 90, 90, 0.65);
    box-shadow: 0 0 1rem rgba(255, 90, 90, 0.25);
}

@keyframes ascOeilFerme {
    from {
        border-color: rgba(255, 90, 90, 0.3);
        box-shadow: inset 0 0 0 0.1rem rgba(255, 90, 90, 0.18);
        color: rgba(255, 120, 120, 0.3);
    }
    to {
        border-color: rgba(255, 90, 90, 0.75);
        box-shadow: inset 0 0 0 0.1rem rgba(255, 90, 90, 0.45),
                    0 0 1.2rem rgba(255, 90, 90, 0.2);
        color: rgba(255, 140, 140, 0.65);
    }
}

/* La carte a deux faces qui occupent la MEME place : c est ce qui permet
   de la retourner sans que la rangee bouge d un pixel. Les positions
   sont la reponse — si elles glissaient, le jeu serait fausse. */
.asc-oeil-carte {
    position: relative;
    /* 6 x 9,2 + 5 x 1,1 = 60,7 rem, sous les 62 du plateau : la largeur est
       au bout de ce que la rangee peut prendre, c est donc la HAUTEUR qui
       grandit. Elle rapproche du coup la carte du format du portrait lui-meme
       (225 x 350), qui remplit mieux et rogne moins de visage. */
    width: 9.2rem;
    height: 13.4rem;
    padding: 0;
    border: 0;
    background: transparent;
    cursor: default;
    transform-style: preserve-3d;
    /* Un peu plus long qu un simple fondu : on doit voir la carte TOURNER,
       c est ce qui dit qu elle est la meme et qu elle n a pas ete remplacee. */
    transition: transform 0.55s cubic-bezier(0.34, 0.88, 0.28, 1);
}

.asc-oeil-face,
.asc-oeil-dos {
    position: absolute;
    inset: 0;
    border-radius: 0.6rem;
    overflow: hidden;
    backface-visibility: hidden;
    display: grid;
    place-items: center;
}

.asc-oeil-face {
    border: 1.5px solid rgba(255, 255, 255, 0.12);
    background: #0d0f16;
}
.asc-oeil-face img { width: 100%; height: 100%; object-fit: cover; display: block; }

.asc-oeil-dos {
    transform: rotateY(180deg);
    border: 1.5px solid rgba(255, 215, 0, 0.22);
    background: linear-gradient(160deg, #161a26, #0d0f16);
    font-family: 'Bebas Neue', 'Russo One', sans-serif;
    font-size: 1.7rem;
    color: rgba(255, 215, 0, 0.3);
}

/* L entree : les cartes se posent l une apres l autre. Sans elle elles
   surgissaient toutes faites et le joueur decouvrait la rangee deja la —
   le clignotement partait avant qu il ait pose les yeux dessus. */
.asc-oeil.phase-entree .asc-oeil-carte { animation: ascOeilPose 0.62s cubic-bezier(0.16, 0.72, 0.22, 1) both; }
.asc-oeil-carte:nth-child(1) { animation-delay: 0s; }
.asc-oeil-carte:nth-child(2) { animation-delay: 0.1s; }
.asc-oeil-carte:nth-child(3) { animation-delay: 0.2s; }
.asc-oeil-carte:nth-child(4) { animation-delay: 0.3s; }
.asc-oeil-carte:nth-child(5) { animation-delay: 0.4s; }
.asc-oeil-carte:nth-child(6) { animation-delay: 0.5s; }

/* Elles DESCENDENT : venant d en bas elles avaient l air de rebondir, et le
   regard suivait le mouvement au lieu de se poser sur les visages. */
@keyframes ascOeilPose {
    from { opacity: 0; transform: translateY(-3rem); }
    to   { opacity: 1; transform: none; }
}

/* Le clignotement : HUIT battements lents. C est ce qui force a regarder —
   un affichage fixe se laisse detailler, un clignotement non. Mais la mesure
   est toujours allee dans le meme sens : 320 ms ne faisaient qu un
   scintillement, 550 pressaient encore. A 825 le battement se suit a l oeil
   nu — on echange donc du nombre contre de la duree, et des pulsations
   lentes valent mieux que des pressees.
   ⚠️ Il y en avait QUATRE, et six visages a retenir en cinq secondes ne se
   retenaient pas : on en gardait trois. Le battement ne change pas, c est le
   NOMBRE qui double — allonger la pulsation aurait rendu le stroboscope mou,
   et c est lui qui empeche de detailler. +3,3 s, pris sur le temps de clic
   (30 s par defaut, donc il en reste large ; au reglage 20 s c est serre).
   ⚠️ 8 x 825 ms tombe pile sur le retournement calé dans app.js : toucher
   a l un demande de refaire la somme de l autre. */
/* ⚠️ Plus de « steps(1) » : la coupure franche faisait un stroboscope, dur a
   l oeil et illisible. Un fondu garde le battement — on sait qu il se passe
   quelque chose — sans jamais eteindre tout a fait le visage. */
.asc-oeil.phase-clignote .asc-oeil-face { animation: ascOeilClin 0.825s ease-in-out 8; }
@keyframes ascOeilClin {
    0%   { opacity: 1; }
    45%  { opacity: 0.18; }
    100% { opacity: 1; }
}

/* « :not(.vue) » : une carte qu on vient de cliquer revient face visible, et
   y reste tant que la serie tient. C est le geste du jeu de memoire. */
.asc-oeil.phase-cache .asc-oeil-carte:not(.vue) { transform: rotateY(180deg); }

.asc-oeil-carte.vue .asc-oeil-face {
    border-color: rgba(255, 215, 0, 0.55);
    box-shadow: 0 0 0.9rem rgba(255, 215, 0, 0.25);
}

/* Retournees, les cartes de la variante « places » redeviennent cliquables
   et le disent. Dans « numeros » elles restent inertes : on designe la
   place par son chiffre, en dessous. */
.asc-oeil.phase-cache.var-places .asc-oeil-carte:not(.vue) { cursor: pointer; }
.asc-oeil.phase-cache.var-places .asc-oeil-carte:not(.vue):hover .asc-oeil-dos {
    border-color: rgba(255, 215, 0, 0.55);
    color: rgba(255, 215, 0, 0.7);
}

/* ⚠️ Sur telephone la rangee devient une GRILLE 2 x 3, et c est le seul
   endroit ou la mise en page change. En une seule ligne, six cartes tombaient
   a 3,4 rem de large — des vignettes sur lesquelles on ne reconnaissait plus
   personne, sur un etage qui ne demande que de reconnaitre. Un telephone a
   de la hauteur et pas de largeur : deux colonnes rendent 7,4 rem par carte,
   soit plus du double. Le retour a la ligne ne brouille rien tant qu il est
   REGULIER — 1-2, 3-4, 5-6 se lit comme une page — et les numeros au dos le
   disent de toute facon. */
@media (max-width: 640px) {
    .asc-oeil { gap: 2.8rem; padding-bottom: 1rem; }

    .asc-oeil-rang {
        display: grid;
        grid-template-columns: repeat(2, auto);
        gap: 0.7rem;
    }

    /* 2 x 7,4 + 0,7 = 15,5 rem de large, 3 x 10,8 + 2 x 0,7 = 33,8 de haut :
       le tout tient sur le plus etroit et le plus court des telephones,
       consigne et titre compris. */
    .asc-oeil-carte { width: 7.4rem; height: 10.8rem; }
    .asc-oeil-dos { font-size: 1.5rem; }
    .asc-oeil-n { font-size: 1.5rem; }
}
