/* /Components/Layout/HomepageLayout.razor.rz.scp.css */
.hp-header[b-2foscemala]{
    position:sticky; top:0; z-index:10;
    background:var(--bg); border-bottom:1px solid var(--line);
}
.hp-header-inner[b-2foscemala]{
    max-width:1100px; margin:0 auto; padding:var(--sp-4) var(--sp-5);
    display:flex; align-items:center; justify-content:space-between;
}
.hp-brand[b-2foscemala]{
    display:flex; align-items:center; gap:var(--sp-2);
    font-family:var(--font-display); font-weight:600; font-size:1.1rem; color:var(--ink);
}
.hp-brand:hover[b-2foscemala]{text-decoration:none;}

main[b-2foscemala]{min-height:70vh;}

.hp-footer[b-2foscemala]{
    border-top:1px solid var(--line); padding:var(--sp-6) var(--sp-5);
    max-width:1100px; margin:0 auto;
    display:flex; justify-content:space-between; flex-wrap:wrap; gap:var(--sp-3);
    font-family:var(--font-mono); font-size:0.78rem; color:var(--ink-3);
}
/* /Components/Layout/MainLayout.razor.rz.scp.css */
.db-shell[b-vyqtfqyq41]{height:100dvh; display:flex; flex-direction:column;}

/* Só nas páginas com rodapé (todas exceto o Dashboard, ver MainLayout.razor `EstaNoDashboard`).
   O Dashboard PRECISA de `.db-shell` com altura fixa (100dvh) e `.db-main` encolhido ao espaço
   restante (`flex:1; min-height:0`, ver mais abaixo) — é essa fixação que dá ao scroll automático
   contínuo dos cartões (`.dash-scroll`, Dashboard.razor.css) uma altura de referência para
   calcular "o que sobra" depois do cabeçalho. Nas outras páginas, essa MESMA fixação é que causava
   o bug: com um `.db-footer` a seguir a `.db-main` dentro do mesmo `.db-shell` de 100dvh, o
   `.db-main` continuava a ser encolhido ao espaço sobrante (ex. 634px), mas o seu conteúdo real
   (ex. uma tabela mais alta do que isso) continuava a ultrapassar essa caixa encolhida — como
   overflow:visible não empurra elementos irmãos, o rodapé ficava colocado logo a seguir à caixa
   encolhida de `.db-main`, e o conteúdo a mais ficava a sobrepor-se visualmente ao rodapé em vez
   de o empurrar para baixo. `height:auto`+`min-height:100dvh` (cresce com o conteúdo, só garante o
   mínimo de um ecrã) em vez de `height:100dvh` (fixo) resolve isto: sem altura fixa a que
   `.db-main` tenha de se ajustar, deixa de haver necessidade de o encolher, o conteúdo real define
   a altura da página e o rodapé fica sempre a seguir a ele, nunca por baixo. */
.db-shell-com-rodape[b-vyqtfqyq41]{height:auto; min-height:100dvh;}
.db-shell-com-rodape .db-main[b-vyqtfqyq41]{flex:1 0 auto; min-height:auto;}

/* Mobile/tablet (revisão 2026-10-02): o Dashboard também deixa de ter altura
   fixa — em ecrã estreito os KPIs empilhados + mapa ocupavam o 100dvh todo e a lista de clientes
   (.dash-scroll) ficava com 0px de altura, invisível. Aqui a página cresce com o conteúdo e faz
   scroll normal. O scroll automático contínuo (só faz sentido num ecrã largo/"wallboard") fica
   inativo — ver Dashboard.razor.css. */
@media (max-width:900px){
    .db-shell:not(.db-shell-com-rodape)[b-vyqtfqyq41]{height:auto; min-height:100dvh;}
    .db-shell:not(.db-shell-com-rodape) .db-main[b-vyqtfqyq41]{flex:1 0 auto; min-height:auto;}
}

.db-header[b-vyqtfqyq41]{
    flex:0 0 auto;
    background:var(--bg); border-bottom:1px solid var(--line);
}
/* Sem max-width/margin:auto (ao contrário de .db-main, ver abaixo) — o cabeçalho ocupa o ecrã
   todo em largura de propósito, para o logótipo/hamburger alinharem com a margem esquerda real
   do ecrã (mesmo `padding` que .dash-layout usa para o mapa, ver Dashboard.razor.css) e o
   nome/"Sair" alinharem com a margem direita real, não com um contentor centrado a 1200px. */
/* 3 filhos flex diretos (brand/user/menu, já não .db-menu aninhado dentro de .db-left) — permite
   reordenar com `order` por breakpoint sem duplicar HTML: em ecrã largo replica exatamente o
   agrupamento antigo (brand+menu à esquerda, user à direita); em mobile (ver media query abaixo)
   só troca `order`/`display`, nunca a estrutura. */
.db-header-inner[b-vyqtfqyq41]{
    padding:var(--sp-4) var(--sp-5);
    display:flex; align-items:center; gap:var(--sp-4);
}
.db-left[b-vyqtfqyq41]{display:flex; align-items:center; gap:var(--sp-4); order:1;}
.db-menu[b-vyqtfqyq41]{order:2;}
.db-user[b-vyqtfqyq41]{order:3; margin-left:auto;}

.db-brand[b-vyqtfqyq41]{
    display:flex; align-items:center; gap:var(--sp-2);
    font-family:var(--font-display); font-weight:600; font-size:1.05rem; color:var(--ink);
}
.db-brand:hover[b-vyqtfqyq41]{text-decoration:none;}

.db-menu[b-vyqtfqyq41]{position:relative;}

/* Cobre a página toda (invisível) enquanto o menu está aberto — um clique em qualquer sítio fora
   do painel fecha-o, sem precisar de nenhum JS interop (a própria hit-testing do browser resolve
   isto: o painel tem z-index maior, por isso um clique dentro dele nunca chega a este backdrop).
   */
