/* Portail BV-Servers — écran d'auth deux volets : anomalie WebGL + panneau formulaire. */
:root{
    --bg:#050102; --panel:#000000;
    --neon:#ff2d3e; --neon-soft:#ff4655; --neon-hi:#ff8790;
    --text:#ffffff; --field-text:#fceaec; --muted:#f5a3ab;
    --line:rgba(255,45,62,.15); --field-line:rgba(255,45,62,.18);
    --err:#ff5468; --ok:#37d67a;
    --ease:cubic-bezier(.16,1,.3,1);
}
*{box-sizing:border-box}
html,body{margin:0; padding:0; height:100%; overflow:hidden; background:var(--bg); -webkit-font-smoothing:antialiased}
body{
    font-family:'Chakra Petch',ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
    color:var(--text);
}

/* Colonne droite élargie : le formulaire est calé à son bord gauche, donc
   élargir la colonne le décale vers la gauche. */
.shell{position:relative; display:grid; grid-template-columns:1fr 660px; width:100vw; height:100vh}

/* ── volet gauche : anomalie ── */
/* Le dégradé sert de repli quand l'anomalie WebGL ne se charge pas. */
/* Plein cadre, sous le formulaire. Confiné à la colonne gauche, le canvas
   s'arrêtait net au bord du panneau : rendre celui-ci transparent ne
   découvrait que le fond du body, donc la dalle sombre restait. */
/* Décalé vers la gauche : le cadre déborde de 40% à gauche et va jusqu'au bord
   droit. Le centre du pulsar tombe donc à 30% de la fenêtre au lieu de 50%,
   tout en couvrant encore toute la largeur — pas de zone sans canvas. */
.stage{
    position:absolute; top:0; bottom:0; left:-40%; right:0;
    z-index:0; overflow:hidden;
    background:radial-gradient(circle at 50% 45%, rgba(255,45,62,.10), transparent 60%), var(--bg);
}
.stage cosmic-swarm{position:absolute; inset:0}
.stage-tag,.stage-status{position:absolute; pointer-events:none; font-size:10px; text-transform:uppercase}
.stage-tag{top:32px; left:32px; letter-spacing:.4em; color:var(--neon); font-weight:600}
.stage-status{top:38px; right:32px; display:flex; align-items:center; gap:8px; letter-spacing:.3em; color:rgba(245,163,171,.55)}
.stage-status::before{
    content:""; width:7px; height:7px; border-radius:50%;
    background:var(--neon); box-shadow:0 0 10px var(--neon);
    animation:bv-pulse 2s ease-in-out infinite;
}
@keyframes bv-pulse{0%,100%{opacity:.55}50%{opacity:1}}

/* ── volet droit : panneau ── */
/* Sol continu : plus de pavé opaque. Le fond, le bord franc et l'ombre portée
   coupaient la page en deux et faisaient lire le formulaire comme un élément
   rapporté. La lumière du pulsar traverse maintenant derrière lui. */
/* Le stage étant sorti du flux, le panneau doit revendiquer la 2e colonne. */
.panel{
    position:relative; z-index:1; grid-column:2;
    display:flex; flex-direction:column; justify-content:safe center;
    padding:48px 56px 48px 64px; background:transparent;
    border-left:0; box-shadow:none;
    overflow-y:auto;
}

/* Logo en ligne avec le nom : le médaillon centré mangeait de la hauteur et
   donnait un air de carte de visite, incompatible avec des champs flottants. */
.brand{display:flex; flex-direction:row; align-items:center; gap:16px; margin-bottom:34px}
.brand-mark{
    position:relative; width:76px; height:76px; flex:none;
    display:flex; align-items:center; justify-content:center;
}
.brand img{
    width:58px; height:58px; border-radius:50%;
    background:radial-gradient(circle, rgba(255,45,62,.25), transparent 70%);
    filter:drop-shadow(0 0 20px rgba(255,45,62,.5));
    transition:filter .6s var(--ease);
}
/* Anneaux de chargement : inertes tant que le formulaire n'est pas envoyé. */
.brand-ring,.brand-ring2{
    position:absolute; border-radius:50%; opacity:0;
    transition:opacity .45s var(--ease);
    pointer-events:none;
}
.brand-ring{
    inset:0;
    border:2px solid rgba(255,45,62,.10);
    border-top-color:var(--neon); border-right-color:var(--neon);
    box-shadow:0 0 18px rgba(255,45,62,.45), inset 0 0 10px rgba(255,45,62,.12);
}
.brand-ring2{
    inset:9px;
    border:1.5px solid transparent;
    border-bottom-color:rgba(255,45,62,.5);
}
@keyframes bv-ring{to{transform:rotate(360deg)}}
/* Le décalage et la marge haute servaient au médaillon centré : en ligne,
   ils désalignent le nom par rapport au logo. */
