/* =============================================================================
   Genisium — kit de marca (static/brand/brand.css)

   O portão de entrada comum às três apps do backoffice — Assistente
   (genisium.sousaantunes.com), Inteligência (pulse.) e Operações (stocks.) —:
   o wordmark GENISIUM de partículas sobre o abismo, uma linha mono, as três
   portas e, por baixo, o login de cada app. Auto-suficiente: não depende do
   style.css do Genisium nem do Tailwind das outras apps. Todas as classes
   começam por .gn- (não chocam com Tailwind/shadcn) e os tokens por --gn-.

   O portão ocupa o ecrã todo e traz o seu fundo e color-scheme: dark — serve
   dentro de uma app clara. O resto de cada app não muda com esta folha: não
   há regras globais (nem :root, nem elementos, nem @layer — dentro de uma
   layer as regras do kit perdiam para o reset do Tailwind), só seletores
   .gn-* e as @font-face. Os tokens --gn-* vivem no .gn-gate e no .gn-root
   (para usar uma peça do kit fora do portão: o wordmark pequeno, o seletor
   de apps numa barra lateral).

   REGRAS
     - Editar SÓ no repositório do Genisium. Nos outros copia-se tal e qual e
       um teste compara os sha256 com o MANIFEST.json (ver README.md).
     - Números só na secção de tokens (.gn-root/.gn-gate, abaixo, e a versão
       de movimento reduzido no fim). Nas regras, var(--gn-*), palavras-chave,
       0, % e as unidades de viewport — tests/test_brand_kit.py falha com um
       literal. As condições das @media são a única excepção (não aceitam var()).
     - Estados (atributos, para o React não os apagar ao re-renderizar):
         .gn-gate                        coreografia de entrada LIGADA (como no
                                         Genisium): as portas, as acções e o
                                         painel nascem escondidos até
                                         [data-gn-ready]; rede de 3 s em CSS
         .gn-gate[data-gn-ready]         revelado (o wordmark formou-se ou
                                         passaram 600 ms) — põe-no o
                                         particles.js (reveal: true) ou a app
         .gn-gate[data-gn-reveal="off"]  sem coreografia: tudo visível desde o
                                         primeiro paint (ex.: terminais lentos)
         .gn-gate[data-gn-reveal="quiet"] também a linha e o rodapé
         .gn-gate[data-mode="sso"]       o SSO é a acção primária: o .gn-form
                                         só aparece com .is-open
         .gn-door[aria-current="page"]   a porta da app em que se está
         .gn-wordmark[data-gn-live] / [data-gn-formed]  postos pelo
                                         particles.js
     - As portas são opcionais: sem o .gn-doors (ex.: o consentimento do
       SSO, a meio de uma autorização) o portão fica wordmark + painel, sem
       buracos — a coluna é flex com gap e as acções só reservam altura
       quando há portas.
   ============================================================================= */

/* ---- Fontes ------------------------------------------------------------------
   Relativas a esta folha (./fonts/): num bundle (Vite, Next) o brand.css e a
   pasta fonts/ ficam juntos e o bundler emite as fontes com hash. Subsets
   latin; a versão vai no NOME do ficheiro (nunca sobrescrever in-place).
   Sans variável (100–700), Mono 400/500/600 (números, códigos e etiquetas;
   swap como a Sans: com optional o interior das apps ficava com o fallback do
   sistema na primeira visita, e o 600 evita o negrito sintético dos
   números de ênfase), Unbounded 800 só para a marca (swap: o wordmark de
   texto é o LCP do portão). Os subsets são latin: setas (→ ↗), ≥ ≤ ≠ ≈, Δ e
   ⌘ não existem nestas faces e caem no fallback do sistema. */