.db-menu-backdrop[b-vyqtfqyq41]{position:fixed; inset:0; z-index:19;}
.db-menu-btn[b-vyqtfqyq41]{
    display:flex; align-items:center; justify-content:center;
    width:32px; height:32px; padding:0; background:none; border:none;
    color:var(--ink-2); cursor:pointer;
}
.db-menu-btn:hover[b-vyqtfqyq41]{background:var(--panel); color:var(--ink);}
.db-menu-btn svg[b-vyqtfqyq41]{width:18px; height:18px;}

.db-menu-panel[b-vyqtfqyq41]{
    position:absolute; top:calc(100% + var(--sp-2)); left:0; z-index:20;
    display:flex; flex-direction:column; min-width:180px;
    background:var(--bg); border:1px solid var(--line); box-shadow:0 4px 16px rgba(11,35,56,0.12);
}
.db-menu-panel a[b-vyqtfqyq41]{
    padding:var(--sp-3) var(--sp-4); color:var(--ink); font-size:0.9rem; text-decoration:none;
}
.db-menu-panel a:hover[b-vyqtfqyq41]{background:var(--panel);}

.db-menu-grupo-titulo[b-vyqtfqyq41]{
    padding:var(--sp-3) var(--sp-4) var(--sp-1);
    font-family:var(--font-mono); font-weight:500; font-size:0.72rem; color:var(--ink-3);
    text-transform:uppercase; letter-spacing:0.02em;
}
.db-menu-panel a.db-menu-sub[b-vyqtfqyq41]{padding-left:var(--sp-6); padding-top:var(--sp-2); padding-bottom:var(--sp-2);}

.db-user[b-vyqtfqyq41]{display:flex; align-items:center; gap:var(--sp-4);}
.db-user-nome[b-vyqtfqyq41]{
    font-size:0.9rem; color:var(--ink-2); font-family:var(--font-body);
    background:none; border:none; padding:0; cursor:pointer;
}
.db-user-nome:hover[b-vyqtfqyq41]{color:var(--ink); text-decoration:underline;}

/* Nome + "Sair" dentro do painel do hamburger — só visível em mobile (ver media query abaixo), é
   a mesma informação de .db-user, duplicada aqui porque .db-user fica escondida do cabeçalho
   nesse breakpoint (pedido explícito do utilizador, 2026-09-30). */
.db-menu-conta[b-vyqtfqyq41]{
    display:none;
    align-items:center; justify-content:space-between; gap:var(--sp-3);
    padding:var(--sp-3) var(--sp-4); margin-top:var(--sp-2); border-top:1px solid var(--line);
}
.db-menu-conta-nome[b-vyqtfqyq41]{
    font-size:0.9rem; color:var(--ink-2); font-family:var(--font-body);
    background:none; border:none; padding:0; cursor:pointer; text-align:left;
}
.db-menu-conta-nome:hover[b-vyqtfqyq41]{color:var(--ink); text-decoration:underline;}

/* Mobile: só o logo (sem o nome "AquaStatus") à esquerda e o hamburger à direita — nome do
   utilizador e "Sair" saem do cabeçalho e passam a viver dentro do painel do hamburger
   (.db-menu-conta acima). */
@media (max-width:480px){
    .db-brand span[b-vyqtfqyq41]{display:none;}
    .db-user[b-vyqtfqyq41]{display:none;}
    .db-menu[b-vyqtfqyq41]{order:3; margin-left:auto;}
    .db-menu-conta[b-vyqtfqyq41]{display:flex;}

    /* Painel a abrir a toda a largura do ecrã (pedido explícito), não só um dropdown estreito
       ancorado ao hamburger — `position:fixed` (não `absolute` relativo a `.db-menu`, um botão de
       32px) com `left:0;right:0` ignora de propósito a posição/largura do botão. `top:64px` é a
       altura real do cabeçalho em mobile (padding `var(--sp-4)` cima+baixo = 32px + 32px do
       hamburger/logo) — não há forma de exprimir isto sem um valor fixo, já que `position:fixed`
       não aceita percentagens relativas a um ascendente. */
    .db-menu-panel[b-vyqtfqyq41]{
        position:fixed; left:0; right:0; top:64px;
        width:auto; min-width:0; max-height:calc(100dvh - 64px); overflow-y:auto;
    }
}

.db-main[b-vyqtfqyq41]{
    flex:1; min-height:0; max-width:1200px; width:100%; margin:0 auto; padding:var(--sp-6) var(--sp-5);
    display:flex; flex-direction:column;
}

/* Ausente no Dashboard (ver MainLayout.razor, EstaNoDashboard) — o resto das páginas usa `.db-main`
   sem `flex:1`, por isso o rodapé fica logo a seguir ao conteúdo (não fixo ao fundo do ecrã), tal
   como o `.hp-footer` da página pública (HomepageLayout.razor.css) que serviu de referência. */
.db-footer[b-vyqtfqyq41]{
    flex:0 0 auto;
    border-top:1px solid var(--line);
}
.db-footer-inner[b-vyqtfqyq41]{
    max-width:1200px; margin:0 auto; padding:var(--sp-5);
    display:flex; justify-content:space-between; flex-wrap:wrap; gap:var(--sp-3);
    font-family:var(--font-mono); font-size:0.78rem; color:var(--ink-3);
}
/* /Components/Layout/ReconnectModal.razor.rz.scp.css */
.components-reconnect-first-attempt-visible[b-mn7esn2tk3],
.components-reconnect-repeated-attempt-visible[b-mn7esn2tk3],
.components-reconnect-failed-visible[b-mn7esn2tk3],
.components-pause-visible[b-mn7esn2tk3],
.components-resume-failed-visible[b-mn7esn2tk3],
.components-rejoining-animation[b-mn7esn2tk3] {
    display: none;
}