.brand .title{
    text-align:left; font-size:18px; font-weight:600;
    letter-spacing:.26em; margin:0;
}

/* ── onglets connexion / inscription (deux routes distinctes → des liens) ── */
/* Onglets soulignés : aucune piste, aucun fond. Un segmented control dessinait
   une boîte de plus, ce que la composition flottante exclut. */
.tabs{
    display:flex; gap:26px; margin:0 0 34px;
    width:100%; max-width:var(--field-w);
    padding:0; border:0; background:none; border-radius:0;
}
.tabs a{
    padding:0 0 8px; text-align:left; text-decoration:none; border-radius:0;
    font-size:13.5px; font-weight:500; letter-spacing:.01em; text-transform:none;
    color:rgba(245,163,171,.55); background:transparent;
    border-bottom:1.5px solid transparent;
    transition:color .45s var(--ease), border-color .45s var(--ease);
}
.tabs a:hover{color:var(--text)}
.tabs a.is-active{color:#fff; border-bottom-color:var(--neon)}

/* ── champs ──
   `--field-w` borne la largeur des contrôles : sur un panneau de 520 px les
   champs pleine largeur paraissaient très étirés. Le bloc reste centré. */
:root{--field-w:340px; --r-field:16px; --r-btn:18px}
form{display:flex; flex-direction:column; align-items:flex-start; gap:26px}
.field{
    position:relative;
    display:flex; flex-direction:column; gap:9px;
    width:100%; max-width:var(--field-w);
}
.field-head{display:flex; justify-content:space-between; align-items:baseline}
label{
    font-size:10.5px; font-weight:500; letter-spacing:.2em; text-transform:uppercase;
    color:var(--muted);
}
/* Champs en capsules : cadre complet, coins très arrondis, fond à peine
   teinté. L'accent se joue sur la bordure et un anneau au focus. */
input{
    width:100%;
    padding:13px 16px; border-radius:var(--r-field); font-size:15px; font-family:inherit;
    border:1px solid rgba(255,255,255,.14);
    background:rgba(255,255,255,.03); color:var(--field-text);
    outline:none;
    transition:border-color .4s var(--ease), background .4s var(--ease),
               box-shadow .4s var(--ease);
}
input[type=password]{letter-spacing:.15em}
input::placeholder{color:rgba(245,163,171,.35)}
input:hover{border-color:rgba(255,70,85,.45)}
input:focus{
    border-color:var(--neon); background:rgba(255,45,62,.06);
    box-shadow:0 0 0 3px rgba(255,45,62,.12);
}
input:disabled{opacity:.45}
.hint{font-size:10px; letter-spacing:.1em; color:rgba(245,163,171,.5)}

/* Contour d'accent, vide, que l'eau remplit au survol. Le halo fait partie de
   l'identité du bouton, ce n'est pas une ombre portée : il dessine la lueur
   du trait, il ne simule pas une élévation.
   `isolation` + `overflow` : le remplissage est un pseudo-élément en z-index
   négatif, il doit passer au-dessus du fond du bouton mais sous le libellé,
   et rester découpé par les coins arrondis. */
button[type=submit]{
    position:relative; isolation:isolate; overflow:hidden;
    width:100%; max-width:var(--field-w);
    margin-top:16px; padding:14px 20px; cursor:pointer;
    background:transparent;
    border:2px solid var(--neon); border-radius:var(--r-btn);
    color:var(--neon);
    font-family:inherit; font-size:15px; font-weight:700;
    letter-spacing:.12em; text-transform:uppercase;
    box-shadow:0 0 15px rgba(255,45,62,.4);
    transition:color .35s var(--ease), box-shadow .35s var(--ease);
}

/* Le « niveau d'eau » : un disque énorme dont seule la calotte passe devant le
   bouton, donc une ligne de flottaison presque plate malgré les 340 px de
   large. Garé sous le bord bas au repos, il remonte au-dessus du libellé au
   survol ; le border-radius asymétrique + la rotation lente font onduler la
   crête pendant la montée. */
button[type=submit]::before{
    content:"";
    position:absolute; left:50%; bottom:-1rem;
    width:100rem; height:100rem; z-index:-1;
    border-radius:49.2% 50% 49.6% 49.9%;
    background:
        linear-gradient(180deg, rgba(255,190,196,.55), transparent 1.1rem),
        linear-gradient(180deg, var(--neon), #7e101d 70%);
    translate:-50% 100.6rem;
    transition:translate .8s var(--ease);
    /* en pause hors survol : rien à composer tant que l'eau est cachée */
    animation:bv-fill-swirl 9s linear infinite paused;
}
button[type=submit]:hover::before,
button[type=submit]:focus-visible::before{
    translate:-50% 93.8rem;
    animation-play-state:running;
}
@keyframes bv-fill-swirl{to{rotate:360deg}}

button[type=submit]:hover{
    /* le libellé bascule au noir pendant que l'eau arrive, pas avant */
    color:#0a0203; transition-delay:.22s;
    box-shadow:0 0 44px rgba(255,45,62,.75);
}
button[type=submit]:active{
    border-color:#c41f31; color:#0a0203;
    box-shadow:0 0 18px rgba(255,45,62,.5);
}
button[type=submit]:disabled{
    cursor:progress; background:transparent;
    border-color:rgba(255,45,62,.3); color:rgba(255,45,62,.4);
    box-shadow:none;
}
/* Bouton neutralisé pendant le chargement : l'eau se retire et ne remonte pas. */
button[type=submit]:disabled::before{translate:-50% 100.6rem; animation-play-state:paused}

@media (prefers-reduced-motion:reduce){
    button[type=submit]::before{animation:none; transition-duration:.2s}
}
input:focus-visible,button:focus-visible,.tabs a:focus-visible{
    outline:2px solid var(--neon-hi); outline-offset:3px;
}

a{color:var(--neon-soft); text-decoration:none}
a:hover{color:var(--neon-hi); text-decoration:underline}
.link-small{font-size:10px; letter-spacing:.15em; text-transform:uppercase}

/* ── bascule connexion / inscription ──
   Seules les parties qui changent s'effacent. Le logo, le pied de page et le
   volet WebGL ne bougent pas : c'est ce qui distingue la bascule d'un
   rechargement. */
.panel .tabs,.panel form,.panel .alt{
    transition:opacity .17s var(--ease), transform .17s var(--ease);
}
.panel.is-swapping .tabs,
.panel.is-swapping form,
.panel.is-swapping .alt,
.panel.is-swapping .notice{
    opacity:0; transform:translateY(4px); pointer-events:none;
}

/* ── messages ── */
/* Alignés à gauche comme le reste de la colonne, et calés en haut du panneau. */
.notice{
    display:flex; align-items:center; gap:.5rem;
    width:100%; max-width:var(--field-w); margin:0 0 24px;
    padding:.65rem .95rem; border-radius:var(--r-field); font-size:12.5px;
    animation:bv-notice .5s var(--ease) both;
}
@keyframes bv-notice{from{opacity:0; transform:translateY(-6px)}to{opacity:1; transform:none}}
.notice-err{background:rgba(255,84,104,.10); border:1px solid rgba(255,84,104,.32); color:var(--err)}
.notice-err::before{content:"⚠"}
.notice-ok{background:rgba(55,214,122,.10); border:1px solid rgba(55,214,122,.32); color:var(--ok)}
.notice-ok::before{content:"✓"; font-weight:800}

/* Aligné à gauche comme le reste de la colonne. */
.alt{
    text-align:left; margin-top:24px; font-size:12.5px;
    color:rgba(245,163,171,.62); max-width:var(--field-w);
}

/* ── chargement : le panneau s'efface, l'anomalie prend l'écran et morphe en
      singularité (cycle 3). Le morph EST l'écran de chargement. ── */
.panel{transition:opacity .6s var(--ease), transform .6s var(--ease)}
.stage-loading{
    position:absolute; left:0; right:0; bottom:18%;
    text-align:center; pointer-events:none;
    font-size:11px; letter-spacing:.4em; text-transform:uppercase; color:var(--muted);
    opacity:0; transition:opacity .5s var(--ease) .35s;
}
.stage-loading::after{content:"…"; animation:bv-ellipsis 1.4s steps(4,end) infinite}

body.is-loading{cursor:progress}
/* Le panneau RESTE : seuls les contrôles s'effacent, le logo tient l'écran
   pendant que les anneaux tournent. Le stage garde EXACTEMENT sa géométrie —
   le passer en `position:fixed; inset:0` redimensionnait le canvas, ce qui
   déclenchait le ResizeObserver et recadrait la caméra, d'où l'impression
   de zoom au submit. Seul le morph vers le cycle 3 doit bouger. */
body.is-loading .panel{pointer-events:none}
body.is-loading .tabs,
body.is-loading form,
body.is-loading .alt,
body.is-loading .notice{
    opacity:0; transform:translateY(6px);
    transition:opacity .4s var(--ease), transform .4s var(--ease);
    pointer-events:none;
}
/* Le bloc logo remonte doucement pour occuper l'espace libéré. */
body.is-loading .brand{
    transform:translateY(28px);
    transition:transform .6s var(--ease);
    margin-bottom:0;
}
body.is-loading .brand img{filter:drop-shadow(0 0 34px rgba(255,45,62,.65))}
body.is-loading .brand-ring{opacity:1; animation:bv-ring .95s cubic-bezier(.5,.15,.4,.9) infinite}
body.is-loading .brand-ring2{opacity:1; animation:bv-ring 1.7s linear infinite reverse}
body.is-loading .stage-tag,body.is-loading .stage-status{opacity:0; transition:opacity .4s var(--ease)}
body.is-loading .stage-loading{opacity:1}

@keyframes bv-ellipsis{0%{opacity:.25}50%{opacity:1}100%{opacity:.25}}

/* ── tablette : on rétrécit le panneau avant de casser la grille ── */
@media (max-width:1180px){
    .shell{grid-template-columns:1fr minmax(380px,440px)}
    .panel{padding:40px 40px}
}

/* ── écrans étroits : l'anomalie passe en fond, le panneau devient une carte ── */
@media (max-width:900px){
    html,body{overflow:auto}
    .shell{
        display:flex; align-items:center; justify-content:center;
        min-height:100dvh; height:auto; padding:24px 16px;
    }
    /* L'anomalie reste en fond plein écran, derrière le formulaire. */
    .stage{position:fixed; inset:0; z-index:0}
    .stage-tag,.stage-status{display:none}
    .panel{
        position:relative; z-index:1; width:100%; max-width:420px;
        padding:36px 28px; overflow:visible;
        border:1px solid rgba(255,45,62,.22); border-radius:22px;
        /* Sur téléphone le pulsar est le seul décor et passe derrière la carte :
           un voile léger suffit à garder le texte lisible sans le noyer. */
        background:linear-gradient(180deg, rgba(4,1,2,.22), rgba(4,1,2,.55) 60%);
        backdrop-filter:blur(6px) saturate(130%);
        -webkit-backdrop-filter:blur(6px) saturate(130%);
    }
}

/* ── mobile : on resserre et on empile les onglets si besoin ── */
@media (max-width:420px){
    .shell{padding:16px 12px}
    .panel{padding:28px 20px}
    /* Le logo desktop est passé à 58 px : cette règle datait du médaillon de
       96 px et rendait le logo plus gros sur téléphone que sur écran large. */
    .brand-mark{width:64px; height:64px}
    .brand img{width:48px; height:48px}
    .brand .title{font-size:16px; letter-spacing:.2em}
    .tabs a{font-size:12.5px}
}

/* ── viewport bas (paysage mobile) : on retire le centrage vertical ── */
@media (max-height:620px) and (max-width:900px){
    .shell{align-items:flex-start}
}

@media (prefers-reduced-motion:reduce){
    .stage-status::before,.stage-loading::after,.notice{animation:none}
    .panel,.stage-loading,input,button[type=submit],.tabs a{transition:none}
    input:focus,button[type=submit]:hover{transform:none}
    /* Les anneaux restent affichés comme repère visuel, mais ne tournent pas. */
    body.is-loading .brand-ring,body.is-loading .brand-ring2{animation:none}
    body.is-loading .brand{transform:none; transition:none}
    body.is-loading .tabs,body.is-loading form,body.is-loading .alt,
    body.is-loading .notice{transform:none; transition:none}
    .panel .tabs,.panel form,.panel .alt{transition:none}
    .panel.is-swapping .tabs,.panel.is-swapping form,
    .panel.is-swapping .alt,.panel.is-swapping .notice{transform:none}
}