@font-face {
    font-family: 'IBM Plex Sans';
    src: url("./fonts/ibm-plex-sans-latin-v23.woff2") format("woff2");
    font-weight: 100 700;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'IBM Plex Mono';
    src: url("./fonts/ibm-plex-mono-latin-400-v20.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'IBM Plex Mono';
    src: url("./fonts/ibm-plex-mono-latin-500-v20.woff2") format("woff2");
    font-weight: 500;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'IBM Plex Mono';
    src: url("./fonts/ibm-plex-mono-latin-600-v20.woff2") format("woff2");
    font-weight: 600;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Unbounded';
    src: url("./fonts/unbounded-latin-800-v5.woff2") format("woff2");
    font-weight: 800;
    font-style: normal;
    font-display: swap;
}

/* Recurso com as métricas da Plex Sans (asc 1025 / desc 275 / UPM 1000): zero
   salto de layout enquanto o woff2 não chega. */
@font-face {
    font-family: 'Plex Sans Fallback';
    src: local('Segoe UI'), local('Arial');
    size-adjust: 104%;
    ascent-override: 98.6%;
    descent-override: 26.4%;
    line-gap-override: 0%;
}

/* ---- Tokens ------------------------------------------------------------------
   No .gn-gate (o portão) e no .gn-root (qualquer outra peça do kit: pôr a
   classe no próprio elemento ou num antepassado) — nunca no :root, para não
   mexer no resto de cada app. A paleta Gelo é a do :root do Genisium
   (static/style.css): o mesmo valor com outro nome — tests/test_brand_kit.py
   falha se as duas divergirem. Contraste WCAG medido sobre o abismo (topo ·
   base · chão das portas e dos campos, o pior caso): o teste recalcula cada
   número. */
.gn-root,
.gn-gate {
    /* Cores */
    --gn-abyss-top: #0D121A;
    --gn-abyss-bot: #06080B;
    --gn-ink: #DCE6F0;              /* as partículas em repouso e o wordmark de texto */
    --gn-ink-rgb: 220, 230, 240;
    --gn-text: #DCE6F0;             /* 14,86 topo · 15,87 base · 13,90 chão */
    --gn-text-secondary: #9BAEBC;   /*  8,20 topo ·  8,76 base ·  7,67 chão */
    --gn-text-muted: #8B9EAC;       /*  6,78 topo ·  7,24 base ·  6,34 chão */
    --gn-text-bright: #F3F9FD;      /* 17,68 topo · 18,89 base · 16,55 chão */
    --gn-accent: #6FD3FF;           /* 11,12 topo · 11,88 base · 10,40 chão */
    --gn-accent-rgb: 111, 211, 255;
    --gn-accent-hover: #ABE5FE;
    --gn-on-accent: #04121A;        /* a tinta sobre o acento: 11,24 · 13,89 no hover */
    --gn-error: #FF8585;            /*  8,00 topo ·  8,54 base ·  7,48 chão */
    --gn-error-rgb: 255, 77, 77;

    /* Derivados: superfícies, bordas, anéis */
    --gn-floor: rgba(var(--gn-ink-rgb), 0.035);     /* o chão das portas e dos campos: sem vidro, sem desfoque */
    --gn-chip: rgba(var(--gn-ink-rgb), 0.08);
    --gn-field-solid: #11151A;                      /* o chão opaco, por cima do autofill do browser */
    --gn-accent-soft: rgba(var(--gn-accent-rgb), 0.10);
    --gn-border: rgba(var(--gn-accent-rgb), 0.28);          /* hairline (estrutura, não controlo) */
    --gn-border-strong: rgba(var(--gn-accent-rgb), 0.54);   /* a borda É o controlo: ≥ 3:1 */
    --gn-border-hover: rgba(var(--gn-accent-rgb), 0.80);
    --gn-error-border: rgba(var(--gn-error-rgb), 0.70);
    --gn-selection: rgba(var(--gn-accent-rgb), 0.35);
    --gn-focus: var(--gn-accent);
    --gn-ring-focus: 0 0 0 3px rgba(var(--gn-accent-rgb), 0.22);
    --gn-ring-error: 0 0 0 3px rgba(var(--gn-error-rgb), 0.22);
    --gn-glow: 0 0 20px rgba(var(--gn-accent-rgb), 0.20);
    --gn-abyss: radial-gradient(120% 80% at 50% 38%, var(--gn-abyss-top), var(--gn-abyss-bot) 72%);

    /* Letra: Sans para ler, Mono para etiquetas e dados, Unbounded só a marca */
    --gn-font-sans: 'IBM Plex Sans', 'Plex Sans Fallback', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --gn-font-mono: 'IBM Plex Mono', ui-monospace, 'Cascadia Mono', Consolas, monospace;
    --gn-font-brand: 'Unbounded', 'Arial Black', system-ui, sans-serif;
    --gn-text-3xs: 10.5px;          /* só etiquetas Mono maiúsculas */
    --gn-text-2xs: 11px;
    --gn-text-xs: 12px;
    --gn-text-sm: 13px;
    --gn-text-md: 14px;
    --gn-text-base: 15px;
    --gn-text-input: 16px;          /* abaixo disto o iOS faz zoom ao focar */
    --gn-text-lg: 17px;
    --gn-wordmark-em: 100px;        /* a letra no viewBox do wordmark (680 × 110) */
    --gn-tracking-line: 0.16em;
    --gn-tracking-label: 0.14em;
    --gn-tracking-door: 0.12em;
    --gn-tracking-narrow: 0.1em;
    --gn-tracking-code: 0.25em;

    /* Espaço (grelha de 4 px, com os meios degraus do desenho fino) */
    --gn-space-half: 2px;
    --gn-space-1: 4px;
    --gn-space-1h: 6px;
    --gn-space-2: 8px;
    --gn-space-3: 12px;
    --gn-space-4: 16px;
    --gn-space-5: 20px;
    --gn-space-6: 24px;
    --gn-space-7: 28px;

    /* Raio, linhas e controlos */
    --gn-radius-xs: 6px;
    --gn-radius-sm: 10px;
    --gn-radius-md: 14px;
    --gn-radius-full: 9999px;
    --gn-hairline: 1px;
    --gn-wire: 2px;                 /* o fio de acento no topo da porta activa */
    --gn-focus-width: 2px;
    --gn-focus-offset: 3px;
    --gn-underline-offset: 3px;
    --gn-control: 48px;             /* botões e campos do portão */
    --gn-hit-min: 44px;             /* alvo de toque mínimo (WCAG 2.5.5) */
    --gn-error-min: 20px;
    --gn-autofill-cover: 100px;
    --gn-glow-blur: 5px;

    /* Movimento (zerado em prefers-reduced-motion, no fim da folha) */
    --gn-dur-fast: 120ms;
    --gn-dur-base: 200ms;
    --gn-dur-slow: 450ms;
    --gn-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --gn-failsafe: 3s;              /* sem o JS da app, o portão revela-se na mesma */
    --gn-rise: 8px;
    --gn-nudge: 2px;
    --gn-nudge-x: 3px;
    --gn-press: 0.98;

    /* Medidas do portão */
    --gn-wordmark-max: 960px;
    --gn-doors-min: 760px;          /* três portas lado a lado sem partir a linha */
    --gn-doors-max: 960px;
    --gn-actions: 360px;
    --gn-actions-wide: 560px;       /* janela baixa e larga: os dois campos lado a lado */
    --gn-panel-max: 480px;
    --gn-foot-measure: 44ch;
    /* As acções reservam a altura do login de dois campos (rótulo + campo, duas
       vezes, e o Entrar): o wordmark e as portas ficam no MESMO sítio nas três
       apps, com SSO ou sem ele. */
    --gn-actions-reserve: calc(2 * (var(--gn-text-2xs) * 1.2 + var(--gn-space-1h) + var(--gn-control)) + 2 * var(--gn-space-3) + var(--gn-control));

    /* O seletor de apps (.gn-apps): herda a cor do texto de quem o usa (lê-se
       numa barra clara e num cabeçalho escuro); o activo leva o fio e um
       fundo de acento. A app pode trocar estes três no seu CSS. */
    --gn-apps-wire: var(--gn-accent);
    --gn-apps-current-bg: rgba(var(--gn-accent-rgb), 0.14);
    --gn-apps-hover-bg: rgba(var(--gn-accent-rgb), 0.08);
    --gn-apps-dim: 0.72;            /* os outros, apagados: ≥ 4,5:1 se o texto da app tiver ≥ 10,5:1 (fundo claro) / ≥ 8:1 (escuro) */
}

/* ---- Utilitários -------------------------------------------------------------- */
.gn-sr-only {
    position: absolute;
    width: var(--gn-hairline);
    height: var(--gn-hairline);
    padding: 0;
    margin: calc(-1 * var(--gn-hairline));
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ---- O portão ----------------------------------------------------------------- */
.gn-gate,
.gn-gate *,
.gn-gate *::before,
.gn-gate *::after {
    box-sizing: border-box;
}

/* Ocupa o ecrã e rola por dentro (LAND-14): num telemóvel na horizontal ou
   numa janela baixa chega-se sempre ao botão — nunca overflow: hidden. */
.gn-gate {
    position: relative;
    isolation: isolate;
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: 100dvh;
    margin: 0;
    overflow-y: auto;
    overscroll-behavior-y: contain;   /* não puxa o pull-to-refresh */
    background: var(--gn-abyss);
    color: var(--gn-text);
    color-scheme: dark;
    font-family: var(--gn-font-sans);
    font-size: var(--gn-text-base);
    font-weight: 400;
    line-height: 1.6;
    letter-spacing: 0;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}
.gn-gate[hidden] { display: none; }

.gn-gate__col {
    flex: 1 0 auto;
    min-height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: clamp(var(--gn-space-4), 3vh, var(--gn-space-7));
    /* Safe-area nos quatro lados: no iPhone na horizontal o entalhe e a barra
       de início comem as margens laterais (viewport-fit=cover) e o portão
       encostava-lhes. Soma-se ao respiro de sempre: sem inset (env = 0, o
       resto dos ecrãs e dos browsers) o padding é o mesmo de antes. */
    padding: calc(var(--gn-space-6) + env(safe-area-inset-top, 0px))
             calc(var(--gn-space-4) + env(safe-area-inset-right, 0px))
             calc(var(--gn-space-5) + env(safe-area-inset-bottom, 0px))
             calc(var(--gn-space-4) + env(safe-area-inset-left, 0px));
    text-align: center;
}

.gn-gate ::selection {
    background: var(--gn-selection);
    color: var(--gn-text-bright);
}
.gn-gate :focus-visible {
    outline: var(--gn-focus-width) solid var(--gn-focus);
    outline-offset: var(--gn-focus-offset);
}

/* ---- O wordmark ----------------------------------------------------------------
   O SVG de texto ocupa a caixa (viewBox = geometria das partículas do
   particles.js) e é o primeiro paint; o canvas pinta por cima e esconde-o
   quando arranca (data-gn-live). Serve também fora do portão (o Genisium usa-o
   pequeno e parado no estado vazio do chat). */
.gn-wordmark {
    position: relative;
    display: block;
    margin: 0;
    padding: 0;
    aspect-ratio: 680 / 110;
    font: inherit;
}
.gn-wordmark__text,
.gn-wordmark canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    max-width: none;
    pointer-events: none;
}
.gn-wordmark__text {
    overflow: visible;
    transition: opacity var(--gn-dur-slow) ease;
}
.gn-wordmark__text text {
    font-family: var(--gn-font-brand);
    font-weight: 800;
    font-size: var(--gn-wordmark-em);
    fill: var(--gn-ink);
}
.gn-wordmark[data-gn-live] .gn-wordmark__text { opacity: 0; }
/* O brilho é um canvas a ¼ de resolução: o upscale e um desfoque leve chegam. */
.gn-wordmark__glow {
    filter: blur(var(--gn-glow-blur));
    opacity: 0.85;
}

.gn-gate__wordmark {
    margin-top: auto;               /* centro óptico um pouco acima do geométrico */
    width: min(60vw, var(--gn-wordmark-max));
}

/* ---- A linha mono e o rodapé (copy genérica: o portão é público) -------------- */
.gn-gate__line {
    margin: 0;
    font: 500 var(--gn-text-2xs)/1.3 var(--gn-font-mono);
    letter-spacing: var(--gn-tracking-line);
    text-transform: uppercase;
    text-wrap: balance;
    color: var(--gn-text-secondary);
}

.gn-gate__foot {
    margin: auto 0 0;
    max-width: var(--gn-foot-measure);
    font: 400 var(--gn-text-xs)/1.5 var(--gn-font-mono);
    color: var(--gn-text-secondary);
}

/* ---- As três portas ------------------------------------------------------------
   O seletor de apps em ponto grande. Mono maiúsculas; a app em que se está tem
   o fundo do acento + o fio no topo e o ponto (•) — não é ligação —; as outras
   levam ao domínio delas (↗). Três lado a lado com a largura do wordmark; em
   coluna no telemóvel. */
.gn-doors {
    width: min(100%, max(var(--gn-doors-min), 60vw), var(--gn-doors-max));
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--gn-space-1);
    margin: 0;
    padding: var(--gn-space-1);
    background: var(--gn-floor);
    border: var(--gn-hairline) solid var(--gn-border);
    border-radius: var(--gn-radius-md);
    text-align: left;
}

