/* =====================================================================
   Entrada do Portal — a tela de LOGIN vestida com a identidade do Portal.
   Carregada SÓ na rota anônima, pelo hook add_css_anonymous_page.

   ⚠️ O `add_css` comum NÃO chega aqui: PluginExtension::getPluginsCssFiles()
   troca de hook conforme a página e, em página anônima, lê apenas
   ADD_CSS_ANONYMOUS_PAGE. Registrar do jeito de sempre dá tela sem estilo,
   com HTTP 200 e nada no log.

   Cor só por token --pk-* (skin/pcnet-tokens.css é registrado junto, antes
   deste). Nenhum literal: a trava do padrão visual cobra, e com razão.

   As medidas vêm do banco, como custom properties impressas pelo PHP no
   próprio <html> — ver PluginPortalpcnetEntrada e hook.php.
   ===================================================================== */

.page-anonymous {
    --pe-logo-w: 148px;
    --pe-logo-gap: 10px;
    --pe-logo-off: 80px;
    --pe-logo-r: .5333;                 /* proporção real de img/logo.png (225x120) */
    --pe-logo-d: calc(var(--pe-logo-w) + 2 * var(--pe-logo-gap));
    --pe-logo-h: calc(var(--pe-logo-w) * var(--pe-logo-r));
    --pe-card-pad: 32px;
    --pe-pad-marca: 10px;
    --pe-card-alfa: .52;
    --pe-campo-alfa: .58;
    --pe-campo-borda: 0px;
    --pe-campo-raio: 11px;
    --pe-campo-sy: 3px;
    --pe-campo-sb: 7px;
    --pe-glow: 3px;
    --pe-glow-dif: 0px;
    --pe-glow-alfa: .45;
    --pe-foco-borda: 0px;
    --pe-foco-dif: 0px;
    --pe-foco-cor: var(--pk-green-dark);
    --pe-dsy: 5px;
    --pe-dsb: 10px;
    --pe-dsa: 0;
    --pe-concavo: 6px;
    --pe-malha: .75;

    /* A TINTA das sombras de medida variável (campo, disco). É a mesma cor das
       sombras da casa (--pk-shadow-rest / -float usam rgba(16,38,6,…)), mas
       aqueles tokens trazem geometria FIXA e aqui o deslocamento e a difusão
       vêm do banco. Derivar do --pk-ink mantém uma fonte só: se a paleta mudar,
       isto acompanha. */
    /* `var(--pk-ink)` e não `black`: a trava do padrão conta `black` como
       paleta própria, e com razão — a tinta escura da casa É um token. A
       diferença medida na sombra final é invisível (o alfa é 0,42). */
    --pe-sombra: color-mix(in srgb, var(--pk-green-ink) 82%, var(--pk-ink));
}

/* ---------------- fundo ---------------- */
.page-anonymous { background: var(--pk-bg); }

/* A malha do portal. Um <svg> tem proporção INTRÍNSECA: nenhum par de
   inset/left/right o dimensiona — as DUAS dimensões têm de ser explícitas,
   senão ele deriva a que falta e sobra faixa. É o que o portal já faz. */
#pe-malha {
    position: fixed;
    top: 0; left: 0;
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    z-index: 0;
    pointer-events: none;
    display: block;
    opacity: var(--pe-malha);
}
#pe-malha .fw-mesh-wire { fill: none; stroke: var(--pk-green-dark); stroke-width: 1.3; stroke-opacity: .5; }
#pe-malha .fw-mesh-tri  { stroke: none; animation-timing-function: ease-in-out;
                          animation-iteration-count: infinite; animation-fill-mode: backwards; }