#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible[b-mn7esn2tk3],
#components-reconnect-modal.components-reconnect-show .components-rejoining-animation[b-mn7esn2tk3],
#components-reconnect-modal.components-reconnect-paused .components-pause-visible[b-mn7esn2tk3],
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible[b-mn7esn2tk3],
#components-reconnect-modal.components-reconnect-retrying[b-mn7esn2tk3],
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible[b-mn7esn2tk3],
#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation[b-mn7esn2tk3],
#components-reconnect-modal.components-reconnect-failed[b-mn7esn2tk3],
#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible[b-mn7esn2tk3] {
    display: block;
}


#components-reconnect-modal[b-mn7esn2tk3] {
    background-color: white;
    width: 20rem;
    margin: 20vh auto;
    padding: 2rem;
    border: 0;
    border-radius: 0.5rem;
    box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.3);
    opacity: 0;
    transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
    animation: components-reconnect-modal-fadeOutOpacity-b-mn7esn2tk3 0.5s both;
    &[open]

{
    animation: components-reconnect-modal-slideUp-b-mn7esn2tk3 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity-b-mn7esn2tk3 0.5s ease-in-out 0.3s;
    animation-fill-mode: both;
}

}

#components-reconnect-modal[b-mn7esn2tk3]::backdrop {
    background-color: rgba(0, 0, 0, 0.4);
    animation: components-reconnect-modal-fadeInOpacity-b-mn7esn2tk3 0.5s ease-in-out;
    opacity: 1;
}

@keyframes components-reconnect-modal-slideUp-b-mn7esn2tk3 {
    0% {
        transform: translateY(30px) scale(0.95);
    }

    100% {
        transform: translateY(0);
    }
}

@keyframes components-reconnect-modal-fadeInOpacity-b-mn7esn2tk3 {
    0% {
        opacity: 0;
    }

    100% {
        opacity: 1;
    }
}

@keyframes components-reconnect-modal-fadeOutOpacity-b-mn7esn2tk3 {
    0% {
        opacity: 1;
    }

    100% {
        opacity: 0;
    }
}

.components-reconnect-container[b-mn7esn2tk3] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
}

#components-reconnect-modal p[b-mn7esn2tk3] {
    margin: 0;
    text-align: center;
}

#components-reconnect-modal button[b-mn7esn2tk3] {
    border: 0;
    background-color: #6b9ed2;
    color: white;
    padding: 4px 24px;
    border-radius: 4px;
}

    #components-reconnect-modal button:hover[b-mn7esn2tk3] {
        background-color: #3b6ea2;
    }

    #components-reconnect-modal button:active[b-mn7esn2tk3] {
        background-color: #6b9ed2;
    }

.components-rejoining-animation[b-mn7esn2tk3] {
    position: relative;
    width: 80px;
    height: 80px;
}

    .components-rejoining-animation div[b-mn7esn2tk3] {
        position: absolute;
        border: 3px solid #0087ff;
        opacity: 1;
        border-radius: 50%;
        animation: components-rejoining-animation-b-mn7esn2tk3 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
    }

        .components-rejoining-animation div:nth-child(2)[b-mn7esn2tk3] {
            animation-delay: -0.5s;
        }

@keyframes components-rejoining-animation-b-mn7esn2tk3 {
    0% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    4.9% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    5% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 1;
    }

    100% {
        top: 0px;
        left: 0px;
        width: 80px;
        height: 80px;
        opacity: 0;
    }
}
/* /Components/Pages/ClienteDetalhe.razor.rz.scp.css */
/* Nome do cliente + badge "Ativo"/"Inativo" — sem isto ficavam colados (o badge é um
   inline-flex sem margem própria), o mesmo padrão já usado em
   ServidorCard.razor.css (.servidor-titulo-linha). */
.cliente-detalhe-titulo[b-n9z3gy65ce]{
    display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap;
}

/* "Atualizar dados" em curso: o cartão fica esbatido (e sem cliques) com um spinner centrado por
   cima. */
.cliente-detalhe-cartao[b-n9z3gy65ce]{position:relative;}
.cliente-detalhe-cartao.a-atualizar .cliente-detalhe-cartao-conteudo[b-n9z3gy65ce]{opacity:0.45; pointer-events:none;}
.cliente-detalhe-cartao-spinner[b-n9z3gy65ce]{
    position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
    pointer-events:none;
}

/* Mobile: "Utilizadores associados" + botão "Gerir utilizadores" não cabem lado a lado sem
   espremer o botão (o global .page-header não tem flex-wrap, pensado para títulos curtos) — o
   botão passa para a sua própria linha em vez de partir o texto a meio. */
@media (max-width:480px){
    .cliente-detalhe-utilizadores-header[b-n9z3gy65ce]{flex-wrap:wrap;}

    /* Mesma nota de Clientes.razor.css — o `table{min-width:560px}` global (app.css) força
       scroll lateral mesmo numa tabela de 1 coluna só. */
    .tbl-wrap table[b-n9z3gy65ce]{min-width:0;}
}
/* /Components/Pages/Clientes.razor.rz.scp.css */
.clientes-filtros[b-allg17uik8]{
    display:flex; gap:var(--sp-4); align-items:flex-end; margin-bottom:var(--sp-5); flex-wrap:wrap;
}

.clientes-filtros .field select[b-allg17uik8]{
    min-width:220px;
}

.th-sortable[b-allg17uik8]{
    cursor:pointer; user-select:none; white-space:nowrap;
}
.th-sortable:hover[b-allg17uik8]{color:var(--ink);}

