/* =========================================================================
   Sistema de Gestão Educacional — tema da aplicação
   Estende o Bootstrap 5.3 via variáveis CSS. Sem !important, sem hack.
   ========================================================================= */

/* -------------------------------------------------------------- Tipografia */
@font-face {
   font-family: 'InterVar';
   font-style: normal;
   font-weight: 100 900;
   font-display: swap;
   src: local('Inter'), local('Segoe UI Variable Text'), local('Segoe UI');
}

/* ------------------------------------------------------------- Tema: claro */
:root,
[data-bs-theme="light"] {
   --gx-primary:        #4f46e5;
   --gx-primary-hover:  #4338ca;
   --gx-primary-soft:   #eef2ff;

   --gx-sidebar-bg:     #ffffff;
   --gx-sidebar-border: #e9ecef;
   --gx-sidebar-text:   #495057;
   --gx-sidebar-hover:  #f1f3f9;
   --gx-sidebar-active-bg:   var(--gx-primary-soft);
   --gx-sidebar-active-text: var(--gx-primary);

   --gx-topbar-bg:      rgba(255, 255, 255, .85);
   --gx-body-bg:        #f6f7fb;
   --gx-card-bg:        #ffffff;
   --gx-card-border:    #e9ecef;

   --gx-shadow-sm: 0 1px 2px rgba(16, 24, 40, .05);
   --gx-shadow:    0 1px 3px rgba(16, 24, 40, .1), 0 1px 2px rgba(16, 24, 40, .06);
   --gx-shadow-lg: 0 10px 15px -3px rgba(16, 24, 40, .08), 0 4px 6px -4px rgba(16, 24, 40, .05);

   --bs-body-bg:        var(--gx-body-bg);
   --bs-primary:        var(--gx-primary);
   --bs-primary-rgb:    79, 70, 229;
   --bs-link-color:     var(--gx-primary);
   --bs-link-hover-color: var(--gx-primary-hover);
   --bs-border-radius:    .625rem;
   --bs-border-radius-sm: .375rem;
   --bs-border-radius-lg: .875rem;
   --bs-body-font-family: 'InterVar', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
}

/* ------------------------------------------------------------ Tema: escuro */
[data-bs-theme="dark"] {
   --gx-primary:        #818cf8;
   --gx-primary-hover:  #a5b4fc;
   --gx-primary-soft:   #1e1b4b;

   --gx-sidebar-bg:     #14161f;
   --gx-sidebar-border: #262a38;
   --gx-sidebar-text:   #adb5c4;
   --gx-sidebar-hover:  #1c1f2b;
   --gx-sidebar-active-bg:   #1e1b4b;
   --gx-sidebar-active-text: #a5b4fc;

   --gx-topbar-bg:      rgba(17, 19, 26, .85);
   --gx-body-bg:        #0f1117;
   --gx-card-bg:        #171a23;
   --gx-card-border:    #262a38;

   --gx-shadow-sm: 0 1px 2px rgba(0, 0, 0, .3);
   --gx-shadow:    0 1px 3px rgba(0, 0, 0, .4);
   --gx-shadow-lg: 0 10px 20px rgba(0, 0, 0, .45);

   --bs-body-bg:        var(--gx-body-bg);
   --bs-body-color:     #d5d9e3;
   --bs-emphasis-color: #f4f6fa;
   --bs-primary:        var(--gx-primary);
   --bs-primary-rgb:    129, 140, 248;
   --bs-link-color:     var(--gx-primary);
   --bs-link-hover-color: var(--gx-primary-hover);
   --bs-border-color:   var(--gx-card-border);
}

/* -------------------------------------------------------------------- Base */
body {
   background-color: var(--gx-body-bg);
   font-size: .9375rem;
   -webkit-font-smoothing: antialiased;
}

::selection {
   background: var(--gx-primary);
   color: #fff;
}

/* Scrollbar discreta */
* { scrollbar-width: thin; }
::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
   background: var(--bs-border-color);
   border-radius: 6px;
   border: 2px solid transparent;
   background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background-color: var(--bs-secondary-color); background-clip: content-box; }