#pe-malha .fw-mesh-tri--a { animation-name: pe-tw-a; }
#pe-malha .fw-mesh-tri--b { animation-name: pe-tw-b; }
#pe-malha .fw-mesh-tri--c { animation-name: pe-tw-c; }
@keyframes pe-tw-a { 0%,62%,100%{opacity:0;fill:var(--pk-green-dark)} 72%{opacity:.85;fill:var(--pk-green)} 84%{opacity:.5;fill:var(--pk-green-hover)} }
@keyframes pe-tw-b { 0%,58%,100%{opacity:0;fill:var(--pk-green-hover)} 70%{opacity:.75;fill:var(--pk-green-line)} 82%{opacity:.45;fill:var(--pk-green)} }
@keyframes pe-tw-c { 0%,65%,100%{opacity:0;fill:var(--pk-green)} 75%{opacity:.8;fill:var(--pk-green-soft)} 88%{opacity:.4;fill:var(--pk-green-dark)} }

/* o conteúdo fica ACIMA da malha */
.page-anonymous > * { position: relative; z-index: 1; }

/* ---------------- a logo do core sai; a nossa entra ----------------
   🔴 `:has(.pe-marca)` e não a página inteira: a nossa marca só entra pelo hook
   `display_login`, que o core chama em UM único template (login.html.twig:112).
   Escondendo `.glpi-logo` em toda tela anônima, as telas de erro e as de senha
   ficavam SEM LOGO NENHUM — nem a do GLPI, nem a nossa. */
.page-anonymous:has(.pe-marca) .glpi-logo { display: none; }

/* O hook display_login é renderizado numa coluna ao lado do formulário
   (login.html.twig:110). `display:contents` dissolve essa coluna: os nossos
   nós passam a se ancorar ao card, e não a uma coluna que não queremos. */
.page-anonymous .row > .col-auto { display: contents; }

/* ---------------- o card ---------------- */
/* 🔴 O core abre o container em 60rem por ESTILO INLINE
   (login.html.twig + page_card_notlogged: `card_bg_width` vira
   `style="max-width: 60rem"`), e estilo inline só perde para !important. A
   entrada é uma coluna só: 928px de card deixavam o formulário perdido no meio.
   Orçamento de !important desta folha: 2, os dois contra estilo inline do core. */
.page-anonymous .container-tight { max-width: 27rem !important; }

/* 🔴 ALTURA: o core empilha três respiros antes do card — `container py-6`
   (40px), `flex-fill py-4` (24) e `mt-4` (24) = 88px só acima. Num notebook
   1366x768 a tela passava a exigir 47px de rolagem, e 95px em 1280x720. O
   conteúdo é UM card centrado: 88px acima dele não estão fazendo trabalho
   nenhum. Reduzidos, com o `justify-content:center` do core cuidando do
   equilíbrio quando sobra espaço. */
/* !important porque as utilitárias de espaçamento do Tabler (`py-6`, `py-4`,
   `mt-4`) já vêm com !important por construção — sem ele a regra não pega, e
   medir mostrou os 88px intactos. Orçamento desta folha: 5, todos contra
   !important ou estilo inline do core. */
.page-anonymous .container-tight { padding-block: 16px !important; }
.page-anonymous .flex-fill { padding-block: 8px !important; margin-top: 0 !important; }

.page-anonymous .main-content-card {
    position: relative;
    overflow: visible;
    /* o rodapé é absoluto em top:100%; sem esta margem ele cobre o copyright */
    margin-bottom: 64px;   /* manual (39px) + vão (16) + folga */
    border-radius: var(--pk-radius);
    border: 1px solid var(--pk-border);
    box-shadow: var(--pk-shadow-rest);
    padding: var(--pe-card-pad) calc(var(--pe-card-pad) - 2px);
    /* max(0px, …): com offset maior que a meia-altura da logo isto virava
       negativo e o card subia por cima do que estivesse acima dele */
    margin-top: max(0px, calc(var(--pe-logo-h) / 2 + 8px - var(--pe-logo-off)));
    font-family: var(--pk-font);
}
/* o card do core já traz padding no .card-body: zeramos para o nosso valer.
   🔴 E `position: static`: o .card-body do Tabler é `relative`, então a marca
   (absolute) ancorava NELE — ou seja, depois do padding-top — e caía em cima
   do título. Medido: offsetParent era div.card-body, marca em 259 quando devia
   estar em 128. Com static, a âncora passa a ser o .main-content-card. */