/* Linha inteira clicável como ligação real: o <a class="linha-link"> do nome estica-se por toda a
   linha (::after absoluto sobre a <tr>), por isso o botão direito, Ctrl+clique e clique do meio
   funcionam em qualquer ponto da linha. Os botões da linha ficam acima da ligação esticada
   (.linha-acoes, z-index maior) e continuam a funcionar por si. */
.linha-clicavel[b-allg17uik8]{position:relative; cursor:pointer;}
.linha-clicavel:hover[b-allg17uik8]{background:var(--panel);}
.linha-link[b-allg17uik8]{color:inherit; text-decoration:none;}
.linha-link[b-allg17uik8]::after{content:""; position:absolute; inset:0;}
.linha-link:focus-visible[b-allg17uik8]{outline:2px solid var(--signal); outline-offset:2px;}
.linha-acoes[b-allg17uik8]{display:flex; gap:var(--sp-2); position:relative; z-index:1;}

.clientes-paginacao[b-allg17uik8]{
    display:flex; align-items:center; justify-content:space-between; gap:var(--sp-4);
    margin-top:var(--sp-4); flex-wrap:wrap;
}
.clientes-paginacao-info[b-allg17uik8]{font-size:0.85rem; color:var(--ink-2);}

/* Mobile: só Nome/Estado/Ações — as restantes colunas (Tipo de Plataforma, Acesso configurado,
   Alarmística, Dashboard) ficam escondidas, para a tabela caber sem precisar de scroll lateral
   (`.tbl-wrap` continua com overflow-x:auto como rede de segurança, mas deixa de ser necessário
   na prática). Os filtros passam a ocupar metade do ecrã cada um. */
@media (max-width:480px){
    .col-extra[b-allg17uik8]{display:none;}

    /* Nome sozinho a 100% (é um campo de texto, beneficia do espaço) numa 1ª linha; Tipo de
       plataforma + Situação a 50% cada numa 2ª linha (`:first-child`/`:not(:first-child)` — a
       ordem no HTML nunca muda, mais simples do que dar uma classe a cada campo). */
    .clientes-filtros[b-allg17uik8]{gap:var(--sp-3); flex-wrap:wrap;}
    .clientes-filtros .field[b-allg17uik8]{min-width:0;}
    .clientes-filtros .field:first-child[b-allg17uik8]{flex:1 1 100%;}
    .clientes-filtros .field:not(:first-child)[b-allg17uik8]{flex:1 1 calc(50% - var(--sp-3) / 2);}
    .clientes-filtros .field select[b-allg17uik8]{min-width:0;}

    /* `table{min-width:560px}` é global (app.css) — com só 3 colunas a tabela já cabe bem abaixo
       disso; sem este reset, o `min-width` global forçava scroll lateral mesmo depois de esconder
       as colunas extra. */
    .tbl-wrap table[b-allg17uik8]{min-width:0;}

    /* Padding das células (global, pensado para a tabela completa) sobrava espaço a mais para só
       3 colunas caberem em 375px — reduzido só aqui, nunca globalmente (as outras tabelas do site
       continuam a precisar do padding original). */
    .tbl-wrap th[b-allg17uik8], .tbl-wrap td[b-allg17uik8]{padding-left:var(--sp-2); padding-right:var(--sp-2);}
}
/* /Components/Pages/Dashboard.razor.rz.scp.css */
/* Quebra o max-width:1200px do .db-main (ver MainLayout.razor.css) — o dashboard ocupa o ecrã
   todo em largura, mapa (1/4) + conteúdo (3/4), não o contentor centrado das outras páginas. */
.dash-layout[b-i1rfrre5t5]{
    flex:1; min-height:0; display:flex; gap:var(--sp-5);
    width:100vw; margin-left:calc(50% - 50vw);
    padding:0 var(--sp-5) var(--sp-6);
}

/* position+z-index (mesmo `z-index:0`, sem subir nada) cria um novo contexto de empilhamento —
   as camadas internas do Leaflet (`.leaflet-pane`, z-index até 700 — bem acima do `.db-menu-panel`
   do cabeçalho, z-index:20, ver MainLayout.razor.css) ficam contidas dentro deste elemento e nunca
   competem em z-index com o resto da página. Sem isto, `.db-header`/`.db-main` não criam contexto
   de empilhamento próprio (só flex, sem position/z-index/transform), por isso o z-index alto do
   Leaflet e o baixo do menu eram comparados diretamente na mesma lista global — o mapa ganhava e
   tapava o dropdown do menu aberto por cima dele. */
.dash-map-col[b-i1rfrre5t5]{
    position:relative; z-index:0;
    flex:0 0 25%; min-width:260px;
    border:1px solid var(--line); background:var(--panel);
    overflow:hidden; /* o Leaflet gere o seu próprio conteúdo interno (tiles/pan/zoom) */
}
/* "Mar" — o fundo do próprio mapa, atrás do contorno vetorial de Portugal (ver mapa.js).
   Sem tiles rasterizadas de nenhum fornecedor externo — só 3 tons fixos do brandbook, pedido
   explícito do utilizador: terreno = `--bg` (aplicado ao polígono do país em JS), mar = `--panel-
   strong` (aqui, "um pouco mais escuro" que o terreno), linha de costa/fronteira = `--line-
   strong` (mais escura ainda, aplicada ao traço do polígono em JS). As 3 já são tokens existentes
   do brandbook — nenhuma cor nova inventada para isto. */
.dash-mapa[b-i1rfrre5t5]{ width:100%; height:100%; background:var(--panel-strong); }

/* Retoques de marca no controlo de zoom do Leaflet — sem isto, vem com o branco/preto puro por
   omissão, inconsistente com os tokens do resto da app. `::deep` — o Leaflet injeta este
   elemento diretamente no DOM via JS, nunca passa pelo Razor, por isso nunca teria o atributo de
   scope do CSS isolation; `::deep` para de aplicar esse atributo à parte do seletor depois dele,
   deixando-a corresponder por classe como CSS global normal (`.dash-mapa` continua scoped). */