/* ===================================================== Estrutura do layout */
.gx-app {
   display: grid;
   /* minmax(0, 1fr) e não 1fr: o mínimo automático de uma coluna de grid é o
      min-content do conteúdo, então uma tabela larga empurraria a coluna,
      criando rolagem horizontal na página e tirando a sidebar da tela. */
   grid-template-columns: var(--gx-sidebar-w, 264px) minmax(0, 1fr);
   min-height: 100vh;
}

.gx-app.is-collapsed { --gx-sidebar-w: 76px; }

/* Véu da gaveta no celular.
   position: fixed precisa valer em TODA largura: sem isso, no desktop o
   backdrop é um item comum do grid e ocupa a primeira coluna, empurrando a
   sidebar para a coluna do conteúdo e o conteúdo para a linha de baixo. */
.gx-backdrop {
   position: fixed;
   inset: 0;
   background: rgba(0, 0, 0, .45);
   z-index: 1029;
   opacity: 0;
   pointer-events: none;
   transition: opacity .2s ease;
}

/* ----------------------------------------------------------------- Sidebar */
.gx-sidebar {
   background: var(--gx-sidebar-bg);
   border-right: 1px solid var(--gx-sidebar-border);
   display: flex;
   flex-direction: column;
   position: sticky;
   top: 0;
   /* align-self: start é obrigatório aqui. Grid items nascem com
      align-self: stretch e passam a ocupar toda a altura da linha; sem folga
      entre o elemento e o container, position: sticky não tem onde grudar e
      a sidebar rola junto com a página. */
   align-self: start;
   height: 100vh;
   height: 100dvh; /* no celular, desconta a barra de endereço */
   overflow: hidden;
   z-index: 1030;
}

.gx-sidebar__brand {
   display: flex;
   align-items: center;
   gap: .625rem;
   height: 62px;
   padding: 0 1.125rem;
   border-bottom: 1px solid var(--gx-sidebar-border);
   text-decoration: none;
   color: var(--bs-emphasis-color);
   flex-shrink: 0;
}