.page-anonymous .main-content-card > .card-body { padding: 0; position: static; }

/* A coluna do formulário é `col-md-5` (41%): num container de 27rem sobravam
   148px de 400, o título quebrava em três linhas e "Password" colidia com
   "Forgot Password?". A entrada é uma coluna só — o formulário ocupa tudo. */
.page-anonymous .row > .col-md-5 { flex: 0 0 100%; max-width: 100%; }
.page-anonymous .main-content-card > .card-header:empty { display: none; }
.page-anonymous .main-content-card > .card-header {
    padding: 0 0 4px; background: none; border: 0;
}
/* o espaço que a marca ocupa no topo */
.page-anonymous .main-content-card {
    padding-top: max(var(--pe-card-pad),
                 calc(var(--pe-logo-h) / 2 + var(--pe-logo-off) + var(--pe-pad-marca)));
}

/* ---------------- a marca ---------------- */
.pe-marca {
    position: absolute;
    top: var(--pe-logo-off);
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 2;
    pointer-events: none;
}
.pe-marca img { width: var(--pe-logo-w); height: auto; display: block; }
/* variante DISCO: a logo dentro de um círculo */
.pe-marca--disco {
    width: var(--pe-logo-d);
    height: var(--pe-logo-d);
    border-radius: 50%;
    background: var(--pk-surface);
    padding: var(--pe-logo-gap);
    display: grid;
    place-items: center;
    box-shadow: 0 var(--pe-dsy) var(--pe-dsb) color-mix(in srgb, var(--pe-sombra) calc(var(--pe-dsa) * 100%), transparent);
}
.pe-marca--disco img { width: 100%; }
.pe-marca--anel { border: 1px solid var(--pk-green-line); }
/* GOTA: vidro CÔNCAVO. Convexo tem luz em cima; côncavo tem SOMBRA no topo
   interno e LUZ na base interna, porque a superfície afunda. */
.pe-marca--gota {
    background: linear-gradient(165deg, color-mix(in srgb, var(--pk-surface) 40%, transparent), color-mix(in srgb, var(--pk-surface) 14%, transparent));
    box-shadow:
        inset 0 var(--pe-concavo) calc(var(--pe-concavo) * 1.5) color-mix(in srgb, var(--pe-sombra) 30%, transparent),
        inset 0 calc(var(--pe-concavo) * -.8) calc(var(--pe-concavo) * 1.4) color-mix(in srgb, var(--pk-surface) 90%, transparent),
        0 var(--pe-dsy) var(--pe-dsb) color-mix(in srgb, var(--pe-sombra) calc(var(--pe-dsa) * 100%), transparent);
}
.pe-marca--gota::after {
    content: ""; position: absolute; left: 14%; right: 14%; top: 7%; height: 32%;
    border-radius: 50%; pointer-events: none;
    background: linear-gradient(180deg, color-mix(in srgb, var(--pk-surface) 72%, transparent), transparent);
}

/* ---------------- título e campos ---------------- */
/* 🔴 `--pk-text-2xl` (25,5px) NÃO cabe: "Bem-vindo, faça seu login" tem 25
   caracteres e o card deixa ~336px úteis — o limite é 24,4px, e o título
   quebrava em duas linhas por pouco. O clamp mantém uma linha do celular ao
   desktop; `text-wrap: balance` é a rede para um título mais longo que alguém
   escreva depois, que aí quebra equilibrado em vez de deixar uma palavra órfã. */
.page-anonymous .main-content-card h2 {
    font-size: clamp(18px, 5.6vw, var(--pk-text-xl));
    font-weight: 600;
    color: var(--pk-ink);
    text-align: center;
    text-wrap: balance;
    line-height: 1.25;
    margin: 0 0 var(--pk-sp-4);
}
.page-anonymous .card-header h2 { margin-bottom: 0; }
.page-anonymous .form-label { color: var(--pk-ink-2); font-weight: 500; }
.page-anonymous .form-control {
    border: var(--pe-campo-borda) solid var(--pk-border);
    border-radius: var(--pe-campo-raio);
    background: var(--pk-surface);
    color: var(--pk-ink);
    font-family: var(--pk-font);
    font-size: var(--pk-text-md);
    padding: 11px 13px;
    box-shadow: 0 var(--pe-campo-sy) var(--pe-campo-sb) color-mix(in srgb, var(--pe-sombra) 22%, transparent);
    transition: border-color .15s, box-shadow .15s;
}
/* 🔴 O glow do foco é box-shadow empilhado, e a linha é `inset`, NÃO
   border-width: engrossar a borda de verdade muda a caixa e o campo pula ao
   receber o cursor. */