.gn-door {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
        "name glyph"
        "desc desc";
    align-content: start;
    column-gap: var(--gn-space-2);
    row-gap: var(--gn-space-1h);
    min-height: var(--gn-hit-min);
    margin: 0;
    padding: var(--gn-space-4) var(--gn-space-3) var(--gn-space-5) var(--gn-space-4);
    border-radius: var(--gn-radius-sm);   /* raio de dentro = de fora − o padding do contentor */
    color: var(--gn-text-secondary);
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--gn-dur-base) var(--gn-ease-out),
                color var(--gn-dur-base) var(--gn-ease-out);
}

.gn-door__name {
    grid-area: name;
    font: 500 var(--gn-text-xs)/1.3 var(--gn-font-mono);
    letter-spacing: var(--gn-tracking-door);
    text-transform: uppercase;
}

.gn-door__desc {
    grid-area: desc;
    font: 400 var(--gn-text-sm)/1.45 var(--gn-font-sans);
    color: var(--gn-text-muted);
    text-wrap: balance;
    transition: color var(--gn-dur-base) var(--gn-ease-out);
}

/* Canto de cima à direita: ↗ = "vai para outra app"; • = "estás aqui". Forma
   diferente nos dois estados — o activo não depende só da cor. */
.gn-door__glyph {
    grid-area: glyph;
    align-self: center;
    display: grid;
    place-items: center;
    width: var(--gn-space-4);
    height: var(--gn-space-4);
    color: var(--gn-text-muted);
}
.gn-door__arrow {
    display: block;
    width: 100%;
    height: 100%;
    transition: transform var(--gn-dur-base) var(--gn-ease-out);
}
.gn-door__here {
    display: none;
    width: var(--gn-space-1h);
    height: var(--gn-space-1h);
    border-radius: var(--gn-radius-full);
    background: var(--gn-accent);
    box-shadow: var(--gn-glow);
}