.gx-sidebar__logo {
   width: 34px;
   height: 34px;
   border-radius: .5rem;
   background: linear-gradient(135deg, var(--gx-primary), #7c3aed);
   color: #fff;
   display: grid;
   place-items: center;
   /* justify-content além do place-items: as telas de login aplicam
      d-inline-flex por cima, e sob flexbox o justify-items do place-items é
      ignorado — o ícone encostava na borda esquerda do quadrado. Com os dois
      declarados, o logo fica centrado em grid ou em flex. */
   justify-content: center;
   font-size: 1.05rem;
   line-height: 1;
   flex-shrink: 0;
}

.gx-sidebar__name {
   font-weight: 650;
   letter-spacing: -.01em;
   white-space: nowrap;
   line-height: 1.1;
}
.gx-sidebar__name small {
   display: block;
   font-weight: 400;
   font-size: .6875rem;
   color: var(--bs-secondary-color);
}

.gx-sidebar__nav {
   flex: 1;
   overflow-y: auto;
   overflow-x: hidden;
   padding: .75rem .625rem 1.5rem;
}

.gx-nav-titulo {
   font-size: .6875rem;
   font-weight: 600;
   letter-spacing: .06em;
   text-transform: uppercase;
   color: var(--bs-secondary-color);
   padding: 1rem .75rem .375rem;
   white-space: nowrap;
}

.gx-nav-link {
   display: flex;
   align-items: center;
   gap: .75rem;
   padding: .5rem .75rem;
   margin-bottom: .125rem;
   border-radius: .5rem;
   color: var(--gx-sidebar-text);
   text-decoration: none;
   font-weight: 500;
   white-space: nowrap;
   transition: background-color .15s ease, color .15s ease;
}

.gx-nav-link:hover { background: var(--gx-sidebar-hover); color: var(--bs-emphasis-color); }

.gx-nav-link.active {
   background: var(--gx-sidebar-active-bg);
   color: var(--gx-sidebar-active-text);
   font-weight: 600;
}

.gx-nav-link i:first-child {
   font-size: 1.075rem;
   width: 1.25rem;
   text-align: center;
   flex-shrink: 0;
}

.gx-nav-link .gx-chevron {
   margin-left: auto;
   font-size: .75rem;
   transition: transform .2s ease;
}
.gx-nav-link[aria-expanded="true"] .gx-chevron { transform: rotate(90deg); }

.gx-nav-sub { padding-left: 2.1rem; }
.gx-nav-sub .gx-nav-link { padding: .375rem .75rem; font-weight: 450; font-size: .875rem; }

.gx-nav-badge { margin-left: auto; }

/* Módulo mapeado no menu mas ainda não entregue: visível como orientação,
   sem se comportar como link. */
.gx-nav-link--pendente {
   opacity: .42;
   cursor: default;
   user-select: none;
}
.gx-nav-link--pendente:hover { background: transparent; color: var(--gx-sidebar-text); }

/* Estado recolhido: mostra só ícones.
   Restrito ao desktop — no celular a sidebar vira gaveta e precisa dos rótulos.
   Sem esta media query, recolher no desktop e depois estreitar a janela deixaria
   o menu móvel só com ícones. */
@media (min-width: 992px) {
   /* Os rótulos encolhem até zero em vez de sumirem com display:none —
      display não é interpolável, e era isso que fazia o recolhimento
      acontecer de um quadro para o outro. O overflow corta o texto enquanto
      a largura corre. */
   .gx-sidebar__name,
   .gx-nav-link span {
      max-width: 14rem;
      overflow: hidden;
   }

   .gx-nav-titulo {
      max-height: 3rem;
      overflow: hidden;
   }

   .is-collapsed .gx-sidebar__name,
   .is-collapsed .gx-nav-link span {
      max-width: 0;
      opacity: 0;
   }

   /* O título de grupo ocupa uma linha inteira: some colapsando a altura,
      não a largura, senão restaria uma faixa vazia entre os grupos. */
   .is-collapsed .gx-nav-titulo {
      max-height: 0;
      opacity: 0;
      padding-top: 0;
      padding-bottom: 0;
   }

   /* Chevron e badge não têm meia-medida útil: saem de uma vez. */
   .is-collapsed .gx-chevron,
   .is-collapsed .gx-nav-badge,
   .is-collapsed .gx-nav-sub { display: none; }

   /* O gap precisa ir junto: com o rótulo em largura zero, sobraria um
      respiro à direita do ícone e ele não ficaria centralizado. */
   .is-collapsed .gx-nav-link {
      justify-content: center;
      padding: .625rem;
      gap: 0;
   }

   .is-collapsed .gx-sidebar__brand {
      justify-content: center;
      padding: 0;
      gap: 0;
   }

   /* Recolhida, sobram só os ícones. O rótulo vai para o atributo title,
      aplicado pelo JS — um balão em CSS seria cortado, porque a lista tem
      overflow-y: auto e o navegador força o outro eixo a recortar junto. */

   /* ------------------------------------------- Transição do recolhimento
      Tudo aqui depende de .is-animada, que o JS aplica depois do primeiro
      quadro. A classe is-collapsed é restaurada do localStorage durante o
      carregamento; sem essa trava, toda página aberta com a sidebar
      recolhida começaria com a barra deslizando de 264px até 76px. */
   .gx-app.is-animada {
      transition: grid-template-columns .26s cubic-bezier(.4, 0, .2, 1);
   }

   .is-animada .gx-nav-link,
   .is-animada .gx-sidebar__brand {
      transition: background-color .15s ease, color .15s ease,
                  padding .26s cubic-bezier(.4, 0, .2, 1),
                  gap .26s cubic-bezier(.4, 0, .2, 1);
   }

   /* O texto sai um pouco antes de a largura fechar: some enquanto ainda há
      espaço, em vez de ser espremido contra o ícone. */
   .is-animada .gx-sidebar__name,
   .is-animada .gx-nav-link span {
      transition: opacity .15s ease,
                  max-width .26s cubic-bezier(.4, 0, .2, 1);
   }

   .is-animada .gx-nav-titulo {
      transition: opacity .13s ease,
                  max-height .26s cubic-bezier(.4, 0, .2, 1),
                  padding .26s cubic-bezier(.4, 0, .2, 1);
   }
}

/* ------------------------------------------------------------------ Topbar */
.gx-main { display: flex; flex-direction: column; min-width: 0; }

.gx-topbar {
   position: sticky;
   top: 0;
   z-index: 1020;
   height: 62px;
   display: flex;
   align-items: center;
   gap: .75rem;
   padding: 0 1.25rem;
   background: var(--gx-topbar-bg);
   backdrop-filter: saturate(180%) blur(10px);
   border-bottom: 1px solid var(--bs-border-color);
}

.gx-busca {
   position: relative;
   max-width: 380px;
   width: 100%;
}
.gx-busca i {
   position: absolute;
   left: .75rem;
   top: 50%;
   transform: translateY(-50%);
   color: var(--bs-secondary-color);
   pointer-events: none;
}
.gx-busca input { padding-left: 2.25rem; border-radius: 2rem; }

.gx-icone-btn {
   width: 38px;
   height: 38px;
   border-radius: .5rem;
   display: grid;
   place-items: center;
   border: 0;
   background: transparent;
   color: var(--bs-body-color);
   font-size: 1.1rem;
   transition: background-color .15s ease;
}
.gx-icone-btn:hover { background: var(--gx-sidebar-hover); }

/* Nas telas sem menu (login, recuperação de senha) o alternador de tema
   fica flutuante no canto — mesma aparência do botão da topbar. */
.gx-tema-flutuante {
   position: fixed;
   right: 1rem;
   bottom: 1rem;
   background: var(--gx-card-bg);
   border: 1px solid var(--gx-card-border);
   box-shadow: var(--gx-shadow-sm);
   z-index: 1040;
}

.gx-conteudo { padding: 1.5rem 1.25rem 3rem; flex: 1; }

.gx-pagina-titulo {
   font-size: 1.4rem;
   font-weight: 650;
   letter-spacing: -.02em;
   margin: 0;
}

/* ------------------------------------------------------------------- Cards */
.card {
   background-color: var(--gx-card-bg);
   border-color: var(--gx-card-border);
   box-shadow: var(--gx-shadow-sm);
}

.card-header {
   background-color: transparent;
   border-bottom-color: var(--gx-card-border);
   padding: .875rem 1.125rem;
   font-weight: 600;
}

/* KPI */
.gx-kpi { position: relative; overflow: hidden; }

/* Cartão clicável: o realce é do cartão inteiro, não do texto — os valores
   mantêm a mesma cor dos cartões sem link, para a linha ficar homogênea. */
.gx-kpi--link { transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease; }
.gx-kpi--link:hover {
   transform: translateY(-2px);
   box-shadow: var(--gx-shadow);
   border-color: var(--gx-primary);
}
.gx-kpi--link:hover .gx-kpi__valor { color: var(--gx-primary); }
.gx-kpi__valor {
   font-size: 1.75rem;
   font-weight: 700;
   letter-spacing: -.03em;
   line-height: 1.1;
}
.gx-kpi__rotulo {
   font-size: .8125rem;
   color: var(--bs-secondary-color);
   font-weight: 500;
}
.gx-kpi__icone {
   width: 46px;
   height: 46px;
   border-radius: .75rem;
   display: grid;
   place-items: center;
   font-size: 1.35rem;
   flex-shrink: 0;
}

/* --------------------------------------------------------------- Tabelas */
.table { --bs-table-bg: transparent; }

.table > :not(caption) > * > * { padding: .6875rem .875rem; }

.table thead th {
   font-size: .75rem;
   font-weight: 600;
   text-transform: uppercase;
   letter-spacing: .04em;
   color: var(--bs-secondary-color);
   border-bottom-width: 1px;
   white-space: nowrap;
}

.table-hover > tbody > tr:hover > * { --bs-table-accent-bg: var(--gx-sidebar-hover); }

.gx-tabela-acoes { white-space: nowrap; text-align: right; }

/* Wrapper com rolagem horizontal — nunca deixa a página rolar lateralmente */
.gx-tabela-scroll { overflow-x: auto; }

/* --------------------------------------------------------------- Formulário */
.form-label { font-weight: 550; font-size: .8125rem; margin-bottom: .3125rem; }

.form-control, .form-select {
   background-color: var(--gx-card-bg);
   border-color: var(--bs-border-color);
}

.form-control:focus, .form-select:focus {
   border-color: var(--gx-primary);
   box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .15);
}

