/* ════════════════════════════════════════════════════════════
   🏔️ 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 {
    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. */
.asc-fut {
    position: absolute;
    left: 2.2rem;
    top: -0.2rem;
    bottom: -0.2rem;
    width: 2.6rem;
    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;
}

/* Combien de monde ici. Le pastillage remplace les jetons : à cinquante
   grimpeurs, cinquante initiales ne se lisaient plus. */
.asc-etg-nb {
    position: absolute;
    left: 2.2rem;
    width: 2.6rem;
    display: grid;
    place-items: center;
    height: 1.15rem;
    font-family: 'Bebas Neue', sans-serif;
    font-size: 0.8rem;
    color: #0b0b0e;
    background: rgba(140, 180, 255, 0.85);
    border-radius: 2rem;
    transform: scale(0.82);
    animation: ascCompte 0.4s cubic-bezier(0.2, 1.3, 0.35, 1) both;
}

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

/* 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);
}

.asc-etg.moi .asc-etg-nb {
    color: #14100a;
    background: var(--neon-primary, #ffd700);
}

/* 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 {
        position: fixed;
        top: 0;
        bottom: 0;
        right: 0;
        z-index: 58;
        width: 5.4rem;
        padding: 3.2rem 0.4rem 4.6rem;
        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;
    gap: 0.75rem;
    /* Le titre se collait à l'image : il lui faut sa propre marge, en bas
       comme sur les côtés, sinon la carte paraît coupée. */
    padding: 0.55rem 0.55rem 0.85rem;
    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. Les cartes se donnent de la
   hauteur — sans image elles n'en tiraient aucune du contenu et restaient des
   fentes — et les deux rangées s'écartent d'autant. */
.asc-colonnes.sansImage { gap: clamp(4rem, 13vh, 8rem); }
.asc-colonnes.sansImage .asc-noeud-t { font-size: clamp(0.95rem, 1.8vw, 1.25rem); }

/* Une carte sans portrait 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 de quoi se lire. */
.asc-colonnes.sansImage { gap: clamp(4rem, 13vh, 8rem); }

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

.asc-colonnes.sansImage .asc-noeud:not(.avecImage) {
    min-height: 5.4rem;
    padding: 1.15rem 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. */
    .asc-colonnes,
    .asc-colonnes.sansImage {
        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;
    }

    .asc-colonnes.sansImage .asc-noeud:not(.avecImage) { min-height: 2.9rem; }
    .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; }

.asc-fin-actions {
    display: flex;
    flex-direction: column;
    align-items: 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;
}