a.gn-door:hover {
    background: var(--gn-accent-soft);
    color: var(--gn-text-bright);
}
a.gn-door:hover .gn-door__desc { color: var(--gn-text-secondary); }
a.gn-door:hover .gn-door__glyph { color: var(--gn-accent); }
a.gn-door:hover .gn-door__arrow { transform: translate(var(--gn-nudge), calc(-1 * var(--gn-nudge))); }
.gn-gate .gn-door:focus-visible { outline-offset: calc(-1 * var(--gn-focus-width)); }

.gn-door[aria-current="page"] {
    background: var(--gn-accent-soft);
    color: var(--gn-text-bright);
    box-shadow: inset 0 var(--gn-wire) 0 var(--gn-accent);
    cursor: default;
}
.gn-door[aria-current="page"] .gn-door__desc { color: var(--gn-text); }
.gn-door[aria-current="page"] .gn-door__arrow { display: none; }
.gn-door[aria-current="page"] .gn-door__here { display: block; }

/* ---- As acções: o login da app ------------------------------------------------- */
.gn-gate__actions {
    width: min(100%, var(--gn-actions));
    display: flex;
    flex-direction: column;
    gap: var(--gn-space-3);
    text-align: left;
}
/* Com as portas, as acções reservam a altura do login de dois campos: o
   wordmark e as portas ficam no MESMO sítio nos três domínios (com SSO ou
   sem ele). Sem portas, nada a alinhar — e nenhum buraco por baixo. Sem
   :has() (browsers antigos) só se perde o alinhamento entre domínios. */