.form-obrigatorio::after { content: ' *'; color: var(--bs-danger); }

/* ------------------------------------------------------------ Campo de senha
   Os botões do campo — revelar a senha, sortear uma nova — ficam dentro do
   próprio input, sobrepostos à direita. Antes era um .input-group: o botão
   virava uma segunda caixa colada ao lado, com borda própria, e o conjunto
   parecia dois controles em vez de um.

   --gx-senha-acoes é quantos botões o campo tem. O padding à direita reserva
   o espaço deles, para que a senha digitada nunca corra por baixo. Um campo
   com dois botões só precisa declarar o número, sem recalcular posição. */
.gx-senha {
   position: relative;
   --gx-senha-acoes: 1;
   --gx-senha-largura: 2.75rem;
}

.gx-senha .form-control {
   padding-right: calc(var(--gx-senha-acoes) * var(--gx-senha-largura));
}

.gx-senha--duplo { --gx-senha-acoes: 2; }

/* inset com left auto: cola no topo, na base e à direita, e a largura vem
   dos próprios botões. */
.gx-senha__acoes {
   position: absolute;
   inset: 0 0 0 auto;
   display: flex;
}

.gx-senha__botao {
   width: var(--gx-senha-largura);
   display: grid;
   place-items: center;
   padding: 0;
   border: 0;
   background: transparent;
   color: var(--bs-secondary-color);
   cursor: pointer;
   transition: color .15s ease;
}