.page-anonymous .form-control:focus {
    border-color: var(--pe-foco-cor);
    outline: none;
    box-shadow:
        inset 0 0 var(--pe-foco-dif) var(--pe-foco-borda) var(--pe-foco-cor),
        0 0 var(--pe-glow-dif) var(--pe-glow) color-mix(in srgb, var(--pk-green) calc(var(--pe-glow-alfa) * 100%), transparent),
        0 var(--pe-campo-sy) var(--pe-campo-sb) color-mix(in srgb, var(--pe-sombra) 22%, transparent);
}
.page-anonymous .btn-primary {
    background: var(--pk-green);
    border-color: var(--pk-green);
    color: var(--pk-on-green);
    font-family: var(--pk-font);
    font-weight: 600;
    font-size: var(--pk-text-md);
    border-radius: var(--pk-radius-sm);
    padding: 12px 16px;
}
.page-anonymous .btn-primary:hover { background: var(--pk-green-hover); border-color: var(--pk-green-hover); color: var(--pk-on-green); }
.page-anonymous .form-label-description a,
.page-anonymous a { color: var(--pk-green-ink); }

/* ---------------- vidro ---------------- */
/* ⚠️ Sem var() DENTRO das funções de filtro: o WebKit (todo navegador do
   iPhone) descarta a propriedade inteira como "Invalid property value". A
   string do backdrop-filter é montada em PHP e sai resolvida no style inline
   do <html> — ver plugin_portalpcnet_entrada_vars(). */
.pe-vidro .main-content-card { background: color-mix(in srgb, var(--pk-surface) calc(var(--pe-card-alfa) * 100%), transparent); }
.pe-vidro-campo .form-control { background: color-mix(in srgb, var(--pk-surface) calc(var(--pe-campo-alfa) * 100%), transparent); }

/* Sem suporte a vidro (WebView antigo, GPU bloqueada), o card translúcido
   ficaria ilegível sobre a malha: volta a ser quase opaco. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .pe-vidro .main-content-card { background: color-mix(in srgb, var(--pk-surface) 94%, transparent); }
    .pe-vidro-campo .form-control { background: color-mix(in srgb, var(--pk-surface) 96%, transparent); }
}
/* Quem pediu MENOS TRANSPARÊNCIA no sistema recebe o card sólido. */
@media (prefers-reduced-transparency: reduce) {
    .pe-vidro .main-content-card { background: color-mix(in srgb, var(--pk-surface) 96%, transparent); }
    .pe-vidro-campo .form-control { background: color-mix(in srgb, var(--pk-surface) 98%, transparent); }
}

/* ================= VALIDAÇÃO =================
   As caixas de mensagem nascem VAZIAS no DOM e o CSS reage a `:empty`. É a
   única forma confiável de uma região `role="alert"` ser anunciada: o leitor de
   tela precisa dela presente desde o início para notar a mudança de conteúdo.
   Em runtime o JS só escreve `textContent` — o visual inteiro está aqui.

   Sem faixa lateral colorida: a regra da casa a proíbe em qualquer largura, e
   o alerta se distingue pelo fundo e pela borda inteira.
   ============================================= */
.pe-aviso:empty,
.pe-erro-campo:empty,
.pe-dica:empty { display: none; }

