.app-header {
  display: flex;
  align-items: center;
  gap: 20px;
  height: 60px;
  padding: 0 32px;
  background: var(--color-panel);
  border-bottom: 1px solid var(--color-border);
  position: sticky;
  top: 0;
  z-index: 5;
  flex-shrink: 0;
}

/* Official wordmark image (user-provided PNG, 06/08/2026) -- replaces the styled-text
   .app-header-brand block. Height fits the 60px header with breathing room; width follows the
   PNG's ~4:1 aspect ratio (~115px). */
.app-header-brand-img {
  height: 32px;
  width: auto;
  display: block;
  flex-shrink: 0;
}

.app-header-divider {
  width: 1px;
  height: 28px;
  background: var(--color-border);
}

.app-header-title {
  font-family: var(--font-mono);
  font-size: 17px;
  color: var(--color-label);
}

.app-header-title .accent {
  color: var(--color-primary);
}

.app-header-right {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-left: auto;
}

.app-header-ref {
  color: var(--color-label);
  font-size: 11px;
  font-family: var(--font-mono);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  padding: 4px 10px;
  border-radius: var(--radius);
}

.app-header-ref strong {
  color: var(--color-header);
}

.service-banner {
  flex-shrink: 0;
  padding: var(--space-2) var(--space-4);
  /* Sem equivalente na legacy -- fundo dedicado (não --color-negative-text, que agora é o vermelho
     puro da legacy) pra manter branco-sobre-fundo em ~5:1+, já que não há visual de referência aqui. */
  background: color-mix(in srgb, var(--color-negative) 65%, black);
  color: #fff;
  font-weight: 600;
  font-size: 13px;
  text-align: center;
}

.layout {
  /* flex, não grid: o painel de negociação precisa colapsar pra width:0 e devolver o espaço pro
     ranking (panel-detail.open em components.css) -- um item flex com largura explícita se ajusta
     de graça, enquanto uma coluna de grid "auto" não garante essa contração de forma confiável. */
  display: flex;
  gap: var(--space-3);
  padding: var(--space-3);
  align-items: stretch;
  flex: 1;
  min-height: 0; /* body is a flex column; this lets .layout take the remaining height instead of growing past it */
}

.panel {
  background: var(--color-panel);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-3);
  min-height: 0; /* required for overflow-y to work inside a flex item */
  overflow-y: auto;
}

.panel-params {
  width: 330px;
  flex-shrink: 0;
  /* Vertical voltou pro nível do .panel genérico (16px) -- o valor da legacy (28px) somado ao resto
     do conteúdo do painel (mais denso aqui, com "Informações" fundido a "Base de Dados") ultrapassava
     a altura da viewport a 100% de zoom em telas comuns (1366x768), forçando scroll interno onde a
     legacy nunca precisa. Horizontal mantém o respiro extra. */
  padding: 12px 24px;
}

/* O scroll vertical passa de .panel (que tem padding-top:16px, então o cabeçalho sticky da tabela
   deixava um respiro por onde uma linha rolando aparecia sem cobertura -- o padding fica DENTRO da
   área visível de um overflow:auto, só o que passa da borda do padding é cortado) para #ranking-wrap
   (components.css), que não tem padding nenhum -- igual ao #table-wrap da legacy (wwwroot/legacy/
   index.html:285), onde o thead sticky encosta direto na borda do scrollport, sem respiro. */
.panel-ranking {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  overflow: visible;
  /* Container query for the column tiers (item 5, components.css .col-t2/.col-t3): it must be a
     container, not a media query, because this card's usable width depends on the Negociação
     panel being open (430px less), not on the window size. inline-size is safe here: flex:1
     (basis 0) + min-width:0 already make the width come from the flex layout, never from the
     content. */
  container-name: ranking;
  container-type: inline-size;
  /* #ranking-wrap (a tabela) fica flush com a borda do card -- igual ao #table-wrap da legacy, que
     nunca tem padding (só .card-header tem o seu próprio, wwwroot/legacy/index.html:271-284). Sem
     isso, #ranking-wrap herdava os 16px de padding do .panel genérico e ficava ~17px mais pra dentro
     da borda esquerda do card do que a legacy. */
  padding: 0;
}

