/* ==========================================================================
   Palco das telas sem menu — a cena iluminada onde vivem login, erro e e-mail.

   Direção "luz, não vidro": a profundidade vem de ILUMINAÇÃO, não de
   transparência. Três focos de luz difusa no fundo, e nada translúcido — texto
   sobre superfície de vidro quase sempre reprova em contraste, e o efeito já
   datou.

   Também não há cartão. Nessas telas o conteúdo é curto (um formulário, uma
   frase de erro), e a caixa em volta não acrescentava informação nenhuma — só
   escondia a luz do fundo.

   ── Por que este arquivo existe ─────────────────────────────────────────
   Nasceu dentro do login.css em 2026-09-06. Quando a segunda e a terceira tela
   precisaram da mesma cena, copiar os focos de luz seria criar três verdades
   sobre a mesma coisa. O palco mora aqui; cada tela traz só o que é dela.

   Quem usa: views/login.html, views/fragments/erro-pagina.html (as 5 telas de
   erro) e, adiante, views/e-mail.html.
   ========================================================================== */

/* Flex e não grid: com `place-items: center` num grid de uma linha só, o item é
   centralizado DENTRO da célula — e a célula, de altura automática, encosta no
   topo. O bloco ficava 100px acima do centro. Em flex, os focos saem do fluxo
   por serem absolutos e sobra um único filho para centralizar. */
.xn-palco {
    --xn-luz-x: 0;
    --xn-luz-y: 0;

    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    overflow: hidden;
    padding: var(--occ-space-6) var(--occ-space-4);
    background: var(--occ-bg);
    /* Perspectiva na cena, e não no bloco. 1800px e não 1200px: quanto mais longa,
       menor a deformação para o mesmo ângulo — e menos o texto sofre ao inclinar. */
    perspective: 1800px;
}

/* ── Os focos de luz ─────────────────────────────────────────────────────
   Três camadas de gradiente radial. A primeira versão media os focos em
   PIXELS FIXOS (860px, 620px), dimensionados olhando uma maquete de 560px de
   altura: num monitor de 1900x940 eles viravam duas manchas encolhidas nos
   cantos, com um vazio chapado no meio da tela. Agora a medida é `vmax`, que
   acompanha o maior lado da janela — a proporção da cena fica a mesma em
   qualquer monitor.

   O halo central é o que faltava: sem ele o meio da tela morria, e é
   justamente onde o conteúdo está. Ele não é decoração — é o que faz o olho
   pousar no lugar certo.

   O foco verde usa a segunda cor da marca (#6abd45, a mesma do "by carsoft"
   no logo) em dose mínima, para a cena não virar um degradê monocromático.

   --xn-luz-x / --xn-luz-y são escritas pelo tela-limpa.js conforme o ponteiro,
   e valem 0 enquanto ele não entrar na cena. Cada camada anda uma distância
   diferente: é a diferença entre elas que cria a sensação de profundidade. */
.xn-palco__beam {
    position: absolute;
    border-radius: 50%;
    pointer-events: none;
    /* transição longa e suave: o foco "escorre" atrás do ponteiro em vez de
       colar nele, que é como luz difusa se comporta */
    transition: transform .9s cubic-bezier(.22, .61, .36, 1);
}

.xn-palco__beam--accent {
    width: 95vmax;
    height: 95vmax;
    top: -48vmax;
    left: -28vmax;
    background: radial-gradient(circle at center, var(--occ-accent-soft) 0%, transparent 60%);
    transform: translate(calc(var(--xn-luz-x) * 70px), calc(var(--xn-luz-y) * 70px));
}

.xn-palco__beam--brand {
    width: 72vmax;
    height: 72vmax;
    bottom: -40vmax;
    right: -20vmax;
    background: radial-gradient(circle at center, rgba(106, 189, 69, .11) 0%, transparent 62%);
    transform: translate(calc(var(--xn-luz-x) * -46px), calc(var(--xn-luz-y) * -46px));
}

[data-color-theme="dark"] .xn-palco__beam--brand {
    background: radial-gradient(circle at center, rgba(106, 189, 69, .14) 0%, transparent 62%);
}

/* Halo atrás do conteúdo: separa o bloco do fundo sem desenhar um cartão. */
.xn-palco__beam--halo {
    width: 56vmax;
    height: 56vmax;
    top: 50%;
    left: 50%;
    margin: -28vmax 0 0 -28vmax;
    background: radial-gradient(circle at center, rgba(28, 167, 167, .09) 0%, transparent 58%);
    transform: translate(calc(var(--xn-luz-x) * 26px), calc(var(--xn-luz-y) * 26px));
}

[data-color-theme="dark"] .xn-palco__beam--halo {
    background: radial-gradient(circle at center, rgba(60, 208, 208, .10) 0%, transparent 58%);
}