.gn-gate__col:has(> .gn-doors) > .gn-gate__actions { min-height: var(--gn-actions-reserve); }

/* Com SSO, «Continuar com a Conta Genisium» é a acção primária e o login próprio da
   app a alternativa: o formulário só abre pela ligação (.gn-form.is-open). */
.gn-sso {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--gn-space-1h);
}
.gn-sso[hidden] { display: none; }
.gn-gate[data-mode="sso"] .gn-form:not(.is-open) { display: none; }

/* ---- Coreografia de entrada (ligada por omissão; data-gn-reveal="off" desliga) ----
   As portas, as acções e o painel nascem invisíveis E fora do foco
   (visibility, não só opacity) e revelam-se com [data-gn-ready] quando o
   wordmark se forma ou ao fim de 600 ms, o que chegar primeiro (o
   particles.js fá-lo com reveal: true). Rede: sem JS, aparecem na mesma ao
   fim de --gn-failsafe. Com data-gn-reveal="off" no HTML, nada se esconde —
   nem antes de o JS correr. */
.gn-gate:not([data-gn-ready]):not([data-gn-reveal="off"]) .gn-doors,
.gn-gate:not([data-gn-ready]):not([data-gn-reveal="off"]) .gn-gate__actions,
.gn-gate:not([data-gn-ready]):not([data-gn-reveal="off"]) .gn-panel {
    opacity: 0;
    visibility: hidden;
    transform: translateY(var(--gn-rise));
    animation: gn-gate-failsafe 0s linear var(--gn-failsafe) forwards;
}
.gn-gate[data-gn-reveal="quiet"]:not([data-gn-ready]) .gn-gate__line,
.gn-gate[data-gn-reveal="quiet"]:not([data-gn-ready]) .gn-gate__foot {
    visibility: hidden;
    animation: gn-gate-failsafe 0s linear var(--gn-failsafe) forwards;
}
.gn-gate:not([data-gn-reveal="off"]) .gn-doors,
.gn-gate:not([data-gn-reveal="off"]) .gn-gate__actions,
.gn-gate:not([data-gn-reveal="off"]) .gn-panel {
    transition: opacity var(--gn-dur-slow) var(--gn-ease-out),
                transform var(--gn-dur-slow) var(--gn-ease-out),
                visibility 0s;
}
@keyframes gn-gate-failsafe {
    to { opacity: 1; visibility: visible; transform: none; }
}

/* ---- Botões ------------------------------------------------------------------- */
.gn-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--gn-space-2);
    min-height: var(--gn-control);
    margin: 0;
    padding: 0 var(--gn-space-6);
    font: 600 var(--gn-text-base)/1.2 var(--gn-font-sans);
    letter-spacing: 0;
    text-align: center;
    text-decoration: none;
    text-transform: none;
    white-space: nowrap;
    color: var(--gn-text);
    background: transparent;
    border: var(--gn-hairline) solid transparent;
    border-radius: var(--gn-radius-full);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--gn-dur-fast) var(--gn-ease-out),
                border-color var(--gn-dur-fast) var(--gn-ease-out),
                color var(--gn-dur-fast) var(--gn-ease-out),
                transform var(--gn-dur-fast) var(--gn-ease-out);
}
.gn-btn[hidden] { display: none; }
.gn-btn:active:where(:not(:disabled, [aria-disabled="true"])) { transform: scale(var(--gn-press)); }
.gn-btn:disabled,
.gn-btn[aria-disabled="true"] { opacity: 0.55; cursor: not-allowed; }
.gn-btn[aria-busy="true"] { cursor: progress; }
.gn-btn svg {
    flex: none;
    transition: transform var(--gn-dur-base) var(--gn-ease-out);
}
/* A seta (e o ícone do SSO) avançam no hover. */
.gn-btn:hover:where(:not(:disabled, [aria-disabled="true"])) svg { transform: translateX(var(--gn-nudge-x)); }