.gx-senha__botao:last-child {
   border-radius: 0 var(--bs-border-radius) var(--bs-border-radius) 0;
}

.gx-senha__botao:hover { color: var(--bs-emphasis-color); }

/* O foco desenha para dentro: um outline externo passaria por cima da borda
   do input e brigaria com o anel de foco do próprio campo. */
.gx-senha__botao:focus-visible {
   outline: 2px solid var(--gx-primary);
   outline-offset: -3px;
   color: var(--gx-primary);
}

/* ------------------------------------------------------------------ Select2
   O tema bootstrap-5 do Select2 traz as cores fixas do Bootstrap padrão, então
   não acompanha o modo escuro nem a paleta daqui. As regras abaixo repetem a
   especificidade do tema (sem !important) e trocam cada cor por um token — o
   campo passa a ser indistinguível de um .form-select em qualquer tema. */

.select2-container--bootstrap-5 .select2-selection {
   min-height: calc(1.5em + .75rem + 2px);
   color: var(--bs-body-color);
   background-color: var(--gx-card-bg);
   border-color: var(--bs-border-color);
   border-radius: var(--bs-border-radius);
}

.select2-container--bootstrap-5.select2-container--focus .select2-selection,
.select2-container--bootstrap-5.select2-container--open .select2-selection {
   border-color: var(--gx-primary);
   box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .15);
}

.select2-container--bootstrap-5 .select2-selection--single .select2-selection__rendered {
   color: var(--bs-body-color);
}

.select2-container--bootstrap-5 .select2-selection--single .select2-selection__rendered .select2-selection__placeholder {
   color: var(--bs-secondary-color);
}