.dash-mapa[b-i1rfrre5t5]  .leaflet-control-zoom a{
    color:var(--ink-2); border-color:var(--line-strong) !important; background:var(--bg) !important;
}
.dash-mapa[b-i1rfrre5t5]  .leaflet-control-zoom a:hover{ background:var(--panel) !important; color:var(--ink); }

.dash-content-col[b-i1rfrre5t5]{
    flex:1; min-width:0; min-height:0;
    display:flex; flex-direction:column;
}

.dash-kpis[b-i1rfrre5t5]{
    flex:0 0 auto;
    display:grid; grid-template-columns:repeat(6,1fr); gap:var(--sp-4);
}
/* 6 cartões só cabem numa linha (sem cortar "Desconhecido"/"Clientes ativos") em ecrãs largos. */
@media (max-width:1599px){.dash-kpis[b-i1rfrre5t5]{grid-template-columns:repeat(3,1fr);}}
@media (max-width:900px){.dash-kpis[b-i1rfrre5t5]{grid-template-columns:repeat(2,1fr);}}
@media (max-width:480px){.dash-kpis[b-i1rfrre5t5]{grid-template-columns:1fr;}}

/* .kpi vem do app.css global (já com position:relative). "Clientes ativos" (só para admin) é
   clicável via uma sobreposição transparente em vez de a raiz do cartão ser um <a> — mesmo padrão
   de `.cliente-card-link` em ClienteCard.razor.css, para o botão de ajuda (irmão, não filho do
   link) nunca ficar aninhado dentro de um elemento interativo. */
.kpi-overlay-link[b-i1rfrre5t5]{position:absolute; inset:0; z-index:1;}
.kpi:has(.kpi-overlay-link):hover[b-i1rfrre5t5]{border-color:var(--ink-3);}

/* Botão de ajuda "?" (canto superior direito de cada cartão de KPI) + popover. z-index bem acima de 1 (não só "1 a mais" que `.kpi-overlay-link`/
   `.cliente-card-link`, ambos a 1) — o empate em z-index resolve-se por ordem no DOM, e os
   cartões de cliente vêm depois no documento, por isso ganhavam ao backdrop com valores
   próximos, tornando "clicar fora fecha" pouco fiável nessa zona da página. */
.kpi-help[b-i1rfrre5t5]{
    position:absolute; top:var(--sp-3); right:var(--sp-3); z-index:11;
    width:20px; height:20px; padding:0; border-radius:50%;
    border:1px solid var(--line-strong); background:var(--bg); color:var(--ink-3);
    font-family:var(--font-mono); font-size:0.72rem; line-height:1;
    display:flex; align-items:center; justify-content:center; cursor:pointer;
}
.kpi-help:hover[b-i1rfrre5t5]{color:var(--ink); border-color:var(--ink-3);}

.kpi-help-popover[b-i1rfrre5t5]{
    position:absolute; top:calc(var(--sp-3) + 24px); right:var(--sp-3); z-index:12;
    width:220px; max-width:calc(100vw - var(--sp-6));
    background:var(--bg); border:1px solid var(--line-strong);
    box-shadow:0 4px 16px rgba(11,35,56,0.12);
    padding:var(--sp-3) var(--sp-4); font-size:0.82rem; line-height:1.5; color:var(--ink-2);
    text-align:left; font-family:var(--font-body); font-weight:400;
}

.kpi-help-backdrop[b-i1rfrre5t5]{position:fixed; inset:0; z-index:10;}

.dash-scroll[b-i1rfrre5t5]{
    flex:1; min-height:0; overflow-y:auto;
    margin-top:var(--sp-6);
    /* scroll continua a funcionar (roda do rato, teclado, toque, e o autoscroll via JS) — só a
       barra visual desaparece (ver dashboard-autoscroll.js) */
    scrollbar-width:none; -ms-overflow-style:none;
}
.dash-scroll[b-i1rfrre5t5]::-webkit-scrollbar{display:none;}

.clientes-grid[b-i1rfrre5t5]{
    display:grid; grid-template-columns:repeat(auto-fill, minmax(min(360px, 100%), 1fr)); gap:var(--sp-5);
    align-items:start;
}
.clientes-grid + .clientes-grid[b-i1rfrre5t5]{ margin-top:var(--sp-5); }

@media (max-width:900px){
    .dash-layout[b-i1rfrre5t5]{ flex-direction:column; }
    .dash-map-col[b-i1rfrre5t5]{ flex:0 0 240px; width:100%; }

    /* Fluxo normal de página (ver MainLayout.razor.css, mesmo breakpoint): sem altura fixa a
       distribuir, a lista de clientes deixa de ser um scroll interno e passa a fazer parte da
       página. A 2ª cópia da lista (só existe para o scroll contínuo dar a volta sem salto) é
       escondida — senão cada cliente apareceria duas vezes. */
    .dash-layout[b-i1rfrre5t5], .dash-content-col[b-i1rfrre5t5], .dash-scroll[b-i1rfrre5t5]{ flex:0 0 auto; min-height:auto; }
    .dash-scroll[b-i1rfrre5t5]{ overflow:visible; }
    .dash-scroll > .clientes-grid[aria-hidden='true'][b-i1rfrre5t5]{ display:none; }
}

/* Mobile (≤480px): sem lista de cartões de cliente — ~70 cartões de ~560px tornavam a página
   interminável e o scroll automático contínuo não funciona bem em ecrã tátil (pedido explícito do
   utilizador, 2026-10-02). Ficam só o mapa e os KPIs; o detalhe por cliente continua na página
   Clientes. Escondido por CSS (os cartões continuam no DOM, só não são desenhados). */