@media (prefers-reduced-motion: reduce) {
    .xn-palco__beam {
        transition: none;
    }
}

/* ── O bloco ─────────────────────────────────────────────────────────────
   Sem fundo, sem borda, sem sombra: não é um cartão. O que o mantém coeso é
   o alinhamento e a largura curta.

   ── Por que o texto borrava, e como para de borrar ──────────────────────
   Transformação 3D faz o navegador rasterizar o elemento numa textura e só
   depois aplicá-la: o detalhe fino da fonte se perde. Na primeira versão o
   bloco carregava `will-change: transform` fixo e guardava o último ângulo
   do ponteiro, então a camada ficava promovida e o texto saía borrado
   PERMANENTEMENTE — inclusive parado, que é justamente quando se lê.

   Três medidas, juntas:
    1. sem translateZ. Ele aproximava o bloco da câmera e ampliava a textura,
       que é o que mais destrói a nitidez;
    2. ângulo pequeno (2,2°) e perspectiva longa (1800px na cena): a
       deformação fica no limite do perceptível;
    3. o `will-change` e o próprio transform só existem enquanto a classe
       `is-tilting` está presente. Parou de mexer o mouse, o script tira a
       classe, o transform volta a `none` e o navegador redesenha o texto
       nítido de novo.

   O ÂNGULO em si é escrito pelo tela-limpa.js direto no style do elemento, e
   não por uma regra com var() aqui: um `transform` montado com calc() sobre
   custom properties nem sempre é recomputado quando a variável muda num
   ancestral, e o bloco ficava sem inclinar nenhum grau.

   O que fica no CSS é o que o CSS faz melhor: a transição. Curta quando o
   bloco acompanha o ponteiro, longa e suave quando ele volta ao repouso. */
.xn-palco__bloco {
    position: relative;
    z-index: 2;
    width: 100%;
    max-width: 312px;
    transition: transform .5s cubic-bezier(.22, .61, .36, 1);
}

.xn-palco__bloco.is-tilting {
    will-change: transform;
    transition: transform .1s linear;
}

@media (prefers-reduced-motion: reduce) {
    .xn-palco__bloco,
    .xn-palco__bloco.is-tilting {
        transform: none !important;
        transition: none;
        will-change: auto;
    }
}

/* ── Marca ───────────────────────────────────────────────────────────────
   Centralizada, criando o eixo vertical que alinha o que vem abaixo dela.
   A troca de arte por tema é a mesma do header (.xn-brand__icon--light/--dark
   em navbar.css); aqui só a altura muda. */
.xn-palco__marca {
    display: block;
    text-align: center;
    margin-bottom: var(--occ-space-6);
    line-height: 0;
}

.xn-palco__marca img.xn-brand__icon {
    height: 48px;
    width: auto;
    /* margin auto, e nao so o text-align do pai: no tema escuro o navbar.css poe
       `display: block` na arte escura, e bloco ignora text-align do pai -- a marca
       encostava na esquerda. No claro a arte fica `inline` e centralizava, o que
       fazia o defeito aparecer so num dos temas. */
    margin-inline: auto;
}

/* ── Entrada ─────────────────────────────────────────────────────────────
   Uma vez, ao carregar. Nada em laço: estas telas são abertas várias vezes por
   dia pela mesma pessoa, e movimento perpétuo cansa.

   A entrada anima `translate`, NÃO `transform`. Os dois são propriedades
   separadas, e é isso que resolve um conflito real: a inclinação do ponteiro
   também escreve em `transform`, e animação vence regra normal na cascata.
   Quando as duas dividiam a mesma propriedade, o bloco ficava preso no
   primeiro quadro do keyframe e a inclinação nunca aparecia.

   SEM fill-mode, de propósito. Com `both` (ou `backwards`) o elemento assume o
   primeiro quadro antes de a animação começar — e o primeiro quadro tem
   `opacity: 0`. Se a animação demorar a iniciar (aba aberta em segundo plano,
   onde o navegador segura as animações), a tela fica INVISÍVEL esperando por
   ela. Sem fill-mode, o repouso é o estado normal e o pior caso é um quadro
   sem o fade. */
@keyframes xn-palco-rise {
    from { opacity: 0; translate: 0 14px; }
    to   { opacity: 1; translate: 0 0; }
}

.xn-palco__bloco {
    animation: xn-palco-rise .5s var(--occ-ease);
}

@media (prefers-reduced-motion: reduce) {
    .xn-palco__bloco {
        animation: none;
        translate: none;
    }
}

@media (max-width: 480px) {
    .xn-palco {
        padding: var(--occ-space-5) var(--occ-space-4);
    }
    .xn-palco__bloco {
        max-width: 100%;
    }
}