/* painel-detalhe a 430px, igual à legacy: o heatmap 6x6 precisa de ~376px de largura intrínseca
   (6 colunas + rótulo de financeiro + padding) -- por baixo disso ele cai num overflow-x que só
   responde a scroll horizontal explícito (shift+roda, trackpad), invisível pra quem usa roda
   vertical comum. 07-front.md pede "heatmap 6x6 sem célula vazia", isto é, tudo visível de cara --
   430px folga mais que o mínimo, sem regredir essa garantia. (06/08/2026, item 7: o score tem
   linha própria, components.css#.score-row, e "// HEATMAP <ticker>" é um header sozinho -- o grid
   do heatmap segue com a largura toda do painel, então esta largura não mudou.) */
.panel-detail {
  width: 430px;
  flex-shrink: 0;
}

@media (max-width: 1024px) {
  body {
    display: block;
  }

  .layout {
    flex-direction: column;
    flex: none;
  }

  .panel {
    overflow-y: visible;
  }

  .panel-params,
  .panel-detail {
    width: auto;
  }

  /* Empilhado, largura não libera espaço nenhum pra ranking -- fechado vira "não mostrado" em vez
     de "width:0", senão o painel some horizontalmente mas continua ocupando uma faixa vertical
     vazia entre ranking e o resto da página. */
  .panel-detail:not(.open) {
    display: none;
  }
}

/* Enquanto o portao de login esta aberto, o conteudo atras dele nao deve rolar: o overlay e fixo e
   cobre tudo, mas a barra de rolagem da aplicacao continuava visivel e utilizavel por tras. */
body.gate-open {
  overflow: hidden;
}

/* A aplicação fica invisível até a sessão resolver (14/08/2026). Corrige a "piscada da tela logada":
   o HTML da tela de negociação é estático e o navegador o pintava imediatamente, enquanto o portão só
   aparecia depois do round trip do /api/auth/me.
   `visibility` e não `display: none`: o layout já nasce calculado, então quando a classe sai não há
   reflow nem salto -- e os módulos que resolvem elementos do DOM em tempo de import continuam medindo
   caixas de verdade, que é do que ranking.js e heatmap.js dependem.
   O portão é filho do <body> e precisa escapar da regra, senão ele também nasceria invisível e a tela
   ficaria em branco no lugar de piscar -- que seria trocar um defeito por outro pior. */
body.booting > *:not(#login-gate) {
  visibility: hidden;
}

/* F11: o corpo da página de administração (usuarios.html). Mesmo respiro do .layout da negociação,
   mas em página própria -- não é uma view alternada, então não há nada a esconder aqui. */
.admin-page {
  flex: 1;
  min-height: 0;
  padding: var(--space-3);
  /* Coluna flex, e não um bloco que cresce (13/08/2026): as duas tabelas empilhadas somavam mais que a
     viewport e quem ganhava a barra de rolagem era a PÁGINA -- rolar até as corretoras levava a tabela
     de usuários junto, para fora da tela. Empilhar deixou de ser o desenho no mesmo dia (viraram abas),
     mas a coluna flex continua sendo o que faz a tabela visível ocupar exatamente a altura que sobra e
     rolar por dentro, em vez de esticar a página.
     `overflow: hidden` e não `auto`: a página em si nunca rola: quem rola é a tabela. */
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Só a tabela encolhe. Sem isto o flex tira altura das abas e da linha de status antes de tirar da
   tabela, e são os controles que somem. */
.admin-page > .admin-tabs,
.admin-page > .login-status {
  flex-shrink: 0;
}

/* A linha de status reserva 32px para não empurrar o conteúdo quando uma mensagem aparece. Entre as
   abas e o painel isso virava uma faixa vazia visível, então aqui ela só ocupa espaço quando tem o que
   dizer. O motivo original da reserva não se perde: a altura sai da TABELA, que encolhe 32px e volta,
   e não dos controles acima dela -- ninguém vê botão pular de lugar. */
.admin-page > .login-status:empty {
  min-height: 0;
  margin-top: 0;
}