@media (max-width:480px){
    .dash-scroll[b-i1rfrre5t5]{ display:none; }
}
/* /Components/Pages/Dominios.razor.rz.scp.css */
.dominios-lista[b-8pbt4l2gkr]{display:flex; flex-direction:column; gap:var(--sp-3);}

.dominio-card[b-8pbt4l2gkr]{
    display:flex; flex-direction:column; gap:var(--sp-4);
    border:1px solid var(--line); background:var(--panel); padding:var(--sp-4) var(--sp-5);
    cursor:pointer;
}
.dominio-card:hover[b-8pbt4l2gkr]{border-color:var(--line-strong);}

.dominio-cabecalho[b-8pbt4l2gkr]{display:flex; align-items:flex-start; justify-content:space-between; gap:var(--sp-4);}
.dominio-titulo[b-8pbt4l2gkr]{display:flex; flex-direction:column; gap:var(--sp-1); min-width:0;}
.dominio-nome[b-8pbt4l2gkr]{font-size:1.05rem; font-weight:600; color:var(--ink); overflow-wrap:anywhere;}
.dominio-empresa[b-8pbt4l2gkr]{font-size:0.85rem; color:var(--ink-2); overflow-wrap:anywhere;}
.dominio-link[b-8pbt4l2gkr]{flex:0 0 auto;}

/* Os blocos (certificado, domínio) lado a lado quando há espaço; empilham em ecrãs estreitos. */
.dominio-blocos[b-8pbt4l2gkr]{
    display:grid; gap:var(--sp-3); cursor:default;
    grid-template-columns:repeat(auto-fit, minmax(min(260px, 100%), 1fr));
}
.dominio-sem-dominio[b-8pbt4l2gkr]{font-size:0.82rem; color:var(--ink-3); align-self:center;}
/* /Components/Pages/Home.razor.rz.scp.css */
.session-notice[b-ivne8xyrzt]{
    background:var(--warn-bg); color:var(--warn); text-align:center;
    padding:var(--sp-3) var(--sp-5); font-size:0.9rem;
}

.hero[b-ivne8xyrzt]{
    background:
        repeating-linear-gradient(0deg, rgba(11,35,56,0.05) 0 1px, transparent 1px 96px),
        repeating-linear-gradient(90deg, rgba(11,35,56,0.05) 0 1px, transparent 1px 96px),
        var(--bg);
    padding:var(--sp-9) var(--sp-5) var(--sp-8);
    text-align:center;
}
.hero-inner[b-ivne8xyrzt]{max-width:640px; margin:0 auto;}
.hero-mark[b-ivne8xyrzt]{margin-bottom:var(--sp-5); display:flex; justify-content:center;}
.kicker[b-ivne8xyrzt]{font-family:var(--font-mono); font-size:0.8rem; color:var(--ink-3); margin-bottom:var(--sp-5);}
.hero h1[b-ivne8xyrzt]{font-size:clamp(2.2rem, 6vw, 3.4rem); line-height:1.08; margin-bottom:var(--sp-5);}
.hero .lede[b-ivne8xyrzt]{font-size:1.1rem; color:var(--ink-2); margin:0 auto;}

.kpis[b-ivne8xyrzt]{padding:var(--sp-7) var(--sp-5); border-top:1px solid var(--line); border-bottom:1px solid var(--line);}
.kpis-inner[b-ivne8xyrzt]{max-width:900px; margin:0 auto; display:grid; grid-template-columns:repeat(3,1fr); gap:var(--sp-4);}
@media (max-width:640px){.kpis-inner[b-ivne8xyrzt]{grid-template-columns:1fr;}}

.traits[b-ivne8xyrzt]{padding:var(--sp-8) var(--sp-5);}
.traits-inner[b-ivne8xyrzt]{max-width:900px; margin:0 auto; display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-6);}
@media (max-width:640px){.traits-inner[b-ivne8xyrzt]{grid-template-columns:1fr;}}
.trait[b-ivne8xyrzt]{border-top:1px solid var(--line-strong); padding-top:var(--sp-3);}
.trait h3[b-ivne8xyrzt]{font-size:1rem; font-family:var(--font-body); font-weight:600; margin-bottom:var(--sp-2);}
.trait p[b-ivne8xyrzt]{color:var(--ink-2); font-size:0.94rem; margin:0;}
/* /Components/Shared/BlocoValidade.razor.rz.scp.css */
.bloco[b-2zo9kdfd85]{
    display:flex; flex-direction:column; gap:var(--sp-2); min-width:0;
    border:1px solid var(--line); background:var(--bg); padding:var(--sp-3) var(--sp-4);
}
/* Bloco desligado (ou domínio por acompanhar): continua visível mas esbatido. */
.bloco-desligado[b-2zo9kdfd85]{opacity:0.6;}

.bloco-titulo[b-2zo9kdfd85]{
    font-family:var(--font-mono); font-size:0.72rem; color:var(--ink-3);
    text-transform:uppercase; letter-spacing:0.02em;
}
.bloco-sub[b-2zo9kdfd85]{font-size:0.9rem; color:var(--ink); font-weight:500; overflow-wrap:anywhere;}
.bloco-vazio[b-2zo9kdfd85]{font-size:0.85rem; color:var(--ink-3);}

.bloco-validade[b-2zo9kdfd85]{display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap;}
.bloco-data[b-2zo9kdfd85]{font-size:0.95rem; color:var(--ink); font-weight:500;}

/* Linha de contexto: o que é verificado automaticamente e quando (ou porque falhou). */
.bloco-info[b-2zo9kdfd85]{font-size:0.78rem; color:var(--ink-3); overflow-wrap:anywhere;}
.bloco-info-erro[b-2zo9kdfd85]{color:var(--warn);}
.bloco-nota[b-2zo9kdfd85]{font-size:0.78rem; color:var(--ink-3); overflow-wrap:anywhere;}