/* o banner: erro que acusa o PAR usuário+senha, acima do formulário */
.pe-aviso {
    display: flex;
    align-items: flex-start;
    gap: var(--pk-sp-2, 8px);
    margin: 0 0 var(--pk-sp-3, 13px);
    padding: 12px 14px;
    border: 1px solid var(--pk-danger-line);
    border-radius: var(--pk-radius-sm);
    background: var(--pk-danger-soft);
    color: var(--pk-danger-ink);
    font-family: var(--pk-font);
    font-size: var(--pk-text-sm, 14.5px);
    line-height: 1.45;
}
.pe-aviso::before {
    /* ícone desenhado, não glifo de fonte nem emoji */
    content: "";
    flex: 0 0 auto;
    width: 18px; height: 18px;
    margin-top: 1px;
    background: currentColor;
    /* 🔴 data-URI LITERAL, nunca por var(): o WebKit descarta a propriedade
       INTEIRA quando há var() dentro de mask/filter/backdrop-filter, e o
       ícone sumiria em todo iPhone sem um único erro no console.
       `currentColor` + mask é o que deixa o traço herdar a cor do alerta. */
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m21.7 18-8-14a2 2 0 0 0-3.4 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.7-3Z'/%3E%3Cpath d='M12 9v4M12 17h.01'/%3E%3C/svg%3E") center / contain no-repeat;
            mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m21.7 18-8-14a2 2 0 0 0-3.4 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.7-3Z'/%3E%3Cpath d='M12 9v4M12 17h.01'/%3E%3C/svg%3E") center / contain no-repeat;
}
.pe-aviso a {
    color: var(--pk-danger-ink);
    font-weight: 600;
    text-underline-offset: 3px;
}

/* a mensagem de um campo: instrução de reparo, ao lado do que reparar */
.pe-erro-campo {
    margin: 6px 0 0;
    color: var(--pk-danger-ink);
    font-family: var(--pk-font);
    /* `sm` e não `xs`: isto impede a pessoa de entrar, então não é nota de
       rodapé. A dica logo abaixo fica em `xs` — a diferença de tamanho é o
       que separa o veredito do conselho. */
    font-size: var(--pk-text-sm, 14.5px);
    line-height: 1.45;
}
/* a dica é conselho, não veredito: cor neutra, nunca vermelha */
.pe-dica {
    margin: 6px 0 0;
    color: var(--pk-ink-2);
    font-family: var(--pk-font);
    font-size: var(--pk-text-xs, 12.5px);
    line-height: 1.4;
}

/* o campo culpado — só onde há culpa provada */
.page-anonymous .form-control[aria-invalid="true"] {
    border-color: var(--pk-danger);
}
.page-anonymous .form-control[aria-invalid="true"]:focus {
    border-color: var(--pk-danger);
    box-shadow:
        inset 0 0 var(--pe-foco-dif) var(--pe-foco-borda) var(--pk-danger),
        0 0 var(--pe-glow-dif) var(--pe-glow) color-mix(in srgb, var(--pk-danger) 40%, transparent),
        0 var(--pe-campo-sy) var(--pe-campo-sb) color-mix(in srgb, var(--pe-sombra) 22%, transparent);
}

/* ---- modo de alto contraste do Windows (forced-colors) ----
   Medido em 2026-09-28, e os dois defeitos eram invisíveis em qualquer outro
   ambiente:

   1. O ícone SUMIA. `background: currentColor` numa máscara é remapeado para
      Canvas (branco) e não para CanvasText — branco sobre branco. As cores de
      sistema são as únicas que o modo respeita, então é delas que ele sai.
   2. O banner ficava INDISTINGUÍVEL de um campo de texto: o modo força fundo
      branco e borda preta em ambos, e a nossa distinção era só a cor. O que
      sobrevive é a ESPESSURA — por isso ela dobra aqui, e só aqui.

   Não se usa `forced-color-adjust: none`: quem liga esse modo escolheu as
   cores do sistema, e desobedecer seria devolver o problema que ele resolve. */
@media (forced-colors: active) {
    .pe-aviso { border-width: 2px; }
    .pe-aviso::before { background-color: CanvasText; }
    .page-anonymous .form-control[aria-invalid="true"],
    .page-anonymous .form-control[aria-invalid="true"]:focus { border-width: 2px; }
}