/* Dropdown */
.select2-container--bootstrap-5 .select2-dropdown {
   color: var(--bs-body-color);
   background-color: var(--gx-card-bg);
   border-color: var(--gx-primary);
   border-radius: var(--bs-border-radius);
   box-shadow: var(--gx-shadow-lg);
}

.select2-container--bootstrap-5 .select2-dropdown .select2-search .select2-search__field {
   color: var(--bs-body-color);
   background-color: var(--gx-card-bg);
   border-color: var(--bs-border-color);
   border-radius: var(--bs-border-radius-sm);
}

.select2-container--bootstrap-5 .select2-dropdown .select2-search .select2-search__field:focus {
   border-color: var(--gx-primary);
   box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .15);
}

.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option--highlighted {
   color: var(--bs-body-color);
   background-color: var(--gx-sidebar-hover);
}

.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option.select2-results__option--selected,
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option[aria-selected=true]:not(.select2-results__option--highlighted) {
   color: #fff;
   background-color: var(--gx-primary);
}

.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option.select2-results__message,
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option[aria-disabled=true] {
   color: var(--bs-secondary-color);
}

/* Múltipla escolha e estado desabilitado */
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered .select2-selection__choice {
   color: var(--gx-primary);
   background-color: var(--gx-primary-soft);
   border-color: transparent;
   border-radius: var(--bs-border-radius-sm);
}

.select2-container--bootstrap-5.select2-container--disabled .select2-selection,
.select2-container--bootstrap-5.select2-container--disabled.select2-container--focus .select2-selection {
   color: var(--bs-secondary-color);
   background-color: var(--bs-secondary-bg);
   border-color: var(--bs-border-color);
}

/* O "x" de limpar é um SVG escuro embutido no tema; no escuro ele some */
[data-bs-theme="dark"] .select2-container--bootstrap-5 .select2-selection--single .select2-selection__clear,
[data-bs-theme="dark"] .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__clear {
   filter: invert(1);
}

.btn-primary {
   --bs-btn-bg: var(--gx-primary);
   --bs-btn-border-color: var(--gx-primary);
   --bs-btn-hover-bg: var(--gx-primary-hover);
   --bs-btn-hover-border-color: var(--gx-primary-hover);
   --bs-btn-active-bg: var(--gx-primary-hover);
   --bs-btn-active-border-color: var(--gx-primary-hover);
   --bs-btn-disabled-bg: var(--gx-primary);
   --bs-btn-disabled-border-color: var(--gx-primary);
}

/* ------------------------------------------------------------ SweetAlert2
   Mesmo caso do Select2 acima: o CSS do SweetAlert2 traz cores fixas e só
   troca para o escuro via @media (prefers-color-scheme), enquanto o tema
   daqui é escolhido no botão da topbar e vive em data-bs-theme. Quem usa o
   tema escuro num sistema em modo claro receberia um diálogo branco sobre a
   página escura. Usar os tokens do Bootstrap, que já acompanham
   data-bs-theme, resolve os dois temas sem media query. */

.swal2-popup {
   background: var(--gx-card-bg);
   color: var(--bs-body-color);
   border: 1px solid var(--gx-card-border);
   border-radius: 1rem;
   box-shadow: var(--gx-shadow-lg);
}

.swal2-title,
.swal2-html-container { color: var(--bs-body-color); }

/* O ícone de aviso do SweetAlert2 tem cor própria e continua legível nos dois
   temas; só a borda precisa acompanhar a superfície. */
.swal2-icon { border-color: var(--gx-card-border); }

.swal2-actions { gap: .5rem; }

/* ------------------------------------------------------------------ Avatar */
.avatar-iniciais {
   background: var(--gx-primary-soft);
   color: var(--gx-primary);
   line-height: 1;
   user-select: none;
}

/* ------------------------------------------------------------- Estado vazio */
.gx-vazio {
   text-align: center;
   padding: 3rem 1rem;
   color: var(--bs-secondary-color);
}
.gx-vazio i { font-size: 2.75rem; opacity: .35; display: block; margin-bottom: .75rem; }