.bloco-verificar[b-2zo9kdfd85]{align-self:flex-start; margin-top:var(--sp-1);}
/* /Components/Shared/ClienteCard.razor.rz.scp.css */
.cliente-card[b-s1ta27mhrw]{
    position:relative;
    border:1px solid var(--line); background:var(--panel); padding:var(--sp-5);
}

/* Sobreposição transparente a cobrir o cartão todo (ver ClienteCard.razor) — torna o cartão
   inteiro clicável sem precisar de mudar a tag raiz nem duplicar o markup. */
.cliente-card-link[b-s1ta27mhrw]{position:absolute; inset:0; z-index:1; cursor:pointer;}
.cliente-card:has(.cliente-card-link):hover[b-s1ta27mhrw]{border-color:var(--ink-3);}

/* Só o estado Crítico ganha destaque de cor no próprio cartão — pedido explícito do utilizador
   (Online/Atenção/Desconhecido continuam com o cartão neutro de sempre, só o badge no cabeçalho
   já os distingue). */
.cliente-card-critico[b-s1ta27mhrw]{
    border-color:var(--crit); background:var(--crit-bg);
}
.cliente-card-critico .cliente-substats[b-s1ta27mhrw]{ border-top-color:var(--crit); }

/* Breve destaque ao clicar no ponto do cliente no mapa (ver dashboard-autoscroll.js
   `destacarCliente`) — confirma visualmente qual cartão "saltou" para o ecrã, para não passar
   despercebido no meio de vários cartões parecidos. */
@keyframes cliente-card-destaque-b-s1ta27mhrw{
    0%, 100%{ box-shadow:0 0 0 0 transparent; }
    25%{ box-shadow:0 0 0 4px var(--signal); }
}
.cliente-card-destacado[b-s1ta27mhrw]{ animation:cliente-card-destaque-b-s1ta27mhrw 1.8s ease-in-out; }

.cliente-header[b-s1ta27mhrw]{
    display:flex; align-items:center; justify-content:space-between; gap:var(--sp-3);
    margin-bottom:var(--sp-5);
}

.cliente-top[b-s1ta27mhrw]{
    display:flex; flex-direction:column; gap:var(--sp-4);
    margin-bottom:var(--sp-5);
}

.donut-block h4[b-s1ta27mhrw]{
    font-family:var(--font-mono); font-weight:500; font-size:0.78rem; color:var(--ink-3);
    margin:0 0 var(--sp-4);
}

/* flex-wrap: em cartões muito estreitos (<~340px) a legenda passa para baixo do donut em vez de ser espremida (revisão mobile). */
.donut-wrap[b-s1ta27mhrw]{display:flex; align-items:center; gap:var(--sp-5); flex-wrap:wrap;}

.donut[b-s1ta27mhrw]{width:130px; height:130px; flex-shrink:0;}
.donut-track[b-s1ta27mhrw]{fill:none; stroke:var(--line); stroke-width:18;}
.donut-seg[b-s1ta27mhrw]{fill:none; stroke-width:18;}

.donut-legend[b-s1ta27mhrw]{display:flex; flex-direction:column; gap:var(--sp-2); min-width:0; flex:1 1 140px;}
.legend-row[b-s1ta27mhrw]{display:flex; align-items:center; gap:var(--sp-2); font-size:0.85rem;}
.legend-row i[b-s1ta27mhrw]{width:9px; height:9px; flex-shrink:0;}
.legend-row span[b-s1ta27mhrw]{color:var(--ink-2); flex:1;}
.legend-row b[b-s1ta27mhrw]{font-family:var(--font-mono); color:var(--ink); font-weight:500;}

.cliente-substats[b-s1ta27mhrw]{
    display:flex; flex-direction:column; gap:var(--sp-2);
    border-top:1px solid var(--line); padding-top:var(--sp-3);
}
.substat-row[b-s1ta27mhrw]{display:flex; align-items:baseline; justify-content:space-between; gap:var(--sp-3); font-size:0.9rem;}
.substat-row span[b-s1ta27mhrw]{color:var(--ink-2);}
.substat-row b[b-s1ta27mhrw]{font-family:var(--font-mono); font-weight:500; color:var(--ink);}

.bar-chart[b-s1ta27mhrw]{display:flex; flex-direction:column; gap:var(--sp-2);}
.bar-row[b-s1ta27mhrw]{display:grid; grid-template-columns:minmax(90px,auto) 1fr auto; align-items:center; gap:var(--sp-3); font-size:0.85rem;}
.bar-track[b-s1ta27mhrw]{height:14px; background:var(--panel-strong);}
.bar-fill[b-s1ta27mhrw]{height:100%; min-width:2px; background:var(--signal);}
.bar-value[b-s1ta27mhrw]{font-family:var(--font-mono); color:var(--ink-2); text-align:right; min-width:2ch;}
.bar-label[b-s1ta27mhrw]{color:var(--ink-2); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}

.cliente-breakdowns[b-s1ta27mhrw]{
    display:flex; flex-direction:column; gap:var(--sp-5);
}

.breakdown h4[b-s1ta27mhrw]{
    font-family:var(--font-mono); font-weight:500; font-size:0.78rem; color:var(--ink-3);
    margin:0 0 var(--sp-3);
}