/* 🔴 A ocultação do alerta nativo na página de erro é ligada por JS (a classe
   entra no <html>), NUNCA por CSS puro. Quem está sem JS precisa ver o erro do
   core — se isto fosse incondicional, ficaria com um cartão vazio e sem saída.
   O JS ainda arma um cão de guarda que remove a classe em 2,5s. */
html.pe-erro-login .page-anonymous .card-body > .alert { visibility: hidden; }

/* ---------------- o que não se seleciona ----------------
   Rótulo, título e texto de botão não existem para serem copiados: arrastar o
   cursor sobre eles só produz aquele realce azul acidental. A marca também sai
   do caminho — a imagem ainda ganha `draggable="false"` no HTML, senão o
   navegador deixa arrastá-la para fora.

   🔴 A exceção é o CONTATO: e-mail e telefone são precisamente o que alguém vai
   querer copiar daqui, então eles ficam selecionáveis de propósito. E os campos
   nunca entram nesta lista — `user-select: none` num input impede o usuário de
   selecionar o que ele mesmo digitou. */
.page-anonymous .main-content-card h2,
.page-anonymous .form-label,
.page-anonymous .btn-primary,
.page-anonymous .form-label-description,
.pe-marca,
.pe-manual {
    -webkit-user-select: none;
            user-select: none;
}
.pe-marca img { -webkit-user-drag: none; user-drag: none; pointer-events: none; }

.pe-contato,
.page-anonymous .form-control {
    -webkit-user-select: text;
            user-select: text;
}

/* ---------------- rodapé: manual e contato ---------------- */
/* Com `display:contents` na coluna do hook, marca, contato e manual viram
   filhos diretos do `.row`, que é flex.
   - a MARCA e o MANUAL são `absolute`: saem do fluxo e não disputam coluna;
   - o CONTATO fica NO FLUXO, com base 100% e `order` alto — é informação do
     card e cai depois do botão, dentro dele. */
.pe-pe {
    position: absolute;
    top: 100%;
    left: 0; right: 0;
    /* 28px literais, não var(--pk-sp-4): se o token não resolver neste contexto
       a margem vira ZERO e o manual encosta no card — foi o que aconteceu. */
    margin-top: 16px;
    text-align: center;
}
.pe-contato {
    flex: 0 0 100%;
    max-width: 100%;
    order: 9;
    margin: var(--pk-sp-3, 13px) 0 0;
    text-align: center;
    padding-top: var(--pk-sp-3, 13px);
    border-top: 1px solid var(--pk-border);
}
.pe-manual {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 16px;
    border: 1px solid var(--pk-green-line);
    border-radius: var(--pk-radius-pill);
    background: var(--pk-surface);
    box-shadow: 0 2px 8px color-mix(in srgb, var(--pe-sombra) 10%, transparent);
    color: var(--pk-green-ink);
    font-family: var(--pk-font);
    font-weight: 600;
    font-size: var(--pk-text-sm);
    text-decoration: none;
    transition: background .16s, border-color .16s;
}
.pe-manual:hover { background: var(--pk-green-soft); border-color: var(--pk-green-dark); color: var(--pk-green-ink); }
.pe-manual svg { width: 16px; height: 16px; stroke: currentColor; fill: none;
                 stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; flex: 0 0 auto; }
.pe-contato { font-size: var(--pk-text-xs, 12.5px); color: var(--pk-ink-2);
              font-family: var(--pk-font); line-height: 1.6; }
.pe-contato a { color: var(--pk-ink-2); text-decoration: none;
                border-bottom: 1px solid var(--pk-border-hover); white-space: nowrap; }
.pe-contato a:hover { color: var(--pk-green-ink); border-bottom-color: var(--pk-green-line); }
.pe-contato .pe-sep { color: var(--pk-ink-3); margin: 0 7px; }

/* o rodapé é absoluto: o container precisa reservar o espaço dele */

@media (max-width: 600px) {
    .page-anonymous .container-tight { padding-bottom: 140px; }
}