/* Primário: o acento sólido com a tinta da marca. */
.gn-btn--primary { background: var(--gn-accent); color: var(--gn-on-accent); }
.gn-btn--primary:hover:where(:not(:disabled, [aria-disabled="true"])) { background: var(--gn-accent-hover); }
/* Secundário: contorno — a borda É o controlo (≥ 3:1). */
.gn-btn--secondary { border-color: var(--gn-border-strong); color: var(--gn-text); }
.gn-btn--secondary:hover:where(:not(:disabled, [aria-disabled="true"])) {
    border-color: var(--gn-border-hover);
    background: var(--gn-accent-soft);
    color: var(--gn-text-bright);
}
.gn-btn--block { width: 100%; }

/* Ligação: a alternativa discreta («Entrar com PIN», «Esqueci-me do PIN»). Tem
   o alvo de toque inteiro, mesmo sendo texto. */
.gn-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    align-self: center;
    min-height: var(--gn-hit-min);
    margin: 0;
    padding: var(--gn-space-2) var(--gn-space-3);
    border: 0;
    background: none;
    font: 500 var(--gn-text-md)/1.2 var(--gn-font-sans);
    color: var(--gn-text-secondary);
    text-decoration: underline;
    text-underline-offset: var(--gn-underline-offset);
    text-decoration-thickness: var(--gn-hairline);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.gn-link:hover { color: var(--gn-text-bright); }
.gn-link[hidden] { display: none; }

/* ---- Formulário ----------------------------------------------------------------- */
.gn-form {
    display: flex;
    flex-direction: column;
    gap: var(--gn-space-3);
    margin: 0;
}
.gn-form[hidden] { display: none; }

.gn-field {
    display: block;
    min-width: 0;
    margin: 0;
}

.gn-label {
    display: block;
    margin: 0 0 var(--gn-space-1h);
    font: 500 var(--gn-text-2xs)/1.2 var(--gn-font-mono);
    letter-spacing: var(--gn-tracking-label);
    text-transform: uppercase;
    color: var(--gn-text-secondary);
}

/* 48 px e 16 px de letra (nunca < 16: o iOS faz zoom ao focar). A borda É o
   controlo (≥ 3:1 sobre o abismo); o foco é a borda do acento + o anel. */
.gn-input {
    display: block;
    width: 100%;
    min-width: 0;
    min-height: var(--gn-control);
    margin: 0;
    padding: 0 var(--gn-space-4);
    font: 400 var(--gn-text-input)/1.4 var(--gn-font-sans);
    letter-spacing: 0;
    color: var(--gn-text-bright);
    background: var(--gn-floor);
    border: var(--gn-hairline) solid var(--gn-border-strong);
    border-radius: var(--gn-radius-md);
    outline: none;
    box-shadow: none;
    -webkit-appearance: none;
    appearance: none;
    transition: border-color var(--gn-dur-fast) var(--gn-ease-out),
                box-shadow var(--gn-dur-fast) var(--gn-ease-out);
}
.gn-input::placeholder {
    color: var(--gn-text-muted);
    opacity: 1;
    font-size: var(--gn-text-md);
}
.gn-input:focus { border-color: var(--gn-accent); box-shadow: var(--gn-ring-focus); }
.gn-gate .gn-input:focus-visible { outline: none; }
.gn-input:disabled { opacity: 0.55; cursor: not-allowed; }
.gn-input[aria-invalid="true"] { border-color: var(--gn-error-border); }
.gn-input[aria-invalid="true"]:focus { box-shadow: var(--gn-ring-error); }
/* Códigos e PINs: mono e espaçado; o placeholder volta ao texto normal. */
.gn-input--code { font-family: var(--gn-font-mono); letter-spacing: var(--gn-tracking-code); }
.gn-input--code::placeholder { font-family: var(--gn-font-sans); letter-spacing: normal; }
/* O autofill do browser pinta o campo de claro, mesmo com color-scheme dark. */
.gn-input:-webkit-autofill,
.gn-input:-webkit-autofill:hover {
    -webkit-text-fill-color: var(--gn-text-bright);
    caret-color: var(--gn-text-bright);
    -webkit-box-shadow: 0 0 0 var(--gn-autofill-cover) var(--gn-field-solid) inset;
    box-shadow: 0 0 0 var(--gn-autofill-cover) var(--gn-field-solid) inset;
}
.gn-input:-webkit-autofill:focus {
    -webkit-text-fill-color: var(--gn-text-bright);
    -webkit-box-shadow: 0 0 0 var(--gn-autofill-cover) var(--gn-field-solid) inset, var(--gn-ring-focus);
    box-shadow: 0 0 0 var(--gn-autofill-cover) var(--gn-field-solid) inset, var(--gn-ring-focus);
}