.breakdown-row[b-s1ta27mhrw]{
    display:flex; align-items:center; justify-content:space-between; gap:var(--sp-3);
    padding:var(--sp-2) 0; border-bottom:1px solid var(--line); font-size:0.9rem;
}
.breakdown-row:last-child[b-s1ta27mhrw]{border-bottom:none;}
.breakdown-num[b-s1ta27mhrw]{font-family:var(--font-mono); color:var(--ink-2);}
/* /Components/Shared/Paginacao.razor.rz.scp.css */
.paginacao[b-9ejhz2z4pf]{
    display:flex; align-items:center; justify-content:space-between; gap:var(--sp-4);
    margin-top:var(--sp-4); flex-wrap:wrap;
}
.paginacao-info[b-9ejhz2z4pf]{font-size:0.85rem; color:var(--ink-2);}
.paginacao-botoes[b-9ejhz2z4pf]{display:flex; gap:var(--sp-2); align-items:center;}
.paginacao-pagina[b-9ejhz2z4pf]{font-size:0.85rem; color:var(--ink-2);}
/* /Components/Shared/ServidorCard.razor.rz.scp.css */
.servidor-card[b-tyq6o1jrvl]{
    border:1px solid var(--line); background:var(--panel); padding:var(--sp-5);
    display:flex; flex-direction:column; gap:var(--sp-4);
}
.servidor-card-inativo[b-tyq6o1jrvl]{opacity:0.55;}

/* flex-wrap + min-width:0 no filho da esquerda — sem isto, um nome comprido ou o badge "Ativo"
   empurravam o botão "Editar" para fora do cartão em ecrãs estreitos, em vez de quebrar linha
   (comportamento por omissão do flexbox: um item não encolhe abaixo do tamanho do seu conteúdo a
   não ser que min-width seja explicitamente 0). */
.servidor-header[b-tyq6o1jrvl]{
    display:flex; align-items:flex-start; justify-content:space-between; gap:var(--sp-3);
    flex-wrap:wrap;
}
.servidor-titulo[b-tyq6o1jrvl]{min-width:0; flex:1 1 auto;}
.servidor-titulo-linha[b-tyq6o1jrvl]{display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap;}
.servidor-header h3[b-tyq6o1jrvl]{margin:0; overflow-wrap:break-word; min-width:0;}
.servidor-link[b-tyq6o1jrvl]{
    display:block; font-size:0.82rem; color:var(--ink-3); text-decoration:none;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:100%;
}
.servidor-link:hover[b-tyq6o1jrvl]{color:var(--signal);}

/* Duplicado de ClienteCard.razor.css — CSS isolation do Blazor não deixa reaproveitar as classes
   de outro componente diretamente, só o desenho visual. Flex (não grid) por causa de ecrãs
   estreitos: um grid "minmax(90px,auto) 1fr auto" deixava a coluna do meio (a barra) colapsar
   para uma linha de 1-2px quando o label+valor já ocupavam quase toda a largura do cartão — o
   min-width no track abaixo garante que a barra nunca fica ilegível. */
.bar-chart[b-tyq6o1jrvl]{display:flex; flex-direction:column; gap:var(--sp-2);}
.bar-row[b-tyq6o1jrvl]{display:flex; flex-wrap:wrap; align-items:center; gap:var(--sp-2) var(--sp-3); font-size:0.85rem;}
.bar-track[b-tyq6o1jrvl]{flex:1 1 100px; min-width:60px; height:14px; background:var(--panel-strong);}
.bar-fill[b-tyq6o1jrvl]{height:100%; min-width:2px; background:var(--signal);}
.bar-value[b-tyq6o1jrvl]{flex:0 0 auto; font-family:var(--font-mono); color:var(--ink-2); text-align:right; min-width:2ch;}
.bar-label[b-tyq6o1jrvl]{flex:0 1 auto; max-width:120px; color:var(--ink-2); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}

.servidor-sem-dados[b-tyq6o1jrvl]{color:var(--ink-3); font-size:0.85rem; margin:0;}

.servidor-substats[b-tyq6o1jrvl]{
    display:flex; flex-direction:column; gap:var(--sp-2);
    border-top:1px solid var(--line); padding-top:var(--sp-3);
}
.substat-row[b-tyq6o1jrvl]{display:flex; align-items:baseline; justify-content:space-between; gap:var(--sp-3); font-size:0.9rem;}
.substat-row span[b-tyq6o1jrvl]{color:var(--ink-2);}
.substat-row b[b-tyq6o1jrvl]{font-family:var(--font-mono); font-weight:500; color:var(--ink);}

.servidor-clientes[b-tyq6o1jrvl]{
    border-top:1px solid var(--line); padding-top:var(--sp-3);
    display:flex; flex-direction:column; gap:var(--sp-2);
}
.servidor-clientes h4[b-tyq6o1jrvl]{
    font-family:var(--font-mono); font-weight:500; font-size:0.78rem; color:var(--ink-3);
    margin:0;
}
.clientes-lista[b-tyq6o1jrvl]{display:flex; flex-wrap:wrap; gap:var(--sp-2);}
.cliente-link[b-tyq6o1jrvl]{
    font-size:0.85rem; color:var(--signal); text-decoration:none;
    border:1px solid var(--line); padding:2px 8px; border-radius:2px;
}
.cliente-link:hover[b-tyq6o1jrvl]{border-color:var(--signal);}

.tarefas-lista[b-tyq6o1jrvl]{display:flex; flex-direction:column; gap:var(--sp-3);}
.tarefa-row[b-tyq6o1jrvl]{padding:var(--sp-2) 0; border-bottom:1px solid var(--line); font-size:0.85rem;}
.tarefa-row:last-child[b-tyq6o1jrvl]{border-bottom:none;}
.tarefa-nome[b-tyq6o1jrvl]{font-weight:600; color:var(--ink); margin-bottom:var(--sp-2);}
.tarefa-detalhe[b-tyq6o1jrvl]{display:flex; flex-wrap:wrap; gap:var(--sp-4); color:var(--ink-2);}
.tarefa-mensagem[b-tyq6o1jrvl]{margin-top:var(--sp-2); color:var(--crit); font-size:0.82rem;}