/* ------------------------------------------------------- Portal do Aluno */
/* Navegação horizontal: o aluno tem poucos destinos e não precisa da
   sidebar densa da área administrativa. */
.gx-portal-nav {
   background: var(--gx-card-bg);
   border-bottom: 1px solid var(--bs-border-color);
   position: sticky;
   top: 62px;
   z-index: 1010;
}

.gx-portal-link {
   display: flex;
   align-items: center;
   gap: .5rem;
   padding: .75rem 1rem;
   color: var(--bs-secondary-color);
   text-decoration: none;
   font-weight: 500;
   font-size: .9375rem;
   white-space: nowrap;
   border-bottom: 2px solid transparent;
   transition: color .15s ease, border-color .15s ease;
}

.gx-portal-link:hover { color: var(--bs-emphasis-color); }

.gx-portal-link.active {
   color: var(--gx-primary);
   border-bottom-color: var(--gx-primary);
}

@media (max-width: 575.98px) {
   .gx-portal-link span { display: none; }
   .gx-portal-link { padding: .75rem 1.125rem; font-size: 1.1rem; }
}

/* -------------------------------------------------------------------- Login */
.gx-login {
   min-height: 100vh;
   display: grid;
   place-items: center;
   padding: 1.5rem;
   background:
      radial-gradient(1000px 500px at 10% -10%, rgba(79, 70, 229, .16), transparent 60%),
      radial-gradient(900px 500px at 110% 110%, rgba(124, 58, 237, .14), transparent 55%),
      var(--gx-body-bg);
}

.gx-login__card {
   width: 100%;
   max-width: 420px;
   background: var(--gx-card-bg);
   border: 1px solid var(--gx-card-border);
   border-radius: 1rem;
   box-shadow: var(--gx-shadow-lg);
   padding: 2rem;
}

/* -------------------------------------------------------------- Responsivo */
@media (max-width: 991.98px) {
   .gx-app { grid-template-columns: minmax(0, 1fr); }

   .gx-sidebar {
      position: fixed;
      inset: 0 auto 0 0;
      width: 264px;
      transform: translateX(-100%);
      /* Mesma curva do recolhimento no desktop, para os dois gestos terem o
         mesmo peso. A sombra entra junto: sem ela, a gaveta parece colada
         na página no meio do caminho. */
      transition: transform .26s cubic-bezier(.4, 0, .2, 1),
                  box-shadow .26s ease;
      box-shadow: none;
   }

   .gx-app.is-aberto .gx-sidebar {
      transform: translateX(0);
      box-shadow: var(--gx-shadow-lg);
   }

   .gx-app.is-aberto .gx-backdrop { opacity: 1; pointer-events: auto; }

   .gx-conteudo { padding: 1.125rem .875rem 2.5rem; }
   .gx-busca { max-width: none; }
}

@media (max-width: 575.98px) {
   .gx-pagina-titulo { font-size: 1.2rem; }
   .gx-kpi__valor { font-size: 1.4rem; }
}

/* ----------------------------------------------------- Movimento reduzido
   Quem pede menos animação no sistema operacional continua tendo os mesmos
   estados — a sidebar recolhe e a gaveta abre — só que sem o percurso. */
@media (prefers-reduced-motion: reduce) {
   .gx-app,
   .gx-sidebar,
   .gx-backdrop,
   .gx-sidebar__brand,
   .gx-sidebar__name,
   .gx-nav-titulo,
   .gx-nav-link,
   .gx-nav-link span,
   .gx-chevron {
      transition-duration: .01ms !important;
   }
}

/* ------------------------------------------------------------- Impressão */
@media print {
   .gx-sidebar, .gx-topbar, .gx-nao-imprimir, .btn { display: none !important; }
   .gx-app { grid-template-columns: minmax(0, 1fr); }
   .gx-conteudo { padding: 0; }
   .card { border: 0; box-shadow: none; }
}