/* Palavra-passe: o controlo de visibilidade conserva o alvo de toque do kit. */
.gn-input-wrap { position: relative; }
.gn-input-wrap .gn-input { padding-right: calc(var(--gn-control) + var(--gn-space-2)); }
.gn-password-toggle {
    position: absolute;
    inset-block: 0;
    right: 0;
    width: var(--gn-control);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    border-radius: var(--gn-radius-md);
    background: transparent;
    color: var(--gn-text-secondary);
    cursor: pointer;
}
.gn-password-toggle:hover { color: var(--gn-text-bright); }

/* Ajuda debaixo de um campo (os formatos aceites). */
.gn-hint {
    display: block;
    margin: var(--gn-space-2) 0 0;
    font-size: var(--gn-text-xs);
    line-height: 1.5;
    color: var(--gn-text-secondary);
}
.gn-hint code {
    padding: var(--gn-space-half) var(--gn-space-1);
    border-radius: var(--gn-radius-xs);
    background: var(--gn-chip);
    font-family: var(--gn-font-mono);
    font-size: var(--gn-text-2xs);
    color: var(--gn-accent);
}

/* ---- Mensagens ------------------------------------------------------------------ */
/* O erro do login (role="alert"). Vazio, não ocupa altura. */
.gn-error {
    margin: 0;
    min-height: var(--gn-error-min);
    font-size: var(--gn-text-sm);
    font-weight: 500;
    color: var(--gn-error);
    text-align: center;
}
.gn-error:empty { min-height: 0; }

/* Uma resposta neutra (role="status"), ex.: «o administrador foi avisado». */
.gn-status {
    margin: 0;
    font-size: var(--gn-text-xs);
    line-height: 1.5;
    color: var(--gn-text-secondary);
}
.gn-status:empty { display: none; }

/* ---- Painel (ex.: o pedido de consentimento do SSO) ------------------------------ */
.gn-panel {
    width: min(100%, var(--gn-panel-max));
    margin: 0;
    padding: var(--gn-space-5);
    background: var(--gn-floor);
    border: var(--gn-hairline) solid var(--gn-border);
    border-radius: var(--gn-radius-md);
    text-align: left;
}
.gn-panel__title {
    margin: 0 0 var(--gn-space-2);
    font: 600 var(--gn-text-lg)/1.3 var(--gn-font-sans);
    color: var(--gn-text-bright);
}
.gn-panel__text {
    margin: 0 0 var(--gn-space-3);
    font-size: var(--gn-text-md);
    color: var(--gn-text-secondary);
}
.gn-panel__list {
    margin: 0 0 var(--gn-space-4);
    padding: 0 0 0 var(--gn-space-5);
    list-style: disc;
    font-size: var(--gn-text-md);
    color: var(--gn-text);
}
.gn-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gn-space-3);
}
.gn-row > .gn-btn { flex: 1 1 0; }

/* ---- Seletor de apps (.gn-apps) ---------------------------------------------------
   As três apps do backoffice, pela ordem do apps.js: numa fila (cabeçalhos) ou
   empilhadas (.gn-apps--stack, a forma recomendada nas barras laterais — numa
   barra de 256–288 px «INTELIGÊNCIA» não cabe em três colunas). Mono em
   maiúsculas; a app em que se está tem aria-current="page", não é ligação e
   leva o fio de acento (em cima na fila, à esquerda empilhado) e o fundo do
   acento. Herda a cor do texto de quem o usa: lê-se numa barra clara e num
   cabeçalho escuro. Precisa dos tokens — .gn-root no próprio <nav>. */
.gn-apps {
    display: flex;
    gap: var(--gn-space-half);
    margin: 0;
    padding: 0;
    list-style: none;
}
.gn-apps__item {
    display: inline-flex;
    align-items: center;
    margin: 0;
    padding: var(--gn-space-1h) var(--gn-space-2);
    border-radius: var(--gn-radius-xs);
    font: 500 var(--gn-text-3xs)/1.2 var(--gn-font-mono);
    letter-spacing: var(--gn-tracking-door);
    text-transform: uppercase;
    text-decoration: none;
    white-space: nowrap;
    color: inherit;
    opacity: var(--gn-apps-dim);
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--gn-dur-base) var(--gn-ease-out),
                opacity var(--gn-dur-base) var(--gn-ease-out);
}
a.gn-apps__item:hover {
    opacity: 1;
    background: var(--gn-apps-hover-bg);
}
.gn-apps__item:focus-visible {
    outline: var(--gn-focus-width) solid var(--gn-apps-wire);
    outline-offset: var(--gn-hairline);
}
.gn-apps__item[aria-current="page"] {
    opacity: 1;
    background: var(--gn-apps-current-bg);
    box-shadow: inset 0 var(--gn-wire) 0 var(--gn-apps-wire);
    cursor: default;
}
/* Empilhado: uma app por linha, com o alvo de toque inteiro. */
.gn-apps--stack {
    flex-direction: column;
    gap: 0;
}
.gn-apps--stack .gn-apps__item {
    display: flex;
    min-height: var(--gn-hit-min);
    padding: 0 var(--gn-space-3);
    font-size: var(--gn-text-2xs);
}
.gn-apps--stack .gn-apps__item[aria-current="page"] {
    box-shadow: inset var(--gn-wire) 0 0 var(--gn-apps-wire);
}
@media (pointer: coarse) {
    .gn-apps__item { min-height: var(--gn-hit-min); }
}

/* ---- Telemóvel e janelas baixas ------------------------------------------------- */
@media (orientation: portrait) {
    .gn-gate__wordmark { width: 86vw; }
}

@media (max-width: 640px) {
    /* As três portas em coluna, uma por linha: nome e linha à esquerda, o ↗/•
       ao centro da altura, à direita. */
    .gn-doors { grid-template-columns: 1fr; }
    .gn-door {
        grid-template-areas:
            "name glyph"
            "desc glyph";
        row-gap: var(--gn-space-half);
        padding: var(--gn-space-3) var(--gn-space-4);
    }
}

@media (max-width: 420px) {
    .gn-gate__line { font-size: var(--gn-text-3xs); letter-spacing: var(--gn-tracking-narrow); }
}

/* LAND-14: janelas baixas (telemóvel na horizontal, portátil com a barra de
   tarefas) — o wordmark desce para ~20vh (124vh de largura × 110/680), a
   linha, as ajudas e as descrições das portas saem, e o «Entrar» fica ao lado
   da ligação que vem a seguir. */
@media (max-height: 500px) {
    .gn-gate__wordmark { width: min(86vw, 124vh); }
    .gn-gate__line,
    .gn-hint,
    .gn-door__desc { display: none; }
    .gn-gate__col {
        gap: var(--gn-space-3);
        padding-top: calc(var(--gn-space-3) + env(safe-area-inset-top, 0px));
        padding-bottom: calc(var(--gn-space-3) + env(safe-area-inset-bottom, 0px));
    }
    .gn-gate__col:has(> .gn-doors) > .gn-gate__actions { min-height: 0; }
    .gn-door {
        grid-template-areas: "name glyph";
        align-content: center;
        padding: var(--gn-space-2) var(--gn-space-3) var(--gn-space-2) var(--gn-space-4);
    }
    .gn-form {
        display: grid;
        grid-template-columns: 1fr auto;
        column-gap: var(--gn-space-3);
    }
    .gn-form > * { grid-column: 1 / -1; }
    .gn-form > .gn-btn--primary { grid-column: 1; }
    .gn-form > .gn-btn--primary + .gn-link { grid-column: 2; }
}

/* Baixa e larga (o telemóvel na horizontal): os campos lado a lado — assim o
   portão inteiro, rodapé incluído, cabe em 390 px de altura. */
@media (max-height: 500px) and (min-width: 600px) {
    .gn-gate__actions { width: min(100%, var(--gn-actions-wide)); }
    .gn-form { grid-template-columns: 1fr 1fr; }
    .gn-form > .gn-field { grid-column: auto; }
    .gn-form > .gn-btn--primary + .gn-link { justify-self: start; }
}

/* ---- Cores forçadas (Windows de alto contraste) ---------------------------------- */
@media (forced-colors: active) {
    .gn-door[aria-current="page"] {
        outline: var(--gn-wire) solid CanvasText;
        outline-offset: calc(-1 * var(--gn-wire));
    }
    .gn-door__here { forced-color-adjust: none; background: CanvasText; }
    .gn-btn--primary { border-color: ButtonText; }
}

/* ---- Movimento reduzido ---------------------------------------------------------
   Só tokens: as transições ficam instantâneas e nada desliza (as partículas
   param no particles.js, que lê a mesma media query). A rede de 3 s mantém-se:
   não é movimento. */
@media (prefers-reduced-motion: reduce) {
    .gn-root,
    .gn-gate {
        --gn-dur-fast: 0s;
        --gn-dur-base: 0s;
        --gn-dur-slow: 0s;
        --gn-rise: 0px;
        --gn-nudge: 0px;
        --gn-nudge-x: 0px;
        --gn-press: 1;
    }
}
