/* Meta Acústica — tema, tipografia e componentes da tela.
   Direção visual dos vídeos da 3R (`3r-motion`, direção `padrao`, operador
   2026-10-13): noite petróleo, traço fino, âmbar só no que se aponta, título
   largo e fino, rótulo largo em caixa-alta espaçada, número em mono. O escuro é
   o tema da casa (operador, 2026-10-15); o claro, no papel da 3R, entrou em
   2026-10-20, escolhido pelo botão de tema (`tema.js`, `<html data-tema>`).
   Cores institucionais: #03585F, #00A19C,
   #006C81 e o dourado #D9B50D. Nenhuma fonte, folha ou imagem externa: a CSP
   da API é `default-src 'self'` e vale para `/` e `/ui/`; as fontes (Archivo e
   JetBrains Mono, OFL) moram em `ui/fonts/`. */

@font-face {
  font-family: "Archivo"; font-style: normal; font-display: swap;
  font-weight: 100 900; font-stretch: 62% 125%;
  src: url("fonts/archivo-var.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono"; font-style: normal; font-display: swap;
  font-weight: 100 800;
  src: url("fonts/jetbrains-mono-var.woff2") format("woff2");
}

/* ---------------------------------------------------------------- tokens */
:root {
  color-scheme: dark;

  --sans: "Archivo", system-ui, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;

  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px; --s7: 48px;
  /* Canto vivo, como a moldura dos painéis do vídeo. */
  --radius: 0; --radius-sm: 0;

  /* A noite petróleo: o fundo é o `ink` do vídeo, a faixa do topo é a tarja
     de cinema, o cartão é o painel no mundo, um tom acima do fundo. */
  --bg: #051e21;
  --band: #020d0e;
  --surface: #072528;
  --surface-2: #0a2d31;
  --sunken: #041a1c;
  --line: #2e4f51;
  --line-soft: #173538;
  --frame: #5f7f7f;
  --ink: #f1eee6;
  --ink-2: #c9d3cf;
  --muted: #8fa9a8;
  /* O teal institucional é o estado "em ordem" e o link. */
  --accent: #00a19c;
  --accent-ink: #051e21;
  --accent-soft: #0b3a3b;
  /* O âmbar aponta: o fio do título, a aba aberta, a ação principal, o foco. */
  --signal: #d9b50d;
  --signal-soft: #1a2d1f;
  --series: #2cc5be;
  /* O ponto dentro da bolha do gráfico: a bolha é `--ink` (papel), e o ponto
     precisa aparecer sobre ela. */
  --series-bolha: #00a19c;
  --series-b: #e6e1d3;
  /* A cor do tipo de sonda (`plano-graficos.md` §4, Direção 2, operador 2026-10-20):
     principal e contexto. Reforça o nome, a letra e o ícone, e nunca diz o tipo
     sozinha: ar e meteorologia não se separam pela cor para quem tem daltonismo. */
  --tipo-ruido: #00a19c;
  --tipo-ruido-2: #027977;
  --tipo-meteo: #3987e5;
  --tipo-meteo-2: #2968a9;
  --tipo-ar: #9085e9;
  --tipo-ar-2: #6466ab;
  --tipo-vibracao: #d55181;
  --tipo-vibracao-2: #934365;
  /* O passo de contexto da série (LASmax, MP10, setas); fora da sonda, o apagado. */
  --series-2: var(--muted);
  --gold: #d9b50d;
  /* Escala de risco da 3R: o nível de ação é o âmbar, o limite é o vermelho. */
  --warn: #e2c13a;
  --warn-bg: #1a2d1f;
  --warn-line: #4f531a;
  --danger: #ef6a5f;
  --danger-bg: #241f21;
  --danger-line: #6a2a27;
  --focus: #d9b50d;
  --grid: #12363a;
  --axis: #3a5b5c;
  --gap-fill: #1a2d1f;
  --gap-line: #4f531a;
  --skeleton: #0e3236;
  --shadow: none;
  /* O degradê do fundo e a nuvem (`nuvem.mjs` lê o tema na pintura). */
  --fundo-alto: #0a2c30;
  --fundo-borda: #03171a;
}

/* O claro (decisão do operador, 2026-10-20): o papel dos vídeos da 3R, com o
   texto no petróleo escuro. As institucionais escurecem onde viram texto, para
   o contraste de 4,5:1 sobre o papel; o turquesa fica no traço e no preenchimento. */
/* `.tema-claro`: o cartão de compartilhar (P5), sempre no claro, qualquer que seja o tema da tela. */
.tema-claro,
:root[data-tema="claro"] {
  color-scheme: light;
  --bg: #f1eee6;
  --band: #e7e2d6;
  --surface: #faf8f3;
  --surface-2: #f4f1e9;
  --sunken: #e8e4da;
  --line: #b4c0bd;
  --line-soft: #dad9d0;
  --frame: #7b908f;
  --ink: #051e21;
  --ink-2: #213b3c;
  --muted: #4c6665;
  --accent: #03585f;
  --accent-ink: #f1eee6;
  --accent-soft: #d5e6e3;
  --signal: #7a5f00;
  --signal-soft: #f1e8c8;
  --series: #00837e;
  --series-bolha: #2cc5be;
  --series-b: #3d5253;
  --tipo-ruido: #00a19c;
  --tipo-ruido-2: #50bdb8;
  --tipo-meteo: #2a78d6;
  --tipo-meteo-2: #6da1df;
  --tipo-ar: #6250d6;
  --tipo-ar-2: #9386df;
  --tipo-vibracao: #b13e70;
  --tipo-vibracao-2: #e87ba4;
  --gold: #7a5f00;
  --warn: #775c00;
  --warn-bg: #f4ecca;
  --warn-line: #d6c174;
  --danger: #b0281f;
  --danger-bg: #f7e2df;
  --danger-line: #e2a49d;
  --focus: #7a5f00;
  --grid: #e1e2da;
  --axis: #9eaeab;
  --gap-fill: #f1e8c8;
  --gap-line: #cdb55c;
  --skeleton: #e6e2d8;
  --fundo-alto: #fbfaf6;
  --fundo-borda: #e6e1d4;
}
/* Na página de uma sonda, os gráficos tomam a cor do tipo (operador, 2026-10-21): Tempo
   real, Séries e Clima. Início e Painel misturam tipos e ficam no `--series`. */
.cartao-fora[data-tipo="noise"], #sonda-view[data-tipo="noise"] { --series: var(--tipo-ruido); --series-2: var(--tipo-ruido-2) }
.cartao-fora[data-tipo="weather"], #sonda-view[data-tipo="weather"] { --series: var(--tipo-meteo); --series-2: var(--tipo-meteo-2) }
.cartao-fora[data-tipo="air_quality"], #sonda-view[data-tipo="air_quality"] { --series: var(--tipo-ar); --series-2: var(--tipo-ar-2) }
.cartao-fora[data-tipo="vibration"], #sonda-view[data-tipo="vibration"] { --series: var(--tipo-vibracao); --series-2: var(--tipo-vibracao-2) }
/* Símbolo de norma e unidade não mudam de caixa no rótulo em caixa alta (`simbolos`). */
.simbolo { text-transform: none }
/* A assinatura da 3R na tela de acesso: a versão de cada fundo. */
:root[data-tema="claro"] .so-escuro, :root:not([data-tema="claro"]) .so-claro { display: none }

/* --------------------------------------------------------------- básicos */
* { box-sizing: border-box }
/* A calha da barra de rolagem não é reservada (operador, 2026-10-27, §61). De 2026-09-07
   a 2026-10-27 ela era (`scrollbar-gutter: stable`), para a página não andar 7,5 px quando a
   barra aparece; mas, em tela que cabe sem rolar, a calha ficava vazia: uma faixa de 15 px à
   direita onde o cabeçalho, a nuvem e o fundo não chegam. O operador preferiu sem a faixa. */
html { -webkit-text-size-adjust: 100% }
/* A barra própria, por cima da página (`rolagem.mjs`, §62): só o polegar, meio
   transparente, ao rolar ou com o mouse na borda. A nativa sai só com ponteiro fino;
   no celular a do sistema já é de sobreposição. Não empurra nada para o lado. */
@media (pointer: fine) {
  html.rolagem-propria { scrollbar-width: none }
  html.rolagem-propria::-webkit-scrollbar { display: none }
}
.barra-rolagem { position: fixed; top: 0; right: 0; bottom: 0; width: 12px; z-index: 50;
           opacity: 0; transition: opacity .25s ease }
.barra-rolagem.ativa, .barra-rolagem:hover { opacity: 1 }
.barra-rolagem-polegar { position: absolute; top: 0; right: 2px; width: 6px; border-radius: 6px;
                   background: color-mix(in srgb, var(--ink) 38%, transparent);
                   transition: width .15s ease, background-color .15s ease }
.barra-rolagem:hover .barra-rolagem-polegar, .barra-rolagem.arrastando .barra-rolagem-polegar {
  width: 10px; background: color-mix(in srgb, var(--ink) 55%, transparent) }
/* O fundo é a noite do vídeo: mais claro no alto, onde a luz do painel cai, e
   fechando nas bordas. Fixo, para não andar com a rolagem. */
body {
  margin: 0;
  background: radial-gradient(120% 70% at 50% 0%, var(--fundo-alto) 0%, var(--bg) 55%,
                              var(--fundo-borda) 100%) fixed var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.55;
  font-synthesis: none;
  text-rendering: optimizeLegibility;
}
/* A nuvem de pontos dos vídeos (`nuvem.mjs`): fixa atrás de tudo, apagando
   para cima. No acesso ela é a cena; dentro do sistema recua, porque ali quem
   fala é o dado. Mede `100lvh` para não redesenhar com a barra do celular. */
.nuvem { position: fixed; left: 0; top: 0; width: 100%; height: 100lvh; z-index: -1;
         pointer-events: none; opacity: .45;
         -webkit-mask-image: linear-gradient(to bottom, transparent 38%, #000 64%);
         mask-image: linear-gradient(to bottom, transparent 38%, #000 64%) }
body:has(#login-view:not([hidden])) .nuvem { opacity: 1 }
/* A moldura de visor das telas do Death Stranding (Kojima, 2019), referência do
   operador em 2026-10-15: traços curtos nas bordas, em pares, a um terço e a
   dois terços da altura. É moldura, não conteúdo: fixa, atrás de tudo, e sai no
   celular, onde a calha de 16 px é do conteúdo. */
.hud { position: fixed; inset: 0; z-index: -1; pointer-events: none;
       --t: linear-gradient(var(--frame) 0 0);
       background-image: var(--t), var(--t), var(--t), var(--t), var(--t), var(--t), var(--t), var(--t);
       background-repeat: no-repeat;
       background-size: 12px 1px, 5px 1px, 12px 1px, 5px 1px, 12px 1px, 5px 1px, 12px 1px, 5px 1px;
       background-position: left 12px top 33%, left 12px top calc(33% + 5px),
                            left 12px top 67%, left 12px top calc(67% + 5px),
                            right 12px top 33%, right 12px top calc(33% + 5px),
                            right 12px top 67%, right 12px top calc(67% + 5px) }
/* Os cantos de mira dos painéis do mesmo jogo: o painel se define pelos quatro
   cantos claros sobre um fio apagado. Só imagem de fundo, então a cor de fundo
   continua de quem usa (`background-color`, nunca o atalho `background`). */
/* A ordem importa para os testes que leem a regra de `dialog.dlg` pelo nome:
   ele não pode ser o último da lista, colado na chave. */
.mira, dialog.dlg, .card, .cat, .chart-card, .detail-card, .filterbar, .export-panel, .aviso,
.manage-panel {
        --m: linear-gradient(var(--frame) 0 0);
        background-image: var(--m), var(--m), var(--m), var(--m), var(--m), var(--m), var(--m), var(--m);
        background-repeat: no-repeat; background-origin: border-box;
        background-size: 14px 1px, 1px 14px, 14px 1px, 1px 14px, 14px 1px, 1px 14px, 14px 1px, 1px 14px;
        background-position: left top, left top, right top, right top,
                             left bottom, left bottom, right bottom, right bottom }
h1, h2, h3, p { margin-top: 0 }
h1, h2, h3 { color: var(--ink); text-wrap: balance }
/* O título é a manchete do vídeo: Archivo larga e fina. */
h1 { font-weight: 300; font-stretch: 125%; letter-spacing: .01em }
h2 { font-size: 1.35rem; font-weight: 350; font-stretch: 118%; letter-spacing: .01em; line-height: 1.25 }
h3 { font-size: .95rem; font-weight: 600; letter-spacing: .01em; margin-bottom: var(--s3) }
a { color: var(--accent) }
img { max-width: 100%; height: auto }
[hidden] { display: none !important }
/* Minimalista por padrão (operador, 2026-10-15): a explicação de método, o
   rodapé de leitura e a instrução de uso do gráfico saem da tela; o botão
   "Mostrar detalhes" do título (`body.detalhes`) os traz de volta. Número,
   gráfico, aviso e estado nunca são `.explica`. A instrução de zoom some só
   enquanto não há zoom: com zoom, a linha diz o recorte e tem o botão de voltar. */
body:not(.detalhes) .explica,
body:not(.detalhes) .legend-text small,
/* A faixa acima do gráfico é só explicação, de "Mostrar detalhes". Até o §61 ela aparecia
   com a aproximação, levando o botão, e a caixa crescia (achado do operador, 2026-10-27). */
body:not(.detalhes) .zoombar { display: none }
/* O botão de voltar à janela inteira mora no cabeçalho do cartão, e o lugar dele fica
   reservado mesmo escondido: aproximar e voltar não mudam a altura de nada (§61). */
.zoom-volta { margin-left: auto }
.zoom-volta[hidden] { display: inline-flex !important; visibility: hidden }
.chart-top .zoom-volta + .compartilhar { margin-left: 0 }

/* O rótulo do vídeo: largo, caixa-alta espaçada, com o fio âmbar na frente. */
.eyebrow { font-size: 11px; font-weight: 500; font-stretch: 125%; letter-spacing: .19em;
           color: var(--ink-2); line-height: 1.7; text-transform: uppercase;
           border-left: 2px solid var(--signal); padding-left: 10px }
.muted, .footnote { color: var(--muted) }
.small { font-size: 12px; line-height: 1.6 }
.warning { color: var(--warn) }
.error { color: var(--danger); font-size: 14px; min-height: 20px }
.footnote { font-size: 11px; line-height: 1.8; max-width: 900px; margin: var(--s2) 0 var(--s5) }

.sr-only, .skip:not(:focus) {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.skip:focus { position: absolute; left: 20px; top: 6px; background: var(--surface); padding: var(--s3); z-index: 3; border-radius: var(--radius-sm) }

/* --------------------------------------------------------------- controles */
button, input, select { font: inherit }
button, summary { cursor: pointer }
button {
  min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  border-radius: var(--radius-sm);
  padding: 10px var(--s4);
  font-size: .875rem; font-weight: 500; letter-spacing: .02em;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink);
}
button:hover:not(:disabled) { border-color: var(--ink-2); color: var(--ink) }
button:disabled { opacity: .45; cursor: default }
/* A ação principal é traço âmbar, não bloco chapado: o vídeo não tem massa de
   cor. Do Death Stranding vem a barra com direção: o fio grosso na entrada e a
   luz que apaga ao longo dela. */
.primary, .botao.primary { color: var(--signal); border-color: color-mix(in srgb, var(--signal) 55%, transparent);
           border-left: 3px solid var(--signal);
           background: linear-gradient(90deg, color-mix(in srgb, var(--signal) 20%, transparent),
                                       color-mix(in srgb, var(--signal) 3%, transparent) 75%) }
.primary:hover:not(:disabled), .botao.primary:hover { color: var(--signal); border-color: var(--signal);
           background: linear-gradient(90deg, color-mix(in srgb, var(--signal) 32%, transparent),
                                       color-mix(in srgb, var(--signal) 8%, transparent) 80%) }
.quiet { background: transparent }
.icon { width: 18px; height: 18px; flex: none }

label { display: block; font-size: 12px; font-weight: 500; letter-spacing: .02em; color: var(--ink-2); margin: var(--s4) 0 }
input, select {
  display: block; width: 100%; min-height: 44px;
  margin-top: var(--s2); padding: 10px 11px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--sunken); color: var(--ink); font-size: 13px;
}
select { padding-right: var(--s2) }
input:hover, select:hover { border-color: var(--frame) }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px }
fieldset { border: 0; border-top: 1px solid var(--line); padding: 0; margin: var(--s5) 0 0 }
legend { font-size: 11px; font-weight: 700; padding: 0 var(--s2) 0 0; color: var(--muted) }
.check { display: flex; align-items: center; gap: 10px; line-height: 1.5 }
.check input { width: 17px; height: 20px; min-height: 0; margin: 0; flex: none; accent-color: var(--accent) }
/* No celular o alvo é o rótulo inteiro, com 44 px de altura: a caixa de 17 px sozinha é
   pequena para o dedo (§54). */
@media (max-width: 760px) { .check { min-height: 44px } }

/* ----------------------------------------------------------------- topbar */
/* A tarja de cima do vídeo: faixa quase preta, a marca à esquerda com o fio
   âmbar, e o selo da 3R sempre presente. */
.topbar {
  background: var(--band);
  border-bottom: 1px solid var(--line-soft);
  padding: var(--s2) 4%;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s4); flex-wrap: wrap;
}
.brand { display: flex; gap: 12px; align-items: center; text-decoration: none; color: var(--ink) }
.brand-mark { width: 32px; height: 32px; flex: none }
.brand-name { font-size: 15px; font-weight: 400; font-stretch: 125%; letter-spacing: .28em;
              text-transform: uppercase; line-height: 1.1;
              border-left: 2px solid var(--signal); padding-left: 12px }
.brand-name small { display: block; letter-spacing: .24em; font-size: 7.5px; font-weight: 500;
                    color: var(--muted); margin-top: 5px }
.topbar-end { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; justify-content: flex-end }
.topbar-end .eyebrow { color: var(--muted) }
.tema { white-space: nowrap; font-size: 12.5px }
/* O botão de tema (2026-10-20) divide a ponta da barra com a conta: o nome
   encolhe com reticências, e a barra não quebra em duas linhas. */
.topbar-end { flex-wrap: nowrap; min-width: 0; max-width: 100% }
.account { min-width: 0 }
.account { display: flex; gap: var(--s3); align-items: center; font-size: 13px; color: var(--ink-2) }
#who { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }

/* ------------------------------------------------------------------ login */
/* O acesso é uma tela do jogo: o título de tela na largura toda, com o fio que
   a atravessa, e embaixo dele dois painéis — o que é o sistema e a entrada. */
.login-layout {
  max-width: 1240px; margin: 8vh auto 0; padding: var(--s5) var(--s6) var(--s6);
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(340px, 420px);
  gap: var(--s6) 96px; align-items: start;
}
/* O título de tela: o fio embaixo vai de ponta a ponta, mais claro no começo,
   com um traço de esquadro em cada ponta. */
.tela-titulo { grid-column: 1 / -1; position: relative; padding-bottom: var(--s4);
               border-bottom: 1px solid var(--line);
               background: linear-gradient(var(--ink-2) 0 0) left bottom / 72px 1px no-repeat }
.tela-titulo::before, .tela-titulo::after { content: ""; position: absolute; bottom: -4px;
               width: 1px; height: 7px; background: var(--frame) }
.tela-titulo::before { left: 0 }
.tela-titulo::after { right: 0 }
.tela-titulo h1 { font-size: clamp(30px, 3.2vw, 46px); line-height: 1.15; margin: var(--s3) 0 0 }
.intro { padding-top: var(--s2) }
.intro > p:not(.signature-wrap) { font-size: 15.5px; line-height: 1.75; max-width: 46ch; color: var(--ink-2); margin: 0 }
/* A hierarquia dos dados como a rota do mapa do jogo: nós ligados por fio, o
   nome embaixo de cada um. É lista ordenada, e o leitor de tela lê assim. */
.cadeia { list-style: none; margin: var(--s6) 0 0; padding: 0; max-width: 560px;
          display: grid; grid-template-columns: repeat(4, 1fr) }
.cadeia li { position: relative; padding-top: 22px; font-size: 11px; font-weight: 500;
             font-stretch: 125%; letter-spacing: .16em; text-transform: uppercase; color: var(--muted) }
.cadeia li::before { content: ""; position: absolute; left: 1px; top: 1px; width: 8px; height: 8px;
             border: 1px solid var(--frame); transform: rotate(45deg) }
.cadeia li::after { content: ""; position: absolute; left: 18px; right: 8px; top: 6px;
             height: 1px; background: var(--line) }
.cadeia li:last-child::after { display: none }

/* A cabeça do painel do vídeo: o nome largo e fino com o fio âmbar à
   esquerda, a função em rótulo à direita, e o fio fino embaixo. */
.cab { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s2) var(--s4);
       flex-wrap: wrap; padding-bottom: var(--s3); border-bottom: 1px solid var(--line) }
.cab h2 { margin: 0; font-size: 17px; font-weight: 300; font-stretch: 125%; letter-spacing: .3em;
          text-transform: uppercase; line-height: 1.2; border-left: 2px solid var(--signal); padding-left: 12px }
.cab > span { font-size: 11px; font-weight: 500; font-stretch: 125%; letter-spacing: .19em;
              text-transform: uppercase; color: var(--muted) }
.signature-wrap { margin: var(--s6) 0 0 }
.signature { width: 260px; max-width: 78%; height: auto; display: block; opacity: .92 }

/* O cartão de acesso é o painel do jogo sobre o mundo: fio apagado, cantos de
   mira (`.mira`), um véu que deixa a onda passar, cabeça com o rótulo. */
.login-card {
  padding: var(--s6); background-color: color-mix(in srgb, var(--surface) 88%, transparent);
  border: 1px solid var(--line-soft); border-radius: var(--radius); box-shadow: var(--shadow);
}
.login-card .cab { margin-bottom: var(--s4) }
/* O rótulo do campo é o cabeçalho de seção do jogo: o fio curto na frente, em
   caixa-alta larga. */
.login-card label > span { display: block; font-size: 11px; font-stretch: 125%; letter-spacing: .16em;
                           text-transform: uppercase; line-height: 1.4;
                           border-left: 2px solid var(--frame); padding-left: 8px }
.login-card input { background: color-mix(in srgb, var(--band) 70%, transparent) }
/* O olho da senha mora dentro do campo, na ponta direita, sem tirar a largura dele. */
.login-card label.senha { position: relative }
.login-card label.senha input { padding-right: 96px }
.ver-senha { position: absolute; right: 1px; bottom: 1px; min-height: 42px; padding: 0 var(--s3);
             border: 0; background: transparent; color: var(--ink-2); font-size: 12px }
/* A barra de entrada: o nome à esquerda e a seta na ponta, como as barras de
   confirmação do jogo. */
.login-card .primary { width: 100%; margin-top: var(--s2); justify-content: space-between;
                       padding-left: var(--s4); font-stretch: 125%; letter-spacing: .19em;
                       text-transform: uppercase; font-size: 12px }
.login-card .primary::after { content: ""; width: 7px; height: 7px; margin-right: 4px;
                       border-top: 1.5px solid; border-right: 1.5px solid; transform: rotate(45deg) }
/* A linha de comandos do pé da tela do jogo. Só diz o que o teclado já faz;
   some no celular, que não tem teclado. */
.atalhos { grid-column: 2; display: flex; align-items: center; justify-content: flex-end;
           gap: var(--s2); margin: calc(var(--s5) * -1) 0 0; font-size: 11px; color: var(--muted) }
.atalhos kbd + span ~ kbd { margin-left: var(--s3) }
kbd { font-family: var(--mono); font-size: 10px; line-height: 1.6; padding: 0 6px; min-width: 22px;
      text-align: center; color: var(--ink-2); border: 1px solid var(--frame) }

/* ------------------------------------------------------------- as telas */
.page { max-width: 1600px; margin: auto; padding: var(--s6) 4% }
/* Inventário e Cadastro: o título de tela das outras páginas, com o rótulo em
   cima e a descrição (explicação, sai na tela enxuta) embaixo do nome. */
#equipamentos-view .tela-titulo > .eyebrow, #manage .tela-titulo > .eyebrow { margin: 0 0 var(--s2) }
#equipamentos-view .tela-titulo .head, #manage .tela-titulo .head { margin-bottom: 0 }
#equipamentos-view .tela-titulo > .explica, #manage .tela-titulo > .explica { margin: var(--s2) 0 0; font-size: 13px }
#equipamentos-view > .card { padding: var(--s2) var(--s4) }
#equipamentos-view > .card > .table-scroll { margin: 0 }
#equipamentos td:first-child, #equipamentos td:nth-child(4) { font-family: var(--mono); font-size: 11.5px }

/* O aviso é a faixa de condição do jogo, como o estado: fio na cor, um véu
   dela, sem caixa. */
.notice {
  border: 0; border-left: 2px solid var(--warn);
  background: color-mix(in srgb, var(--warn) 8%, transparent); color: var(--ink);
  border-radius: var(--radius-sm); padding: 13px var(--s4); font-size: 14px; line-height: 1.6;
  margin-bottom: var(--s4);
}
.notice.error { border-left-color: var(--danger); background: color-mix(in srgb, var(--danger) 9%, transparent) }
.notice.working { border-left-color: var(--accent); background: color-mix(in srgb, var(--accent) 9%, transparent) }

/* Os painéis das abas são os do `.card`: véu, fio apagado e cantos de mira. */
.chart-card, .detail-card {
  background-color: color-mix(in srgb, var(--surface) 90%, transparent);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius); box-shadow: var(--shadow);
}
/* O nome do painel é o cabeçalho de seção do jogo: largo, fino, o fio curto. O
   nome da grandeza (`.chart-top`, `.chart-heading`) é dado, e fica em caixa
   normal. */
.section-heading h2, .section-heading h3, .detail-card > h2, .export-panel h2 {
  font-size: 13px; font-weight: 400; font-stretch: 125%; letter-spacing: .24em;
  text-transform: uppercase; line-height: 1.3; margin: 0 0 var(--s3);
  border-left: 2px solid var(--frame); padding-left: 10px }
.results { min-width: 0; outline: none }
.results[aria-busy="true"] .chart-card { border-color: var(--accent) }
/* Atualização sobre um recorte já na tela: ele continua legível e o aviso diz por quê. */
.results[aria-busy="true"] .stats, .results[aria-busy="true"] .chart { opacity: .55 }

/* ------------------------------------------------------------ cartão série */
.chart-card { padding: var(--s5) }
.chart-heading, .section-heading { display: flex; align-items: start; justify-content: space-between; gap: var(--s4) }
.chart-heading h2 { font-size: 24px; font-weight: 300; font-stretch: 112%; margin-bottom: var(--s2) }
.chart-heading .eyebrow { margin-bottom: 5px }
.badge {
  background: var(--accent-soft); border: 1px solid var(--line); color: var(--ink-2);
  padding: 5px var(--s2); border-radius: 0; font-size: 11px; font-weight: 500;
  font-stretch: 125%; letter-spacing: .12em; text-transform: uppercase;
  max-width: 180px; line-height: 1.5; text-align: center;
}
.stats {
  display: grid; grid-template-columns: 1.25fr 1fr 1fr; gap: var(--s5);
  margin: var(--s5) 0 var(--s3); padding: var(--s5) 0;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
/* As três leituras do recorte falam como as do Início: rótulo largo, número em
   mono fina. */
.stats span { display: block; font-size: 11px; font-weight: 500; font-stretch: 125%;
              letter-spacing: .14em; text-transform: uppercase; color: var(--muted); line-height: 1.4 }
.stats strong {
  display: block; font-family: var(--mono); font-weight: 300; font-size: 26px;
  margin: var(--s2) 0 5px; color: var(--ink); font-variant-numeric: tabular-nums;
}
.stats small { display: block; font-size: 10px; color: var(--muted); line-height: 1.6 }

.chart { min-height: 300px; overflow: hidden; border-radius: var(--radius-sm) }
.chart svg { display: block; max-width: 100%; touch-action: pan-y }
/* Eixo em mono, como o número de todo o resto da tela. */
.chart text { font-family: var(--mono); fill: var(--muted); font-size: 10px; font-variant-numeric: tabular-nums }
.chart .axis-label { fill: var(--ink-2); font-weight: 500; letter-spacing: .4px }
.chart .grid { stroke: var(--grid); stroke-width: 1 }
.chart .axis { stroke: var(--axis); stroke-width: 1 }
.chart .series { fill: none; stroke: var(--series); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round }
.chart .points { fill: none; stroke: var(--series); stroke-width: 2.4; stroke-linecap: round }
.chart .points.dense { stroke-width: 1.2 }
/* Série B: traço interrompido e marca quadrada. Some a cor, a série continua
   identificável — a legenda usa exatamente estes dois traços. */
.chart .series.b { stroke: var(--series-b); stroke-dasharray: 7 4 }
.chart .points.b { stroke: var(--series-b); stroke-linecap: square }
.chart .cursor-dot.b { stroke: var(--series-b) }
.chart .right-axis { fill: var(--series-b) }
.chart .axis-label.b { fill: var(--series-b) }
.chart .rail { stroke: var(--line); stroke-width: 1 }
.chart .rail-key { fill: var(--muted); font-size: 11px; font-weight: 750 }
.chart .flags { stroke: var(--warn); stroke-width: 1.4; fill: var(--warn-bg) }
.chart .gap { fill: url(#gap-hatch); stroke: var(--gap-line); stroke-width: 1 }
.chart .gap-bg { fill: var(--gap-fill) }
.chart .gap-hatch { stroke: var(--gap-line); stroke-width: 1 }
.chart .gap-label { fill: var(--muted); font-family: var(--sans); font-size: 9px; font-weight: 500;
                    font-stretch: 125%; letter-spacing: .14em; text-transform: uppercase }
.chart .cursor { stroke: var(--accent); stroke-width: 1; stroke-dasharray: 4 3 }
.chart .cursor-dot { fill: var(--surface); stroke: var(--series); stroke-width: 2.4 }
.chart:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px }

/* ------------------------------------------------------------------ legenda */
.legend { list-style: none; margin: var(--s3) 0 0; padding: 0; display: grid; gap: var(--s2) }
.legend li { display: flex; align-items: center; gap: var(--s3); min-width: 0 }
.letter {
  flex: none; width: 22px; height: 22px; border-radius: 0;
  display: grid; place-content: center; font-family: var(--mono); font-size: 11px; font-weight: 500;
  background: var(--accent-soft); color: var(--ink); border: 1px solid var(--line);
}
.swatch { flex: none; width: 34px; height: 12px }
.swatch .series { stroke: var(--series); stroke-width: 2; fill: none }
.swatch .series.b { stroke: var(--series-b); stroke-dasharray: 7 4 }
.swatch .mark { fill: var(--surface); stroke: var(--series); stroke-width: 2 }
.swatch .mark.b { fill: var(--surface); stroke: var(--series-b); stroke-width: 2 }
.legend-text { min-width: 0 }
.legend-text strong { display: block; font-size: 13px; font-weight: 600 }
.legend-text small { display: block; font-size: 11px; color: var(--muted); line-height: 1.5 }

.readout {
  min-height: 20px; margin: var(--s2) 0 0; font-size: 12px; font-weight: 500;
  color: var(--ink-2); font-variant-numeric: tabular-nums;
}
.chart-footer {
  border-top: 1px solid var(--line); margin-top: var(--s4); padding-top: var(--s4);
  display: flex; gap: var(--s4); flex-wrap: wrap; font-size: 10px; align-items: center; color: var(--muted);
}
.chart-footer #loaded-at { margin-left: auto }
.key { display: inline-block; width: 13px; height: 3px; vertical-align: middle; margin-right: 5px }
.series-key { background: var(--series) }
.gap-key { background: repeating-linear-gradient(45deg, var(--gap-fill) 0 3px, var(--gap-line) 3px 4.4px);
  border: 1px solid var(--gap-line); height: 10px }
.flag-key { background: var(--warn); width: 7px; height: 7px; transform: rotate(45deg) }

/* ------------------------------------------- estados: vazio, erro, carregando */
.state {
  display: grid; place-content: center; justify-items: center; gap: var(--s2);
  min-height: 280px; text-align: center; color: var(--muted);
  line-height: 1.7; font-size: 14px; padding: var(--s5); margin: 0;
}
.state .icon { width: 30px; height: 30px; color: var(--axis) }
.state strong { display: block; color: var(--ink); font-size: 15px; font-weight: 600 }
.state span { max-width: 44ch; display: block }

/* Carregamento sem animação (`docs/dashboard.md`): blocos estáticos e texto. */
.loading { display: grid; gap: var(--s3); min-height: 280px; align-content: center; padding: var(--s5) 0 }
.bar { background: var(--skeleton); border-radius: 4px; height: 12px }
.bar.tall { height: 150px; border-radius: var(--radius-sm) }
.bar.w60 { width: 60% } .bar.w35 { width: 35% } .bar.w80 { width: 80% }
.loading p { margin: 0; font-size: 13px; color: var(--muted); text-align: center }

/* -------------------------------------------------------------- exportação */
.export-panel {
  margin: var(--s4) 0; display: flex; flex-wrap: wrap; gap: var(--s4);
  justify-content: space-between; align-items: center;
  padding: var(--s5); background-color: color-mix(in srgb, var(--surface) 90%, transparent);
  border: 1px solid var(--line-soft); border-radius: var(--radius);
}
.export-panel h3 { margin-bottom: var(--s1) }
.export-panel p { margin-bottom: 0 }
.export-actions { display: flex; flex-wrap: wrap; gap: var(--s2) }
#export-split { width: 100%; margin: var(--s2) 0 0 }
#downloads { width: 100%; border-top: 1px solid var(--line); padding-top: var(--s3); display: flex; flex-wrap: wrap; gap: var(--s2) }
#export-info { width: 100%; margin-bottom: var(--s2) }

/* ------------------------------------------------------ tabelas e detalhes */
.detail-card { padding: var(--s5); margin-bottom: var(--s4) }
.table-scroll { overflow: auto; max-height: 430px; overscroll-behavior: contain; margin: 0 calc(var(--s2) * -1) }
table { width: 100%; border-collapse: collapse; text-align: left; font-size: 12px }
/* O cabeçalho de coluna é o da lista do Início: caixa-alta larga e miúda. */
th {
  font-size: 11px; color: var(--muted); font-weight: 500; font-stretch: 125%;
  text-transform: uppercase; letter-spacing: .14em; position: sticky; top: 0;
  background: var(--surface); z-index: 1;
}
td, th { padding: 11px var(--s2); border-bottom: 1px solid var(--line-soft); vertical-align: top; line-height: 1.5 }
tbody tr:hover td { background: var(--surface-2) }
td:first-child { white-space: nowrap }
/* Coluna da série: a letra identifica, a cor apenas reforça. */
.side { font-weight: 750; width: 34px }
.side-a { color: var(--series) }
.side-b { color: var(--series-b) }
td:nth-child(2) { font-variant-numeric: tabular-nums; white-space: nowrap; font-weight: 600 }
/* Na via exata, horário e valor são número: mono. Natureza e qualidade, as
   duas últimas colunas, são palavra. */
#observations td { font-family: var(--mono); font-weight: 400 }
#observations td:nth-last-child(-n+2) { font-family: var(--sans) }
td summary { color: var(--accent); font-weight: 600 }
td details p { min-width: 170px; font-size: 11px; margin: var(--s2) 0; overflow-wrap: anywhere; font-family: var(--mono) }
.pager { display: flex; justify-content: flex-end; gap: var(--s2); margin-top: var(--s3) }

.bottom-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); align-items: start }
.bottom-grid .detail-card { min-width: 0 }
dl { margin-bottom: 0; font-size: 12px; line-height: 1.6 }
dt { color: var(--muted); font-size: 11px; font-weight: 500; font-stretch: 125%;
     text-transform: uppercase; letter-spacing: .14em; margin-top: var(--s3) }
dd { margin: 3px 0 0; overflow-wrap: anywhere }
.interval, .event { padding: var(--s3) 0; border-bottom: 1px solid var(--line-soft); font-size: 12px; line-height: 1.6; overflow-wrap: anywhere }
.interval:last-child, .event:last-child { border-bottom: 0 }
.interval strong, .event strong { display: block; font-size: 12px }
.interval small, .event small { color: var(--muted) }

/* Critérios de ruído (Fase 7c, etapa D): duas colunas, uma por norma. */
#crit-card { margin-top: var(--s4) }
#crit-edit { margin-left: auto }
.crit-cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); margin-top: var(--s3) }
/* Uma coluna por norma, com o fio do estado na entrada: a escala de risco da
   3R (âmbar no nível de ação, vermelho no limite) e um véu dela. */
.crit-col { border: 1px solid var(--line-soft); border-left: 2px solid var(--frame); border-radius: var(--radius-sm); padding: var(--s4); min-width: 0 }
.crit-col.crit-acao { border-left-color: var(--warn); background: color-mix(in srgb, var(--warn) 8%, transparent) }
.crit-col.crit-limite { border-left-color: var(--danger); background: color-mix(in srgb, var(--danger) 9%, transparent) }
.crit-col h3 { margin: 0 0 var(--s3); font-size: 14px; font-weight: 400; font-stretch: 112% }
.crit-col span { display: block; font-size: 11px; font-weight: 500; font-stretch: 125%;
                 letter-spacing: .14em; text-transform: uppercase; color: var(--muted) }
.crit-col strong { display: block; font-family: var(--mono); font-size: 23px; font-weight: 300; margin: 3px 0 var(--s3); font-variant-numeric: tabular-nums }
.crit-estado { font-weight: 650; font-size: 12px; margin: 0 0 var(--s2) }
.crit-acao .crit-estado { color: var(--warn) }
.crit-limite .crit-estado { color: var(--danger) }
.crit-form { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); margin-top: var(--s4) }
.crit-form fieldset { display: grid; gap: var(--s2); min-width: 0 }
.crit-acoes { grid-column: 1 / -1; display: flex; gap: var(--s2) }

/* ------------------------------------------------------------ adaptações */
@media (max-width: 1050px) {
  .chart-card { padding: var(--s4) }
  .bottom-grid { grid-template-columns: 1fr }
  .stats { gap: var(--s3) }
  .stats strong { font-size: 23px }
  .chart-heading { flex-wrap: wrap }
  .login-layout { gap: var(--s6) }
}
@media (max-width: 760px) {
  body { font-size: 14px }
  .crit-cols, .crit-form { grid-template-columns: 1fr }
  .topbar { padding: 10px 5%; gap: var(--s2) }
  .brand { min-height: 44px }
  .brand-mark { width: 28px; height: 28px }
  .brand-name { font-size: 12px; letter-spacing: .2em; padding-left: 10px }
  .brand-name small { display: none }
  .topbar-end { gap: var(--s2) }
  .account { font-size: 11px; gap: var(--s2) }
  #who { max-width: 108px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
  .account button { padding: 8px var(--s3) }
  #local-label { display: none }

  .hud { display: none }
  .login-layout { display: block; margin: var(--s4) auto; padding: var(--s5) var(--s4) }
  .tela-titulo h1 { font-size: 30px }
  .intro { padding-top: var(--s5) }
  .intro > p:not(.signature-wrap) { font-size: 14px }
  .cadeia, .atalhos { display: none }
  .signature-wrap { margin-top: var(--s5) }
  .signature { width: 230px }
  .login-card { padding: var(--s5); margin-top: var(--s5) }

  .page { padding: var(--s5) 4% }
  .export-actions { width: 100% }
  .export-actions button { flex: 1 1 auto }
  .chart-heading h2 { font-size: 1.25rem }
  /* Os três números do recorte em duas colunas: o último reporte na linha de cima, inteiro,
     e as duas contagens embaixo. Em três colunas o título quebrava em três linhas e o
     terceiro número encostava na borda (§54). */
  .stats { gap: var(--s3) var(--s2); margin-top: var(--s3); padding: var(--s3) 0;
           grid-template-columns: 1fr 1fr }
  .stats > :first-child { grid-column: 1 / -1 }
  .stats strong { font-size: 21px }
  .stats span { font-size: 11px }
  .stats small { font-size: 9px }
  .chart { min-height: 260px }
  .state, .loading { min-height: 240px }
  .chart-footer { gap: 10px; line-height: 1.6 }
  .chart-footer #loaded-at { margin-left: 0; width: 100% }
  .detail-card, .export-panel { padding: var(--s4) }
  button { touch-action: manipulation }
}

/* Sem animação nesta tela; a regra existe para conter dependências futuras. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important }
}

/* ==========================================================================
 * Cadastro — frente 4
 *
 * Nenhuma cor nova: só os tokens que os temas já redefinem. Alvos de toque em
 * 44 px e a grade que vira coluna única a 760 px, como o resto da tela.
 * ======================================================================== */
.manage-grid { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: var(--s5);
               align-items: start }
.resource-nav ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px }
/* O recurso aberto é a linha escolhida do jogo: fio teal na entrada e um véu,
   como a linha da lista do Início. A contagem é número: mono. */
.resource-tab { display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
                width: 100%; min-height: 44px; padding: 0 var(--s3); border: 0;
                border-left: 2px solid transparent;
                border-radius: var(--radius-sm); background: transparent; color: var(--ink-2);
                font: inherit; font-weight: 500; text-align: left; cursor: pointer }
.resource-tab:hover { background: var(--surface-2) }
.resource-tab[aria-current="true"] { border-left-color: var(--accent); color: var(--accent);
                                     background: color-mix(in srgb, var(--accent) 10%, transparent) }
.resource-tab .count { min-width: 24px; padding: 2px 7px; border-radius: 0;
                       background: var(--sunken); color: var(--muted); font-family: var(--mono);
                       font-size: .78rem; font-variant-numeric: tabular-nums; text-align: center }
.resource-tab[aria-current="true"] .count { background: transparent; color: var(--accent) }

.manage-panel { background-color: color-mix(in srgb, var(--surface) 90%, transparent);
                border: 1px solid var(--line-soft);
                border-radius: var(--radius); padding: var(--s5); box-shadow: var(--shadow) }
.record-layout { display: grid; grid-template-columns: 280px minmax(0, 1fr); gap: var(--s5);
                 align-items: start; margin-top: var(--s4) }
.record-list-wrap { display: flex; flex-direction: column; gap: var(--s3) }
#record-search { width: 100%; min-height: 44px }
.record-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column;
               gap: 2px; max-height: 480px; overflow: auto; overscroll-behavior: contain }
/* `justify-content` desfaz o centro do `button` global: o item curto nascia
   centralizado e o que quebra em duas linhas, à esquerda. */
.record-item { width: 100%; min-height: 44px; padding: var(--s2) var(--s3); border: 0;
               border-left: 2px solid transparent; justify-content: flex-start;
               border-radius: var(--radius-sm); background: transparent; color: var(--ink);
               font: inherit; font-weight: 400; text-align: left; cursor: pointer; word-break: break-word }
.record-item:hover { background: var(--surface-2) }
.record-item[aria-current="true"] { border-left-color: var(--accent); color: var(--accent);
                                    background: color-mix(in srgb, var(--accent) 10%, transparent) }

.record-form { display: flex; flex-direction: column; gap: var(--s4);
               border-left: 1px solid var(--line-soft); padding-left: var(--s5) }
#record-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: var(--s4) }
/* Sem a margem global do `label`: ela somava 32 px ao vão da grade. */
#record-fields label { display: flex; flex-direction: column; gap: 6px; margin: 0; font-weight: 500; color: var(--ink-2) }
#record-fields label.check { flex-direction: row; align-items: center; font-weight: 400 }
/* O `textarea` não entra na regra global de `input` e `select`: sem isto ele
   nascia com o fundo claro do navegador, no meio do formulário escuro. */
#record-fields textarea { resize: vertical; min-height: 44px; font: inherit; font-size: 13px;
                          padding: 10px 11px; border: 1px solid var(--line); border-radius: var(--radius-sm);
                          background: var(--sunken); color: var(--ink) }
/* Tela enxuta: a ajuda de cada campo do Cadastro volta com "Mostrar detalhes". */
body:not(.detalhes) #record-fields .small.muted { display: none }
#record-fields .small { font-weight: 400 }
#record-fields input:disabled, #record-fields select:disabled, #record-fields textarea:disabled {
  background: var(--sunken); color: var(--muted); cursor: not-allowed }
.req { font-size: .74rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
       color: var(--muted) }
.record-actions { display: flex; flex-wrap: wrap; gap: var(--s3) }
#record-version { font-variant-numeric: tabular-nums; word-break: break-all }

@media (max-width: 980px) {
  .manage-grid { grid-template-columns: minmax(0, 1fr) }
  .resource-nav ul { flex-direction: row; flex-wrap: wrap }
  .resource-tab { width: auto }
}
@media (max-width: 760px) {
  .record-layout { grid-template-columns: minmax(0, 1fr) }
  .record-form { border-left: 0; padding-left: 0; border-top: 1px solid var(--line-soft); padding-top: var(--s4) }
  .record-list { max-height: 260px }
  .manage-panel { padding: var(--s4) }
}
.field-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s2) }

/* ==========================================================================
 * Navegação por seção e o arranjo do Tempo real — bloco 1
 *
 * Nenhuma cor nova: só os tokens que os dois temas já redefinem. Alvo de toque
 * em 44 px, 390 px sem transbordo horizontal, e cor nunca como única
 * indicação — o texto de cada carimbo diz o mesmo fato nos três graus.
 * ======================================================================== */
.mainnav { display: flex; gap: var(--s1); flex: 1 1 auto; min-width: 0 }
/* Item da barra: rótulo largo em caixa-alta; o aberto ganha o fio âmbar embaixo. */
.mainnav a { display: inline-flex; align-items: center; gap: var(--s2); min-height: 44px;
             padding: 9px var(--s4); border-radius: var(--radius-sm);
             text-decoration: none; color: var(--muted); font-size: 11.5px;
             font-weight: 500; font-stretch: 125%; letter-spacing: .16em;
             text-transform: uppercase; white-space: nowrap }
.mainnav a:hover { color: var(--ink) }
.mainnav a[aria-current="page"] { color: var(--ink); box-shadow: inset 0 -2px 0 var(--signal) }

.tabpanel { outline: none }
.tabpanel:focus-visible { outline: 2px solid var(--focus); outline-offset: 6px }

/* A migalha é o caminho miúdo no alto das telas do jogo: caixa-alta larga. */
.crumb { font-size: 11px; font-weight: 500; font-stretch: 125%; letter-spacing: .14em;
         text-transform: uppercase; color: var(--muted); margin-bottom: 10px }
.crumb a { color: var(--accent); text-decoration: none; font-weight: 500 }
.crumb i { font-style: normal; margin: 0 8px; opacity: .45 }
.head { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; margin-bottom: var(--s4) }
.head h1 { font-size: 26px; margin: 0 }
.head .right { margin-left: auto; display: flex; gap: var(--s2); align-items: center }
.head .right .inline { margin: 0; font-size: 11px; font-weight: 650; color: var(--muted) }
.head .right select { min-width: 190px }

.chips { display: flex; gap: var(--s2); flex-wrap: wrap }
/* O estado é a faixa de condição do Death Stranding: fio curto na cor do
   estado, um véu dela atrás, sem caixa. A palavra diz o estado; a cor só repete. */
.chip { font-size: 11.5px; font-weight: 500; padding: 4px 10px 4px 8px; border-radius: 0;
        border: 0; border-left: 2px solid var(--frame);
        background: color-mix(in srgb, var(--ink) 5%, transparent); color: var(--ink-2) }
.chip.ok { color: var(--accent); border-left-color: var(--accent);
           background: color-mix(in srgb, var(--accent) 11%, transparent) }
.chip.warn { color: var(--warn); border-left-color: var(--warn);
             background: color-mix(in srgb, var(--warn) 10%, transparent) }
.chip.danger { color: var(--danger); border-left-color: var(--danger);
               background: color-mix(in srgb, var(--danger) 10%, transparent) }

/* As abas da sonda são links: a aba está no endereço, e voltar volta a aba. */
.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--line);
        margin-bottom: var(--s5); overflow-x: auto; scrollbar-width: none }
.tabs::-webkit-scrollbar { display: none }
.tabs a { display: inline-flex; align-items: center; min-height: 44px;
          padding: 12px var(--s4); font-size: 11px; font-weight: 500; font-stretch: 125%;
          letter-spacing: .16em; text-transform: uppercase; color: var(--muted);
          text-decoration: none; border-bottom: 2px solid transparent;
          margin-bottom: -1px; white-space: nowrap }
.tabs a:hover { color: var(--ink) }
.tabs a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--signal) }
/* Dentro do título de tela as abas assentam no fio dele: sem fio próprio, e a
   margem negativa desconta o recuo e o fio do título, para o traço âmbar da
   aba aberta cair sobre a linha. */
.tela-titulo .tabs { border-bottom: 0; margin: var(--s2) 0 calc(-1 * var(--s4) - 1px) }
.tela-titulo .tabs a { margin-bottom: 0 }

/* O cartão é o painel do jogo sobre o mundo, como o de acesso: fio apagado,
   cantos de mira (a regra da `.mira` vale para todo `.card`) e um véu que
   deixa a onda passar. */
.card { background-color: color-mix(in srgb, var(--surface) 90%, transparent);
        border: 1px solid var(--line-soft);
        border-radius: var(--radius); box-shadow: var(--shadow) }
.pad { padding: var(--s5) }
/* O rótulo de leitura: caixa-alta larga, com entreletra menor que a do
   `.eyebrow`, porque a célula é estreita. */
.label { display: block; font-size: 11px; font-weight: 500; font-stretch: 125%;
         letter-spacing: .14em; color: var(--muted); text-transform: uppercase }
.tag { font-size: 11px; font-weight: 500; font-stretch: 125%; letter-spacing: .12em; border-radius: 0;
       padding: 1px 5px; border: 1px solid; white-space: nowrap }
.tag.calc { color: var(--warn); background: color-mix(in srgb, var(--warn) 10%, transparent);
            border-color: color-mix(in srgb, var(--warn) 45%, transparent) }

/* Faixa de estado e faixa de leituras: uma célula por coluna, divisor fino.
   A quantidade de colunas vem do JS, porque depende do que a sonda reportou.
   O número é mono, como em todo o resto. */
.estado, .leituras { display: grid; margin-bottom: var(--s4) }
.estado > div { padding: 14px var(--s5); border-right: 1px solid var(--line-soft) }
.estado > div:last-child { border-right: 0 }
.estado strong { display: block; font-family: var(--mono); font-size: 19px; font-weight: 400; margin-top: 3px }
.estado small { display: block; color: var(--muted); font-size: 11.5px }
.leitura { padding: 13px var(--s4); border-right: 1px solid var(--line-soft) }
.leitura:last-child { border-right: 0 }
.leitura .label { min-height: 24px; line-height: 1.25 }
.leitura strong { display: block; font-family: var(--mono); font-size: 20px; font-weight: 400; margin-top: 4px; white-space: nowrap }
.leitura strong small { font-family: var(--sans); font-size: 12px; font-weight: 500; color: var(--muted) }
/* A hora ou a conta de cada leitura do ruído (§42), como nas essenciais. */
.leitura em { display: block; font-style: normal; font-family: var(--mono); font-size: 10px;
              color: var(--muted); margin-top: 3px }
#live-omitted { margin: 0 0 var(--s4) }
.dose-card, .naw-card { margin-bottom: var(--s4) }
#exp-dose-edit { white-space: nowrap; display: inline-flex; align-items: center; min-height: 44px }
/* A barra da dose (§42): 0 a 200 %, na cor do estado, com o nível de ação e o
   limite marcados. O número fica em cima dela; a barra só dá a escala. */
.dose-barra { position: relative; height: 6px; margin: -6px 0 26px; background: var(--sunken);
              box-shadow: inset 0 0 0 1px var(--line-soft) }
.dose-barra::before { content: ""; position: absolute; inset: 0 auto 0 0; width: var(--dose);
                      background: var(--accent) }
.crit-acao .dose-barra::before { background: var(--warn) }
.crit-limite .dose-barra::before { background: var(--danger) }
.dose-barra i { position: absolute; top: -4px; bottom: -4px; width: 1px; background: var(--frame) }
.dose-barra i::after { content: attr(data-r); position: absolute; top: 15px; transform: translateX(-50%);
                       font: 9.5px var(--mono); color: var(--muted); white-space: nowrap }

/* Cartão principal do Tempo real: gráfico grande e o valor em número grande. */
.chartcard { margin-bottom: var(--s4) }
.chart-top { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap; margin-bottom: var(--s4) }
/* O nome da grandeza é dado: caixa normal, fina, com o fio curto. */
.chart-top h2 { font-size: 20px; font-weight: 300; font-stretch: 112%; margin: 0;
                border-left: 2px solid var(--frame); padding-left: 10px }
.seletor { display: flex; gap: 6px; align-items: center; margin-left: auto;
           max-width: 100%; overflow-x: auto; scrollbar-width: none }
.seletor::-webkit-scrollbar { display: none }
/* A grandeza escolhida é a faixa de condição teal, como o estado: nada de
   bloco chapado. */
.seletor button { flex: none; font-size: 12px; font-weight: 500; white-space: nowrap;
                  padding: 8px var(--s3); border-radius: var(--radius-sm); color: var(--muted);
                  border-color: var(--line-soft) }
.seletor button.on { color: var(--accent); border-color: var(--line); border-left: 2px solid var(--accent);
                     background: color-mix(in srgb, var(--accent) 11%, transparent) }
.principal { display: grid; grid-template-columns: minmax(0, 1fr) 215px; gap: var(--s5) }
.principal-chart { min-width: 0 }
.live-chart { min-height: 250px }
.live-chart svg { display: block; width: 100%; height: 250px }
.live-chart .grid { stroke: var(--grid); stroke-width: 1 }
.live-chart .series { fill: none; stroke: var(--series); stroke-width: 1.6;
                      stroke-linejoin: round; stroke-linecap: round }
.live-chart .points { fill: none; stroke: var(--series); stroke-width: 2.4; stroke-linecap: round }
.live-chart .points.dense { stroke-width: 1.2 }
.live-chart .flags { stroke: var(--warn); stroke-width: 1.4; fill: var(--warn-bg) }
.live-chart .gap { fill: url(#live-hatch); stroke: var(--gap-line); stroke-width: 1 }
.live-chart .gap-bg { fill: var(--gap-fill) }
.live-chart .gap-hatch { stroke: var(--gap-line); stroke-width: 1 }
.eixo { position: relative; height: 17px; margin-top: 6px }
.eixo span { position: absolute; top: 0; transform: translateX(-50%); white-space: nowrap }
.eixo span.start { transform: none }
.eixo span.end { transform: translateX(-100%) }
.agora { display: flex; flex-direction: column; justify-content: center; min-width: 0;
         border-left: 1px solid var(--line-soft); padding-left: var(--s5) }
/* O número de agora em mono fina. A mono é mais larga que a Archivo: em 42 px
   "1013,4" e o "hPa" ainda cabem nos 191 px da coluna. */
.agora .valor { font-family: var(--mono); font-size: 42px; font-weight: 300; line-height: 1;
                margin: 6px 0 0; white-space: nowrap }
.agora .valor span { font-family: var(--sans); font-size: 18px; font-weight: 500; color: var(--muted) }
.agora .quando { font-family: var(--mono); font-size: 11.5px; color: var(--muted); margin: var(--s3) 0 0 }
.agora .faixa { font-family: var(--mono); font-size: 11.5px; color: var(--muted); margin: 14px 0 0;
                padding-top: var(--s3); border-top: 1px solid var(--line-soft) }
.rodape { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s4) }
.rodape p { margin: 6px 0 0 }

@media (max-width: 1050px) {
  .principal { grid-template-columns: minmax(0, 1fr) 180px; gap: var(--s4) }
  .agora .valor { font-size: 36px }
  .agora { padding-left: var(--s4) }
  .estado > div { padding: 13px var(--s4) }
}
/* ---- celular: 390 px sem transbordo horizontal ---- */
@media (max-width: 760px) {
  /* Sem `overflow` aqui, e o motivo é o menu. A barra rolava de lado para
     caber, e container que rola recorta o que sai dele: `Projetos` e
     `Dispositivos` abriam uma caixa de 252 × 125 px que nascia invisível e
     inclicável em toda largura até 900 px. Quem faz caber agora é o próprio
     item — ver `max-width: 420px`, onde o ícone decorativo sai e sobra a
     palavra. O `flex-wrap` é só a rede: com o ícone fora, os três cabem até
     320 px. */
  .mainnav { order: 3; flex: 1 1 100%; width: 100%; margin: 0; padding-bottom: 2px;
             flex-wrap: wrap }
  .mainnav a { padding: 9px var(--s3); font-size: 13.5px }
  .crumb { font-size: 11.5px }
  .crumb a { display: inline-block; padding: 15px 0; margin: -15px 0 }
  .head { gap: var(--s2); margin-bottom: var(--s3) }
  .head h1 { font-size: 20px; width: 100% }
  .head .right { margin-left: 0; width: 100%; flex-wrap: wrap }
  .head .right select { flex: 1 1 auto; min-width: 0 }
  .tabs { margin-bottom: var(--s4) }
  .tabs a { padding: 12px var(--s3); font-size: 13.5px }
  .principal { grid-template-columns: minmax(0, 1fr); gap: var(--s3) }
  /* O valor sobe para cima do gráfico: é o que se lê primeiro no telefone. */
  .agora { order: -1; flex-direction: row; align-items: baseline; flex-wrap: wrap;
           gap: var(--s3); border-left: 0; border-bottom: 1px solid var(--line-soft);
           padding: 0 0 var(--s3) }
  .agora .label { width: 100% }
  .agora .valor { font-size: 44px; margin: 0 }
  .agora .quando, .agora .faixa { margin: 0; padding: 0; border: 0 }
  .agora .faixa { width: 100% }
  .live-chart { min-height: 190px }
  .live-chart svg { height: 190px }
  .chart-top { gap: var(--s2); margin-bottom: var(--s3) }
  .chart-top h2 { font-size: 16px; width: 100% }
  .seletor { margin-left: 0; width: 100% }
  .seletor button { min-height: 44px }
  .estado, .leituras { grid-template-columns: 1fr 1fr !important }
  .estado > div, .leitura { border-bottom: 1px solid var(--line-soft) }
  .estado > div:nth-child(2n), .leitura:nth-child(2n) { border-right: 0 }
  .estado > div:nth-last-child(-n+2), .leitura:nth-last-child(-n+2) { border-bottom: 0 }
  /* As cinco leituras do ruído (§42): a quinta ocupa a fileira, e não fica órfã. */
  .leitura:nth-child(odd):last-child { grid-column: 1 / -1; border-top: 1px solid var(--line-soft);
                                       margin-top: -1px }
  /* O cabeçalho da dose: título, janela e o link em linhas próprias. */
  .dose-card .section-heading { flex-wrap: wrap }
  .dose-card .section-heading > span { order: 3; flex-basis: 100% }
  .estado strong { font-size: 17px }
  .leitura .label { min-height: 32px; font-size: 11px; letter-spacing: .9px }
  .leitura strong { font-size: 20px }
  .pad { padding: var(--s4) }
  .rodape { grid-template-columns: minmax(0, 1fr) }
}

/* ==========================================================================
 * Faixa de filtros, zoom, qualidade, configuração e mapa
 *
 * A faixa de filtros ficava numa coluna de 276 px à esquerda, que tirava o
 * gráfico do centro e comia um terço da largura. Agora ela é horizontal, em
 * uma linha, com a segunda atrás de um botão. Nenhuma cor nova.
 * ======================================================================== */
/* A faixa de filtros é um painel como os outros: véu, fio apagado, mira. */
.filterbar { background-color: color-mix(in srgb, var(--surface) 90%, transparent);
             border: 1px solid var(--line-soft);
             border-radius: var(--radius); box-shadow: var(--shadow);
             padding: var(--s3) var(--s4); margin-bottom: var(--s4) }
.filterbar-line { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--s3) }
.filterbar-line + .filterbar-line { margin-top: var(--s3); padding-top: var(--s3);
                                    border-top: 1px solid var(--line-soft) }
.filterbar label { margin: 0; display: flex; flex-direction: column; gap: 4px;
                   font-size: 11px; font-weight: 500; font-stretch: 125%; color: var(--muted);
                   text-transform: uppercase; letter-spacing: .14em; min-width: 0 }
.filterbar label.check { flex-direction: row; align-items: center; gap: var(--s2);
                         text-transform: none; letter-spacing: 0; font-size: 12.5px;
                         align-self: center; min-height: 44px }
.filterbar label .muted { text-transform: none; letter-spacing: 0; font-weight: 500;
                          font-stretch: 100%; font-size: 11px }
.filterbar input:not([type="checkbox"]), .filterbar select { min-width: 168px }
.filterbar-actions { display: flex; gap: var(--s2); align-items: center; margin-left: auto }
.filterbar-note { flex: 1 1 100%; margin: 0; text-transform: none; letter-spacing: 0 }
#filter-state { margin: var(--s2) 0 0 }
/* Escondido por aba. Separado de `hidden`, que é do `syncCompare`: dois
   mecanismos no mesmo atributo desfariam um ao outro. */
.off-tab { display: none !important }

.zoombar { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
           margin: 0 0 var(--s3) }
.zoombar span { flex: 1 1 220px; min-width: 0 }
.zoombar button { margin-left: auto }
.chart svg, .live-chart svg { touch-action: pan-y; cursor: crosshair }
.chart .selection, .live-chart .selection { fill: var(--accent); opacity: .16 }

.rowbar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--s3);
          margin: var(--s3) 0; padding-bottom: var(--s3); border-bottom: 1px solid var(--line-soft) }
.rowbar label { margin: 0; display: flex; flex-direction: column; gap: 4px;
                font-size: 11px; font-weight: 500; font-stretch: 125%; color: var(--muted);
                text-transform: uppercase; letter-spacing: .14em }
.rowbar label.check { flex-direction: row; align-items: center; gap: var(--s2);
                      text-transform: none; letter-spacing: 0; font-size: 12.5px;
                      min-height: 44px }
.section-heading > span { min-width: 0 }
.rowbar input:not([type="checkbox"]), .rowbar select { min-width: 160px }
.rowbar button { margin-left: auto }

/* Linha do tempo da cobertura. Textura na lacuna: cor nunca sozinha. */
.track { margin-top: var(--s3); border-radius: var(--radius-sm); overflow: hidden;
         border: 1px solid var(--line) }
.track svg { display: block; width: 100%; height: 34px }
.track .track-blank { fill: var(--sunken) }
.track .track-cover { fill: var(--accent-soft); stroke: var(--accent); stroke-width: .5 }
.track .track-gap { fill: url(#track-hatch); stroke: var(--gap-line); stroke-width: .5 }
.track .gap-bg { fill: var(--gap-fill) }
.track .gap-hatch { stroke: var(--gap-line); stroke-width: 1 }
.legend-flat { list-style: none; margin: var(--s3) 0 0; padding: 0;
               display: flex; flex-wrap: wrap; gap: var(--s4); font-size: 12px; color: var(--muted) }
.legend-flat li { display: flex; align-items: center; gap: 6px }
.cover-key { background: var(--accent-soft); border: 1px solid var(--accent); height: 8px; width: 14px }
.blank-key { background: var(--sunken); border: 1px solid var(--line); height: 8px; width: 14px }

/* Eixo do gráfico do Tempo real: as mesmas convenções da aba Séries. */
.live-chart text { font-family: var(--mono); fill: var(--muted); font-size: 10px;
                   font-variant-numeric: tabular-nums }
.live-chart .axis { stroke: var(--axis); stroke-width: 1 }
.live-chart .axis-label { fill: var(--ink-2); font-weight: 500; letter-spacing: .4px }
.live-chart .cursor { stroke: var(--accent); stroke-width: 1; stroke-dasharray: 4 3 }
.live-chart .cursor-dot { fill: var(--surface); stroke: var(--series); stroke-width: 2.4 }
.live-chart:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px }

/* Configuração: grade de 12 colunas, e a largura sai do tipo do campo.
   Até 2026-09-07 era `auto-fit` com mínimo de 230 px, e todo campo saía com
   239 px — `Altura acima do solo (m)`, que guarda `15`, recebia o mesmo espaço
   de `Nota sobre a altura`, que guarda três linhas de prosa. Um desperdiçava, o
   outro truncava em 43 px. Tipo novo no `crud.mjs` sem largura aqui é pego por
   `test_a_largura_do_campo_sai_do_tipo_dele`. */
.config-fields { display: grid; grid-template-columns: repeat(12, 1fr);
                 gap: var(--s4); margin: var(--s4) 0 }
.config-fields label { display: flex; flex-direction: column; gap: 6px;
                       font-weight: 600; color: var(--ink-2); margin: 0;
                       min-width: 0; grid-column: span 12 }
.config-fields label.check { flex-direction: row; align-items: center; font-weight: 400;
                             min-height: 44px }
/* Número curto ocupa pouco; prosa ocupa a linha. O seletor repete
   `.config-fields` de propósito: sem isso `.config-fields label` teria
   especificidade maior e o `span 12` do padrão venceria todas estas. */
.config-fields .campo-number { grid-column: span 3 }
.config-fields .campo-date { grid-column: span 3 }
.config-fields .campo-instant,
.config-fields .campo-zone,
.config-fields .campo-id { grid-column: span 4 }
.config-fields .campo-text,
.config-fields .campo-choice,
.config-fields .campo-ref { grid-column: span 5 }
.config-fields .campo-note,
.config-fields .campo-bool { grid-column: span 12 }
/* No celular é coluna única: largura por conteúdo não faz sentido a 390 px. `1 / -1`, e não
   `span 12`: quando a grade cai para uma coluna só (abaixo), `span 12` criava onze colunas
   implícitas de largura zero, e o mapa da posição (`.geo`, que vai de ponta a ponta da grade
   explícita) ficava com 133 px (§54). */
@media (max-width: 760px) {
  .config-fields label, .config-fields label[class*="campo-"] { grid-column: 1 / -1 }
}
.config-fields input:not([type="checkbox"]), .config-fields select,
.config-fields textarea { width: 100%; min-width: 0 }
/* A nota de versão é frase ("Lido às 17:05 · o Salvar avisa..."): quebra entre palavras.
   O `break-all` de quando ela era o código da versão partia "avis a" (2026-10-21, §49). */
#config-version, #config-site-version { font-variant-numeric: tabular-nums;
                                        overflow-wrap: break-word; min-width: 0; text-align: right }
.config-fields textarea { resize: vertical; min-height: 44px; font: inherit }
.config-fields .small { font-weight: 400 }
.config-fields input:disabled, .config-fields select:disabled, .config-fields textarea:disabled {
  background: var(--sunken); color: var(--muted); cursor: not-allowed }
/* A identidade é leitura, não formulário, e o par rótulo/valor é indivisível.
   Até 2026-09-07 ela usava a mesma grade de 5 colunas dos campos, e são 4
   pares: `Local` caía na coluna 5 e o valor dele quebrava para a linha
   seguinte, a 1.022 px de distância do próprio rótulo. Mudar o número de
   colunas só adiaria — com cinco pares quebraria de novo. O que não pode se
   dividir é o par. */
.identity { display: flex; flex-wrap: wrap; gap: var(--s3) var(--s6);
            border: 1px solid var(--line-soft); border-left: 2px solid var(--frame);
            border-radius: var(--radius-sm);
            background: color-mix(in srgb, var(--surface-2) 70%, transparent);
            padding: var(--s4); margin: var(--s3) 0 }
.identity-pair { min-width: 0; flex: 0 1 auto; max-width: 100% }
.identity-pair dt { margin-top: 0 }
.identity-pair dd { margin: 2px 0 0; font-size: 13px }

/* Grandezas da unidade sensora. É leitura, como a identidade, e o que não pode
   se dividir é o item — nome em cima, código da fonte e confiança embaixo. A
   grade se enche sozinha, então nove grandezas ocupam duas linhas a 1440 px e
   nove linhas a 390 px, sem número de colunas escrito à mão em lugar nenhum. */
/* Grupo empilhado, lista ocupando a largura inteira: é o que dá mais colunas
   de item e menos altura. Pôr os três grupos lado a lado foi tentado e é pior
   — cada um fica estreito demais para duas colunas, e o cartão vai de 544 px
   para 673 px a 1440 x 900.
   A explicação vai na linha do título, e não num parágrafo próprio: são três
   grupos, e três parágrafos custavam 57 px sem dizer nada a mais. */
.metric-group { margin-top: var(--s3); min-width: 0 }
/* O resumo é o alvo que abre e fecha o grupo, então vale a regra dos 44 px
   como qualquer botão — inclusive a 1440 px, onde ele mede 21 px de texto. */
.metric-group-title { font-size: 13px; margin: 0; display: flex; flex-wrap: wrap;
                      align-items: baseline; align-content: center; gap: 2px var(--s2);
                      min-height: 44px; cursor: pointer }
.metric-group-title:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px }
/* `display: flex` no `<summary>` apaga o `::marker` que o navegador desenha, e
   sem marca nenhuma nada diz que o grupo abre. A seta é forma, não cor, e o
   `<details>` continua anunciando o estado por conta própria. */
.metric-group-title::marker, .metric-group-title::-webkit-details-marker { display: none }
.metric-group-title::before { content: '\25B8'; color: var(--muted); font-size: 11px }
.metric-group[open] > .metric-group-title::before { content: '\25BE' }
.metric-group-hint { font-size: 12px; font-weight: 400; color: var(--muted);
                     line-height: 1.6 }
.metric-count { font-family: var(--mono); font-size: 11px; font-weight: 400; color: var(--muted);
                background: var(--sunken); border: 1px solid var(--line);
                border-radius: 0; padding: 1px 8px;
                font-variant-numeric: tabular-nums }
.metric-list { list-style: none; margin: var(--s3) 0 0; padding: 0; display: grid;
               grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
               gap: var(--s3) var(--s4) }
.metric-item { min-width: 0; border-left: 2px solid var(--line); padding-left: var(--s3) }
.metric-name { display: block; font-size: 13px; font-weight: 500; color: var(--ink-2);
               overflow-wrap: anywhere }
.metric-unit { font-weight: 500; color: var(--muted) }
/* Linha de baixo: o código da fonte é o que se procura no `dp.yaml`. Quando
   não há código a mostrar, o nó de texto fica vazio e o `gap` não cria buraco. */
.metric-dp { display: flex; flex-wrap: wrap; align-items: baseline;
             gap: 0 var(--s2); margin-top: 2px; font-size: 11px;
             font-family: var(--mono); color: var(--muted); overflow-wrap: anywhere }
/* A confiança é palavra; a cor só reforça o que o texto já diz. */
.metric-conf { font-family: var(--sans); font-weight: 500 }
.metric-conf.conf-confirmed { color: var(--accent) }
.metric-conf.conf-probable, .metric-conf.conf-unknown { color: var(--warn) }
.metric-conf.conf-ignored { color: var(--muted) }

/* Mapa. Os ladrilhos vêm do OpenStreetMap; a atribuição fica visível, como a
   política do serviço exige, e a escala é desenhada aqui, não pedida a ele. */
.map { position: relative; overflow: hidden; height: 320px; border-radius: var(--radius-sm);
       border: 1px solid var(--line); background: var(--sunken); touch-action: none;
       cursor: grab; user-select: none }
.map:active { cursor: grabbing }
.map:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px }
.map-tiles, .map-marks { position: absolute; inset: 0 }
.map-marks { pointer-events: none }
.map-tile { position: absolute; width: 256px; height: 256px; max-width: none;
            filter: invert(1) hue-rotate(180deg) brightness(.8) contrast(.92) }
/* O mapa no escuro, como os do jogo: o ladrilho do OpenStreetMap invertido, e
   um véu no modo `color` que tinge tudo de petróleo (#03585F, o institucional)
   sem mexer na luz — rua, quadra e nome continuam legíveis. Só a pintura
   muda; o ladrilho e a atribuição são os mesmos. */
.map-tiles::after { content: ""; position: absolute; inset: 0; pointer-events: none;
                    background: #03585f; mix-blend-mode: color }
/* No claro, o ladrilho como é, com o véu petróleo mais leve. */
:root[data-tema="claro"] .map-tile { filter: saturate(.7) }
:root[data-tema="claro"] .map-tiles::after { opacity: .35 }
.map-precision { position: absolute; border-radius: 50%; border: 1.5px dashed var(--accent);
                 background: color-mix(in srgb, var(--accent) 14%, transparent) }
/* O pino é ancorado pela BOLINHA, não pelo bloco.
   Com `translate(-50%, -50%)` quem ficava sobre a coordenada era o meio de
   bolinha + etiqueta: com a etiqueta de 116 px, a bolinha caía 58 px à
   esquerda do lugar. Media-se no seletor de posição, onde o clique e a bolinha
   discordavam, mas o erro é do mapa inteiro e é de sempre. O deslocamento é
   metade da bolinha — 6,5 px de 13 —, e `test_o_pino_do_mapa_e_ancorado_pela_bolinha`
   quebra se um dos dois números mudar sem o outro. */
.map-pin { position: absolute; transform: translate(-6.5px, -50%);
           display: flex; align-items: center; gap: 6px; white-space: nowrap }
/* A bolinha virou o losango do pino do Painel (2026-10-15): o quadrado de
   13 px girado em torno do próprio centro, então o centro continua sobre a
   coordenada e a metade da largura continua sendo o deslocamento. */
.map-pin-dot { width: 13px; height: 13px; border-radius: 0; flex: none; transform: rotate(45deg);
               background: var(--accent); border: 2.5px solid var(--band);
               box-shadow: 0 0 0 1px var(--accent) }
.map-pin-label { font-size: 12px; font-weight: 500; color: var(--ink);
                 background: var(--band); border: 1px solid var(--frame);
                 border-radius: var(--radius-sm); padding: 2px 7px }
.map-zoom { position: absolute; top: var(--s2); right: var(--s2); display: flex;
            flex-direction: column; gap: 4px }
.map-zoom button { width: 44px; min-height: 44px; padding: 0; font-size: 18px;
                   background: var(--surface); border-color: var(--line) }
.map-credit { position: absolute; right: 0; bottom: 0; margin: 0; font-size: 10px;
              color: var(--muted); background: var(--surface); opacity: .92;
              padding: 4px 8px; border-radius: var(--radius-sm) 0 0 0 }
.map-credit a { color: var(--accent); display: inline-block; padding: 15px 4px; margin: -15px 0 }
.map-scale { position: absolute; left: var(--s3); bottom: var(--s3); height: 16px;
             border: 1px solid var(--ink-2); border-top: 0; background: var(--surface);
             opacity: .9; font-size: 10px; font-weight: 650; color: var(--ink-2);
             text-align: center; line-height: 16px }
#map-caption { margin: var(--s3) 0 0 }

/* Seletor de posição (Q-2). Ocupa a linha inteira da grade: mapa espremido em
   coluna de 230 px não serve para marcar coisa nenhuma. */
.geo { grid-column: 1 / -1; display: flex; flex-direction: column; gap: var(--s3) }
/* Leitura e aviso vazios, e o grupo dos campos ocultos, não ocupam lugar: cada um somava
   um intervalo, e sobrava um vão entre o mapa e o Salvar (§57). Os campos seguem no formulário. */
.geo > p:empty, .geo > div:not([class]) { display: none }
.geo-head { display: flex; align-items: center; justify-content: space-between;
            gap: var(--s3); flex-wrap: wrap }
.geo-head button { min-height: 44px }
.geo-edit { display: flex; flex-direction: column; gap: var(--s3);
            padding: var(--s4); border: 1px solid var(--line);
            border-radius: var(--radius-sm); background: var(--sunken) }
.geo-edit p { margin: 0 }
.geo-field { display: flex; flex-direction: column; gap: 6px;
             font-weight: 600; color: var(--ink-2); margin: 0 }
.geo-row { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap }
.geo-row input[type="text"] { flex: 1 1 220px; min-width: 0 }
.geo-row input[type="number"] { flex: 0 0 110px; min-width: 0 }
.geo-row input[type="range"] { flex: 1 1 200px; min-width: 0; min-height: 44px }
.geo-row button { min-height: 44px }
/* A mira é o alvo do caminho por teclado: as setas movem o mapa até ela e o
   Enter marca o centro. Não recebe ponteiro — quem recebe é o mapa. */
.map-cross { position: absolute; left: 50%; top: 50%; width: 34px; height: 34px;
             margin: -17px 0 0 -17px; pointer-events: none;
             background:
               linear-gradient(var(--accent), var(--accent)) center/2px 100% no-repeat,
               linear-gradient(var(--accent), var(--accent)) center/100% 2px no-repeat;
             opacity: .75 }

@media (max-width: 760px) {
  .filterbar { padding: var(--s3) }
  .filterbar-line { gap: var(--s2) }
  .filterbar label, .rowbar label { flex: 1 1 100%; min-width: 0 }
  .filterbar input:not([type="checkbox"]), .filterbar select,
  .rowbar input:not([type="checkbox"]), .rowbar select { min-width: 0; width: 100% }
  .check input { width: 17px; flex: none }
  .filterbar-actions, .rowbar button { margin-left: 0; width: 100% }
  .filterbar-actions button { flex: 1 1 0 }
  .zoombar button { margin-left: 0; width: 100% }
  .map { height: 260px }
  .config-fields, .identity { grid-template-columns: minmax(0, 1fr) }
  .legend-flat { gap: var(--s2); flex-direction: column }
}

/* ==========================================================================
 * Aba Clima — chuva, vento e luz da mesma janela
 *
 * Vento e chuva lado a lado, luz embaixo e com menos peso: decisão do operador
 * em 2026-09-10. Nenhuma cor nova — os avisos usam o mesmo `--warn` do resto da
 * tela, e as duas séries de cada gráfico continuam se distinguindo por traço
 * (contínuo e tracejado), não só por cor.
 * ======================================================================== */
.avisos { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
          gap: var(--s4); margin-bottom: var(--s4) }
/* O aviso é painel com mira (regra da `.mira`): a cor de fundo é só
   `background-color`, senão o atalho apagava os cantos. */
.aviso { border: 1px solid var(--line-soft); border-radius: var(--radius);
         background-color: color-mix(in srgb, var(--surface) 90%, transparent);
         padding: var(--s4); box-shadow: var(--shadow) }
/* Qualificação de sítio é requisito para uso ambiental, não rodapé: peso
   visual, e não cinza no pé da página (`config/dp.yaml`). */
.aviso.strong { border-color: var(--warn-line); border-left: 2px solid var(--warn);
                background-color: var(--warn-bg) }
.aviso strong { display: block; font-size: 13.5px; font-weight: 500; color: var(--ink); margin-bottom: 5px }
.aviso p { margin: 0; font-size: 12px; line-height: 1.65; color: var(--ink-2) }

.clima-par { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
             gap: var(--s4); margin-bottom: var(--s4) }
.clima-bloco { min-width: 0; display: flex; flex-direction: column }
.clima-bloco .estado { border: 1px solid var(--line-soft); border-radius: var(--radius-sm);
                       margin-bottom: var(--s3) }
/* O bloco de chuva tem cinco células em meia largura: o respiro do Tempo real
   (24 px de cada lado) não cabe, e a linha de máximo quebraria em três. */
.clima-bloco .estado > div { padding: 11px var(--s3) }
/* Cinco células em meia largura: o rótulo largo quebrava em três linhas
   ("VELOCIDADE / MEDIA DO / VENTO"). Aqui ele perde a largura extra. */
.clima-bloco .estado .label { font-stretch: 100%; letter-spacing: .06em }
.clima-bloco .estado strong { font-size: 17px }
.clima-bloco .estado small { line-height: 1.5 }
.clima-bloco .legend { margin-bottom: var(--s3) }
.clima-bloco > p.small { margin: 0 0 var(--s2) }

.clima-chart { min-height: 210px }
.clima-chart svg { display: block; width: 100%; height: 210px }
.clima-chart.dir { min-height: 128px }
.clima-chart.dir svg { height: 128px }
.clima-luz .clima-chart { min-height: 150px }
.clima-luz .clima-chart svg { height: 150px }
.clima-chart .grid { stroke: var(--grid); stroke-width: 1 }
.clima-chart .axis { stroke: var(--axis); stroke-width: 1 }
.clima-chart text { font-family: var(--mono); fill: var(--muted); font-size: 10px;
                    font-variant-numeric: tabular-nums }
.clima-chart .axis-label { fill: var(--ink-2); font-weight: 500; letter-spacing: .4px }
.clima-chart .axis-label.b { fill: var(--series-b) }
.clima-chart .series { fill: none; stroke: var(--series); stroke-width: 1.6;
                       stroke-linejoin: round; stroke-linecap: round }
.clima-chart .series.b { stroke: var(--series-b); stroke-dasharray: 7 4 }
.clima-chart .points { fill: none; stroke: var(--series); stroke-width: 2.4; stroke-linecap: round }
.clima-chart .points.b { stroke: var(--series-b); stroke-linecap: square }
.clima-chart .points.dense { stroke-width: 1.2 }
.clima-chart .flags { stroke: var(--warn); stroke-width: 1.4; fill: var(--warn-bg) }
/* A hachura de cada gráfico tem `id` próprio — quatro gráficos no mesmo
   documento não podem dividir um `id`. Por isso o preenchimento vem por
   atributo, no JS: uma regra de folha não conseguiria variar o `url()`. */
.clima-chart .gap { stroke: var(--gap-line); stroke-width: 1 }
.clima-chart .gap-bg { fill: var(--gap-fill) }
.clima-chart .gap-hatch { stroke: var(--gap-line); stroke-width: 1 }
.clima-chart .cursor { stroke: var(--accent); stroke-width: 1; stroke-dasharray: 4 3 }
.clima-chart .cursor-dot { fill: var(--surface); stroke: var(--series); stroke-width: 2.4 }
.clima-chart .cursor-dot.b { stroke: var(--series-b) }
.clima-chart .selection { fill: var(--accent); opacity: .12 }
.clima-chart:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px }
/* A zeragem da meia-noite é comportamento correto do aparelho: marca discreta
   e rotulada, nunca a marca de sinalização. */
.clima-chart .reset { stroke: var(--axis); stroke-width: 1; stroke-dasharray: 2 3 }
.clima-chart .reset-label { fill: var(--muted); font-family: var(--sans); font-size: 9px; font-weight: 500;
                            font-stretch: 125%; letter-spacing: .14em; text-transform: uppercase }

.clima-direcao { display: grid; grid-template-columns: 172px minmax(0, 1fr);
                 gap: var(--s4); align-items: start; margin-top: var(--s2) }
.clima-dir-tempo { min-width: 0 }
.rosa { min-width: 0 }
.rosa svg { display: block; width: 100%; max-width: 172px; height: auto }
.rosa .ring { fill: none; stroke: var(--grid); stroke-width: 1 }
.rosa .spoke { stroke: var(--grid); stroke-width: 1 }
.rosa .sector { fill: var(--series); opacity: .78; stroke: var(--surface); stroke-width: .5 }
.rosa .sector.top { opacity: 1 }
.rosa text { font-family: var(--mono); fill: var(--muted); font-size: 9px; font-weight: 500 }

/* Luz: mesmo vocabulário, menos peso — bloco em fundo rebaixado e sem sombra.
   Só `background-color`: o atalho apagaria os cantos de mira do `.card`. */
.clima-luz { background-color: color-mix(in srgb, var(--surface-2) 80%, transparent); box-shadow: none;
             margin-bottom: var(--s4) }
.clima-luz-grid { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: var(--s4) }
.clima-luz .estado { border: 1px solid var(--line-soft); border-radius: var(--radius-sm);
                     grid-template-columns: minmax(0, 1fr) !important; align-self: start }
.clima-luz .estado > div { border-right: 0; border-bottom: 1px solid var(--line-soft) }
.clima-luz .estado > div:last-child { border-bottom: 0 }

@media (max-width: 1050px) {
  .avisos { grid-template-columns: minmax(0, 1fr) }
  .clima-par { grid-template-columns: minmax(0, 1fr) }
  .clima-luz-grid { grid-template-columns: minmax(0, 1fr) }
}
@media (max-width: 760px) {
  .clima-direcao { grid-template-columns: minmax(0, 1fr) }
  .rosa svg { margin: 0 auto }
  .clima-chart:not(.dir) { min-height: 180px }
  .clima-chart:not(.dir) svg { height: 180px }
  .clima-luz .clima-chart, .clima-luz .clima-chart svg { min-height: 150px; height: 150px }
  .clima-bloco .estado { grid-template-columns: 1fr 1fr !important }
}

/* ==========================================================================
 * A home — 2026-09-10
 *
 * Duas colunas no monitor, uma no celular. Nenhuma cor nova nasce aqui; tudo
 * sai dos tokens do topo do arquivo.
 *
 * Três decisões de forma, e nenhuma é de gosto:
 *
 * - **A saudação empilha.** Texto e pastilhas lado a lado espremiam a frase em
 *   três linhas dentro de 190 px enquanto sobrava metade do cartão vazio.
 * - **As pastilhas dividem a largura em partes iguais** em vez de escorrerem.
 *   Com `flex-wrap` a terceira caía sozinha numa segunda fileira, e a faixa
 *   ficava desalinhada com o resto do painel.
 * - **No celular a tabela vira lista.** Sete colunas num aparelho de 380 px só
 *   existem atrás de rolagem lateral, e rolagem lateral dentro de uma página
 *   que já rola na vertical é onde a informação vai morrer.
 * ======================================================================== */
.home { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
        gap: var(--s4); align-items: stretch }
.col { display: grid; gap: var(--s4); min-width: 0; align-content: start }
/* O mini-painel tem a altura do que ele mostra, e nada mais.
   Ele já acompanhou a altura da coluna da esquerda, com teto de 460 px, para
   não deixar vão embaixo. Custava caro: a sobra ia toda para a curva, que é a
   única linha elástica do cartão, e ou a curva esticava até deformar a série ou
   sobrava um vazio de 100 px entre o rótulo e o desenho. O operador escolheu o
   cartão curto em 2026-09-10, e a coluna curta é o preço aceito. */
#mini { align-content: start }
/* Em `Projetos ativos` o mini-painel sai, e a lista toma a largura em vez de
   deixar 44 % da tela vazia ao lado. */
.home:has(> #mini[hidden]) { grid-template-columns: minmax(0, 1fr) }

/* -------------------------------------------------------------- saudação */
/* A saudação é o título de tela (`.tela-titulo`, o mesmo do acesso), com a data
   do lado direito, como o nome do lugar no alto das telas do jogo. */
.saudacao .tela-titulo { display: grid; grid-template-columns: minmax(0, 1fr) auto;
                         column-gap: var(--s6); align-items: end }
.saudacao .tela-titulo .eyebrow { grid-column: 1 / -1; margin: 0 }
/* Dentro do sistema o título de tela é menor que o do acesso. */
.page .tela-titulo h1 { font-size: clamp(24px, 2.4vw, 34px) }
.saudacao .contexto { margin: 0 0 4px; font-size: 11px; font-weight: 500; font-stretch: 125%;
                      letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
                      text-align: right }

/* As três leituras sob o fio, como o mostrador do pé da tela do jogo: rótulo
   largo, número em mono com os zeros da frente apagados, sem caixa. */
.tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
         margin: var(--s5) 0 var(--s6) }
.pastilha { padding: 2px var(--s5); border-left: 1px solid var(--line); min-width: 0 }
.pastilha:first-child { padding-left: 0; border-left: 0 }
.pastilha .label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.pastilha strong { display: block; font-family: var(--mono); font-size: 34px; font-weight: 300;
                   line-height: 1.15; margin-top: 6px; font-variant-numeric: tabular-nums }
.pastilha strong > span:not(.cont) { font-size: 18px; color: var(--muted) }
/* O zero da frente é desenho, e fica fora do leitor de tela (`aria-hidden`). */
.pastilha small { display: block; color: var(--muted); font-size: 11.5px; margin-top: 2px;
                  line-height: 1.45 }
/* A notificação é a única das três que pode pedir ação, e é a única com cor:
   o fio da esquerda passa a ser o do estado. */
/* `.aviso` também é a caixa de aviso da Configuração (moldura, fundo e
   recuo); aqui ela é desfeita inteira, senão a pastilha nasce emoldurada. */
.pastilha.aviso, .pastilha.calma { padding: 2px var(--s5) 2px var(--s4); border: 0;
                                   border-left: 2px solid; background: none; box-shadow: none }
.pastilha.aviso { border-left-color: var(--warn) }
.pastilha.aviso .label { color: var(--warn) }
.pastilha .linha { display: flex; align-items: flex-start; gap: var(--s2); margin-top: 8px }
.pastilha b { font-size: 14px; font-weight: 500; line-height: 1.3 }
.pastilha.aviso svg { color: var(--warn); margin-top: 1px; width: 17px; height: 17px; flex: none }
.pastilha.calma { border-left-color: var(--accent) }
.pastilha.calma .label, .pastilha.calma svg { color: var(--accent) }

/* -------------------------------------------------------- lista de projetos */
.painel-topo { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
               padding: var(--s4) var(--s5); border-bottom: 1px solid var(--line-soft) }
/* O nome do painel é o cabeçalho de seção do jogo: largo, fino, o fio curto. */
.painel-topo h2 { font-size: 13px; font-weight: 400; font-stretch: 125%; letter-spacing: .24em;
                  text-transform: uppercase; line-height: 1.3; margin: 0; margin-right: auto;
                  border-left: 2px solid var(--frame); padding-left: 10px }
.busca { display: flex; align-items: center; gap: var(--s2); font-size: 11px;
         font-weight: 500; font-stretch: 125%; letter-spacing: .14em; text-transform: uppercase;
         color: var(--muted); margin: 0; min-width: 0 }
.busca input { min-height: 36px; width: clamp(120px, 18vw, 190px); margin: 0;
               padding: 6px 11px; font-size: 13px; letter-spacing: 0; text-transform: none }
.total { font-size: 11px; font-weight: 500; font-stretch: 125%; letter-spacing: .14em;
         text-transform: uppercase; color: var(--muted); white-space: nowrap }
.total b { font-family: var(--mono); font-size: 13px; font-weight: 400; letter-spacing: 0;
           color: var(--ink); margin-left: 4px; font-variant-numeric: tabular-nums }
.painel-topo button { min-height: 36px; padding: 6px var(--s3); font-size: 12.5px }

.rolagem { overflow-x: auto }
/* Largura fixa: sem isso as colunas de número mudam de largura conforme o
   conteúdo, e o nome longo empurra os botões para fora da tela. Os valores são
   enxutos de propósito — o que sobra vai para o nome, que é o que se lê. */
#projetos { border-collapse: collapse; width: 100%; min-width: 640px; table-layout: fixed }
#projetos col.c-data { width: 128px }
#projetos col.c-estado { width: 132px }
#projetos col.c-n { width: 52px }
#projetos col.c-acoes { width: 152px }
/* O cabeçalho é a faixa de colunas das listas do jogo: um tom acima, o nome
   da coluna em caixa-alta larga e miúda. */
#projetos thead th { text-align: left; background: color-mix(in srgb, var(--surface-2) 85%, transparent);
                     padding: 0; border-bottom: 1px solid var(--line); font-size: 11px;
                     font-weight: 500; font-stretch: 112%; letter-spacing: .1em;
                     color: var(--muted); text-transform: uppercase; white-space: nowrap }
/* O botão global centraliza: com o rótulo largo, "DATA DE ALTA" estourava a
   coluna e era cortado nas duas pontas. Aqui o rótulo começa na esquerda. */
#projetos thead th button { display: flex; align-items: center; justify-content: flex-start;
                            gap: 4px; width: 100%;
                            min-height: 0; padding: 11px var(--s2) 11px var(--s3); border: 0; border-radius: 0;
                            background: transparent; color: inherit; font: inherit;
                            letter-spacing: inherit; text-transform: uppercase }
#projetos thead th.n button { justify-content: center; padding: 11px 0 }
#projetos thead th button:hover { color: var(--accent) }
#projetos thead th .ord { font-size: 11px; opacity: .5; font-weight: 400 }
#projetos thead th[aria-sort] .ord { opacity: 1; color: var(--accent) }
#projetos thead th.acoes { padding: 11px var(--s5) 11px 0; text-align: right }
#projetos tbody td { padding: var(--s3); border-bottom: 1px solid var(--line-soft);
                     font-size: 13.5px; vertical-align: middle }
#projetos tbody tr:last-child td { border-bottom: 0 }
/* A linha sob o ponteiro é a linha escolhida do jogo: o fio teal na entrada e
   um véu dele. O fio mora na primeira célula: em tabela colapsada o Chrome não
   desenha sombra no `tr`. */
#projetos tbody tr:hover { background: color-mix(in srgb, var(--accent) 6%, transparent) }
#projetos tbody tr:hover td:first-child { box-shadow: inset 2px 0 0 var(--accent) }
#projetos td.n { text-align: center; font-family: var(--mono); font-weight: 400; font-size: 14px;
                 padding: var(--s3) 0; font-variant-numeric: tabular-nums }
#projetos td.n.zero { color: var(--muted) }
#projetos td.n.alerta { color: var(--warn) }
#projetos td.data { font-family: var(--mono); font-size: 12.5px; color: var(--ink-2);
                    font-variant-numeric: tabular-nums; white-space: nowrap }
#projetos td .chip { white-space: nowrap }
#projetos td.nome a { font-weight: 500; text-decoration: none;
                      display: block; overflow: hidden; text-overflow: ellipsis;
                      white-space: nowrap }
#projetos td.nome a:hover { text-decoration: underline }
#projetos td.acoes { text-align: right; white-space: nowrap; padding-right: var(--s5) }
/* As ações são os ícones em caixa do jogo: traço fino, sem bloco de cor. O
   teal acende no ponteiro. */
.acao { width: 36px; height: 36px; min-height: 0; padding: 0; margin-left: 6px;
        border-radius: var(--radius-sm); border: 1px solid var(--line);
        background: color-mix(in srgb, var(--band) 55%, transparent); color: var(--ink-2) }
.acao:hover:not(:disabled) { color: var(--accent); border-color: var(--accent);
                             background: color-mix(in srgb, var(--accent) 12%, transparent) }
.acao:disabled { opacity: .4 }
.legenda { display: flex; gap: var(--s4); flex-wrap: wrap; padding: 11px var(--s5);
           border-top: 1px solid var(--line-soft); font-size: 11.5px; color: var(--muted) }
.legenda span { display: inline-flex; align-items: center; gap: 6px }
.legenda svg { width: 15px; height: 15px }
.vazio { padding: var(--s5); font-size: 13px; color: var(--muted); margin: 0 }

/* ------------------------------------------------------------- mini-painel */
/* O cartão é uma coluna: cabeça, subtítulo, faixa de leituras e a curva no pé.
   A curva absorve a sobra de altura — é ela que faz o cartão acompanhar a
   coluna da esquerda em vez de deixar um vão embaixo. */
#mini > .card { display: grid; grid-template-rows: auto auto auto auto;
               overflow: hidden }
.mini-cabeca { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
               padding: var(--s4) var(--s5) var(--s3) }
.mini-cabeca h3 { font-size: 15px; font-weight: 400; font-stretch: 112%; letter-spacing: .01em;
                  margin: 0; margin-right: auto; min-width: 0;
                  border-left: 2px solid var(--frame); padding-left: 10px }
.mini-cabeca h3 a { text-decoration: none; color: inherit }
.mini-cabeca h3 a:hover { color: var(--accent) }
.mini-sub { padding: 0 var(--s5) var(--s3); font-size: 11.5px; color: var(--muted); margin: 0;
            line-height: 1.5 }

/* Uma célula por grandeza, cinco no total — a mesma faixa de leituras da tela
   da sonda. Cada uma traz a hora do próprio reporte: elas não chegam juntas. */
.essenciais { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
              border-top: 1px solid var(--line-soft) }
.essencial { padding: 11px var(--s3); border-right: 1px solid var(--line-soft); min-width: 0 }
.essencial:last-child { border-right: 0 }
/* O ruído tem duas (LAeq,T e LASmax, A3): meia faixa cada, em toda largura. */
.essenciais.duas { grid-template-columns: repeat(2, minmax(0, 1fr)) }
.essenciais.duas .essencial { border-bottom: 0 }
/* A célula é estreita (≈ 87 px a 1440): o rótulo perde a largura e quase toda
   a entreletra ("TEMPERATURA" cortava em 110 %), e o número em mono fica
   abaixo dos 17 px. */
.essencial .label { font-stretch: 100%; letter-spacing: .06em; white-space: nowrap;
                    overflow: hidden; text-overflow: ellipsis }
.essencial strong { display: block; font-family: var(--mono); font-size: clamp(14px, 1.15vw, 17px);
                    font-weight: 400; margin-top: 4px; white-space: nowrap;
                    font-variant-numeric: tabular-nums }
.essencial strong span { font-family: var(--sans); font-size: 10.5px; font-weight: 500;
                         color: var(--muted) }
.essencial em { display: block; font-style: normal; font-family: var(--mono); font-size: 10px;
                color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden;
                text-overflow: ellipsis }

/* A curva de 24 h. O rótulo e a faixa observada ficam nas pontas da mesma
   linha — sem isso os dois encostam e o número parece parte do rótulo.
   O recuo mora no rótulo, não no bloco: a curva vai de borda a borda e encosta
   no pé do cartão. Com recuo dos dois lados ela perdia 48 px de largura e
   ficava com cara de figura colada dentro do cartão, em vez de rodapé dele. */
.spark { display: flex; flex-direction: column; min-width: 0;
         border-top: 1px solid var(--line-soft) }
.spark-topo { display: flex; align-items: baseline; justify-content: space-between;
              gap: var(--s3); padding: var(--s3) var(--s5) 0; margin-bottom: var(--s2) }
.spark-topo > .spark-agora { display: grid; gap: 2px; min-width: 0 }
.spark-topo span { font-size: 11px; font-weight: 500; font-stretch: 125%; letter-spacing: .14em;
                   color: var(--muted); text-transform: uppercase; white-space: nowrap }
/* O valor de agora é o título da curva. A faixa observada fica do outro lado da
   mesma linha: o número diz onde a curva termina, a faixa diz o que a altura
   dela significa, e nenhum dos dois sozinho basta. */
.spark-agora strong { display: block; font-family: var(--mono); font-size: 26px; font-weight: 300;
                      line-height: 1.1; font-variant-numeric: tabular-nums }
.spark-agora strong span { font-family: var(--sans); font-size: 14px; font-weight: 500;
                           color: var(--muted); letter-spacing: 0; text-transform: none }

/* A bolha do ponteiro. Ela mora colada ao ponto que descreve, e não num canto
   do cartão: valor longe do lugar onde o dedo está obriga a ler duas vezes. */
/* No escuro, a bolha é a etiqueta do jogo: fundo da tarja, moldura clara. */
.spark-bolha { position: absolute; z-index: 2; transform: translate(-50%, -100%);
               pointer-events: none; white-space: nowrap; background: var(--band);
               color: var(--ink); border: 1px solid var(--frame); border-radius: var(--radius-sm);
               padding: 7px 11px; box-shadow: var(--shadow); display: grid; gap: 2px }
.spark-bolha.abaixo { transform: translate(-50%, 0) }
.spark-bolha .quando { font-family: var(--mono); font-size: 10px; font-weight: 400;
                       color: var(--muted); letter-spacing: 0; text-transform: none }
.spark-bolha b { display: flex; align-items: center; gap: 7px; font-size: 12.5px;
                 font-weight: 500; font-variant-numeric: tabular-nums }
/* A cor não é a única indicação: a bolinha repete a cor da série, e o rótulo
   escrito ao lado diz de que grandeza se trata. */
.spark-bolha b::before { content: ""; width: 8px; height: 8px; border-radius: 50%;
                         background: var(--series-bolha); flex: none }
.spark-topo em { font-style: normal; font-family: var(--mono); font-size: 10.5px; color: var(--muted);
                 white-space: nowrap; font-variant-numeric: tabular-nums }
/* A altura sai da largura, e é isso que trava a proporção de uma vez.
   A viewBox é esticada para o elemento (`preserveAspectRatio="none"`), então
   caixa com proporção diferente da viewBox deforma a curva — 2,91× quando a
   altura era livre no celular, 1,94× para o outro lado quando ela absorvia a
   sobra do cartão no monitor. Com `aspect-ratio` igual à da viewBox a escala é
   a mesma nos dois eixos em qualquer largura, e não há teto a manter. */
.spark-tela { position: relative; min-width: 0 }
.spark svg { display: block; width: 100%; aspect-ratio: 600 / 160 }

/* A lacuna da curva e o eixo das 24 h (plano do Painel, §6.5). O selo da maior
   lacuna fica sobre o desenho quando cabe; estreita demais, vai para o eixo. */
.spark-tela .buraco { position: absolute; top: 0; bottom: 0; display: flex; align-items: center;
                      justify-content: center; pointer-events: none; padding: 0 var(--s2) }
.spark-tela .buraco span { font-size: 11px; font-weight: 600; color: var(--warn); background: var(--surface);
                           border: 1px solid var(--gap-line); border-radius: 0; padding: 3px 9px;
                           text-align: center; max-width: 100% }
.spark-eixo { display: flex; justify-content: space-between; gap: var(--s2); padding: 4px var(--s5) var(--s2);
              font-family: var(--mono); font-size: 10px; color: var(--muted); font-variant-numeric: tabular-nums }
.spark-eixo .eixo-lacuna { color: var(--warn); font-weight: 600; text-align: center }

/* ==========================================================================
 * O Painel do projeto — plano de 2026-09-14, desenho aprovado em
 * `docs/plano/mock/index.html`
 * ======================================================================== */
/* Sem título entre as abas e os cartões (decisão 8): eles sobem para perto das
   abas, e o nome do grupo mora no `aria-label`. */
.page > .tela-titulo { margin-bottom: var(--s5) }

/* Um cartão por tipo. O cartão é `<button>` e herda o `button` global —
   `justify-content: center` —, que é desfeito aqui: é o mesmo defeito que o
   menu da barra teve em 2026-09-11. */
.cats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s3);
        margin-bottom: var(--s4) }
.cat { display: grid; align-content: start; justify-content: stretch; justify-items: stretch;
       gap: 6px; text-align: left; min-height: 104px; min-width: 0;
       padding: var(--s3) var(--s4); border-radius: var(--radius); color: var(--ink);
       background-color: color-mix(in srgb, var(--surface) 90%, transparent);
       border: 1px solid var(--line-soft); box-shadow: var(--shadow) }
/* O cartão sob o ponteiro é a linha escolhida do jogo: o fio teal na entrada. */
.cat:hover:not(:disabled) { border-color: var(--accent); color: var(--ink);
                            box-shadow: inset 2px 0 0 var(--accent) }
.cat:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px }
.cat-topo { display: flex; align-items: center; gap: var(--s2); min-width: 0; color: var(--accent) }
.cat-topo .label { color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
                   min-width: 0; margin-right: auto }
.cat-topo .abre { width: 16px; height: 16px; color: var(--muted) }
/* A contagem é a leitura do Início: mono fina, os zeros da frente apagados. */
.cat strong { font-family: var(--mono); font-size: 30px; font-weight: 300; line-height: 1.05;
              font-variant-numeric: tabular-nums }
.cat strong > span:not(.cont) { font-size: 16px; color: var(--muted) }
.cat small { font-size: 11.5px; color: var(--muted) }
.cat .linha { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px var(--s2) }
.cat .chip, .cat .linha { justify-self: start; white-space: nowrap }
/* Tipo sem aparelho: o painel vazio, sem véu, só os cantos e o fio. */
.cat.vazia { background-color: transparent; box-shadow: none }
.cat.vazia .cat-topo { color: var(--muted) }

/* O painel de um dispositivo à esquerda, o mapa de todos à direita. */
.painel-grid { display: grid; grid-template-columns: minmax(0, 1.85fr) minmax(0, 1fr);
               gap: var(--s4); align-items: stretch }
/* Coluna que não cresce com o conteúdo: sem `minmax(0, 1fr)` a trilha pega a
   largura mínima do filho mais largo, e o `overflow: hidden` do cartão
   recortava 14 px da faixa de leituras a 390 px — medido no mockup. */
.dash { overflow: hidden; display: grid; grid-template-columns: minmax(0, 1fr);
        align-content: start; min-width: 0 }
/* Uma fileira só: o menu e a ação, na mesma altura (decisão 8). */
.dash-cabeca { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s3);
               padding: var(--s3) var(--s5); border-bottom: 1px solid var(--line-soft) }
.dash-cabeca select { flex: 0 1 380px; min-width: 0; width: auto; min-height: 44px; margin: 0 }
.dash-cabeca .botao { margin-left: auto }
/* O link com cara de botão fala como as abas: caixa-alta larga. O `primary`
   dele é a barra âmbar do `.primary`, regra lá em cima. */
.botao { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 44px;
         padding: 0 var(--s4); border-radius: var(--radius-sm); border: 1px solid var(--line);
         font-size: 11px; font-weight: 500; font-stretch: 125%; letter-spacing: .14em;
         text-transform: uppercase; text-decoration: none; color: var(--ink); white-space: nowrap }
.botao:hover { border-color: var(--accent); color: var(--accent) }
.dash-id { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s3);
           padding: var(--s3) var(--s5) 4px }
/* O nome do aparelho é o cabeçalho de seção do jogo, como no mini-painel. */
.dash-id h3 { margin: 0; font-size: 20px; font-weight: 300; font-stretch: 112%; letter-spacing: .01em;
              overflow-wrap: anywhere; border-left: 2px solid var(--frame); padding-left: 10px }
.dash-id .quando { font-family: var(--mono); font-size: 11px; color: var(--muted) }
.dash .mini-sub { padding-top: 2px }
.dash > .state { margin: var(--s5) }
.a-definir { margin: 0; padding: var(--s3) var(--s5); font-size: 12px; color: var(--warn);
             background: var(--warn-bg); border-top: 1px solid var(--warn-line) }

.mapa { display: flex; flex-direction: column; overflow: hidden; min-width: 0 }
.mapa-cabeca { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2);
               padding: var(--s4) var(--s5) var(--s3); border-bottom: 1px solid var(--line-soft) }
.mapa-cabeca h2 { font-size: 13px; font-weight: 400; font-stretch: 125%; letter-spacing: .24em;
                  text-transform: uppercase; line-height: 1.3; margin: 0;
                  border-left: 2px solid var(--frame); padding-left: 10px }
.mapa-cabeca small { color: var(--muted); font-size: 11px; font-weight: 500; font-stretch: 125%;
                     letter-spacing: .14em; text-transform: uppercase; margin-right: auto }
/* O mapa do painel é o `.map` de sempre, sem moldura própria: a moldura é o
   cartão. Ao lado do painel ele acompanha a altura dele; empilhado, a altura
   sai da largura. */
.map.mapa-tela { flex: 1 1 auto; height: auto; min-height: 320px; border: 0; border-radius: 0 }
.mapa-tela > .state { margin: var(--s5) }
.mapa-pe { display: grid; gap: 4px; padding: var(--s3) var(--s5); border-top: 1px solid var(--line-soft);
           font-size: 12px; color: var(--muted) }
.mapa-pe b { color: var(--ink); font-weight: 500 }

/* O pino do painel é botão (plano, §6.6): alvo de 44 px, a letra do tipo e o
   selo `!` quando o aparelho está sem reportar. Quem fica sobre a coordenada é
   a ponta da gota: girado 45°, o quadrado passa (√2 − 1) / 2 ≈ 0,2071 do lado
   abaixo da própria caixa, e é isso que o deslocamento vertical desconta. */
.map-marks .map-botao { pointer-events: auto }
.map-botao { --gota: 30px; position: absolute;
             transform: translate(-50%, calc(-100% - var(--gota) * .2071));
             display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
             gap: 0; min-width: 44px; min-height: 44px; padding: 0; border: 0; background: none;
             color: var(--ink) }
.map-botao:hover:not(:disabled) { color: var(--ink) }
.map-botao:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px }
/* O pino é o marcador do mapa do jogo: um losango de traço teal sobre o
   escuro, com a letra do tipo. A gota de antes perdeu o arredondado, e o
   vértice de baixo continua sobre a coordenada: é o mesmo canto vivo. O
   escolhido acende inteiro e ganha o anel âmbar, que é o que se aponta. */
.map-rotulo { font-size: 11px; font-weight: 500; background: var(--band); border: 1px solid var(--frame);
              border-radius: 0; padding: 2px 8px; white-space: nowrap; box-shadow: var(--shadow) }
.map-gota { width: var(--gota); height: var(--gota); border-radius: 0; transform: rotate(-45deg);
            background: var(--band); display: grid; place-items: center; margin-top: 4px;
            border: 1.5px solid var(--accent); box-shadow: var(--shadow); position: relative }
.map-gota i { transform: rotate(45deg); font-style: normal; font-family: var(--mono); font-size: 12px;
              font-weight: 500; color: var(--accent) }
.map-gota b { position: absolute; top: -7px; right: -7px; transform: rotate(45deg); width: 16px; height: 16px;
              border-radius: 0; background: var(--band); color: var(--warn);
              border: 1px solid var(--warn); font-size: 10px; display: grid; place-items: center }
.map-botao[aria-pressed="true"] .map-gota { background: var(--accent);
                                            outline: 1.5px solid var(--signal); outline-offset: 3px }
.map-botao[aria-pressed="true"] .map-gota i { color: var(--accent-ink) }
.map-botao[aria-pressed="true"] .map-rotulo { border-color: var(--accent); color: var(--accent) }

@media (max-width: 1100px) {
  .painel-grid { grid-template-columns: 1fr }
  .map.mapa-tela { flex: none; min-height: 0; height: clamp(260px, 56vw, 420px) }
}
@media (max-width: 1000px) {
  .cats { grid-template-columns: repeat(2, minmax(0, 1fr)) }
}
/* Abaixo de 560 px o nome do dispositivo não cabe ao lado da ação, e o
   `<select>` nativo corta o texto sem avisar: os dois empilham. */
@media (max-width: 560px) {
  .dash-cabeca select, .dash-cabeca .botao { flex: 1 1 100%; margin-left: 0 }
}
@media (max-width: 480px) {
  /* Rótulo cortado é informação perdida: `QUALIDADE DO AR` pedia 107 px e havia
     97. Quebra em duas linhas em vez de reticências. */
  .cat-topo { align-items: flex-start }
  /* Quebrar só resolve o rótulo de várias palavras. `METEOROLÓGICAS` é uma só,
     e na letra larga (125 %, ≈ 122 px) passava por baixo da seta numa célula
     de 109 px a 390; na largura normal mede ≈ 101 px. */
  .cat-topo .label { white-space: normal; overflow: visible; line-height: 1.35;
                     font-stretch: 100%; letter-spacing: .08em }
}
@media (max-width: 420px) {
  /* O ícone do tipo é decorativo e o rótulo diz o mesmo: `METEOROLÓGICAS` é uma
     palavra só, não quebra, e pedia 105 px onde havia 98. A seta fica. */
  .cat-topo > .icon:not(.abre) { display: none }
  .cat { padding: var(--s3) }
  .cat strong { font-size: 24px }
  .dash-cabeca, .dash-id, .mapa-cabeca, .mapa-pe { padding-left: var(--s4); padding-right: var(--s4) }
}

/* ==========================================================================
 * Telas estreitas
 * ======================================================================== */
@media (max-width: 1100px) {
  .home { grid-template-columns: 1fr }
}
@media (max-width: 900px) {
  .essenciais { grid-template-columns: repeat(3, minmax(0, 1fr)) }
  .essencial { border-bottom: 1px solid var(--line-soft) }
  .essencial:nth-child(3n) { border-right: 0 }
  /* São cinco, sempre: o laço percorre `ESSENCIAIS` e não pula grandeza sem
     reporte — ela vira "—". A quinta ocupa o que sobra da fileira em vez de
     ficar sozinha com o vão ao lado. */
  .essencial:nth-child(5):last-child { grid-column: span 2; border-right: 0 }
}
@media (max-width: 700px) {
  .page { padding: var(--s4) var(--s4) var(--s5) }
  .home { gap: var(--s3) }
  /* A data desce para baixo do nome: lado a lado, as duas espremem. */
  .saudacao .tela-titulo { grid-template-columns: minmax(0, 1fr) }
  .saudacao .contexto { text-align: left; margin: var(--s2) 0 0 }
  .tiles { grid-template-columns: 1fr 1fr; row-gap: var(--s4); margin: var(--s4) 0 var(--s5) }
  .pastilha:nth-child(3) { grid-column: 1 / -1 }
  .painel-topo { padding: var(--s4) }
  .mini-cabeca h3 a { display: inline-block; padding: 12px 0; margin: -12px 0 }
  .painel-topo h2 { width: 100%; margin-right: 0 }
  /* Três linhas, e não duas e meia: título, busca, e o par contagem/recarregar.
     Com a busca em `1 1 auto` o `Total` ficava colado nela e o `Recarregar`
     descia sozinho, com o resto da fileira vazio. */
  .busca { flex: 1 1 100% }
  .busca input { width: 100% }
  .total { margin-right: auto }
  /* Alvo de toque: 44 px é o piso escrito em `docs/ui-next.md`. */
  .busca input, .painel-topo button { min-height: 44px }

  /* A tabela vira lista. Sete colunas em 380 px só existem atrás de rolagem
     lateral, e a rolagem lateral come a informação em vez de mostrá-la. */
  .rolagem { overflow-x: visible }
  #projetos { min-width: 0; display: block }
  #projetos colgroup, #projetos thead { display: none }
  #projetos tbody, #projetos tbody tr, #projetos tbody td { display: block; width: auto }
  #projetos tbody tr { padding: var(--s4); border-bottom: 1px solid var(--line-soft) }
  #projetos tbody tr:hover { background: transparent }
  #projetos tbody td { border: 0; padding: 3px 0; font-size: 13.5px }
  /* O rótulo da coluna vira o rótulo da linha, lido do próprio `td`. */
  /* O rótulo é o `.label`, não o número: sem a família escrita ele herdava a
     mono da célula. */
  #projetos tbody td[data-rotulo]::before { content: attr(data-rotulo) ": ";
    font-family: var(--sans); font-size: 11px; font-weight: 500; font-stretch: 125%;
    letter-spacing: .14em; text-transform: uppercase; color: var(--muted) }
  #projetos td.nome { padding-bottom: var(--s2) }
  #projetos td.nome a { font-size: 16px; white-space: normal;
                        padding: 10px 0; margin: -10px 0 }
  #projetos td.nome::before { content: none }
  #projetos td.n { text-align: left; padding: 3px 0 }
  #projetos td.acoes { text-align: left; padding: var(--s3) 0 0 }
  #projetos td.acoes::before { content: none }
  .acao { margin: 0 8px 0 0; width: 44px; height: 44px }
  .legenda { display: none }   /* sem cabeçalho de coluna, não há o que legendar */
}
@media (max-width: 480px) {
  /* Empilhadas, as leituras se separam pelo fio de cima, não pelo da esquerda. */
  .tiles { grid-template-columns: 1fr; row-gap: 0 }
  .pastilha { border-left: 0; border-top: 1px solid var(--line); padding: var(--s3) 0 }
  .pastilha:first-child { border-top: 0; padding-top: 0 }
  .pastilha.aviso, .pastilha.calma { border-left: 2px solid; padding-left: var(--s4) }
  .pastilha.aviso { border-left-color: var(--warn) }
  .pastilha.calma { border-left-color: var(--accent) }
  .essenciais { grid-template-columns: 1fr 1fr }
  .essencial:nth-child(3n) { border-right: 1px solid var(--line-soft) }
  .essencial:nth-child(2n) { border-right: 0 }
  .essencial:last-child { border-bottom: 0 }
  /* `span 2` num grid de duas colunas é a fileira inteira. A mesma regra serve
     as duas larguras, e nunca cria coluna implícita. */
  .essencial:nth-child(5):last-child { grid-column: span 2 }
}

/* ==========================================================================
 * A barra do topo com menu
 *
 * Três colunas de largura fixa nas pontas: só assim os três itens ficam no
 * centro real da tela. Com `margin:auto` num flex, a nav se equilibra entre a
 * marca e a conta, que têm larguras diferentes — e nasce torta.
 * ======================================================================== */
.topbar { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center }
.brand { justify-self: start; min-width: 0 }
.mainnav { justify-self: center; flex: none }
/* Coluna escrita: com a nav escondida (no login), a colocação automática punha
   a ponta direita na coluna do meio. */
.topbar-end { justify-self: end; grid-column: 3 }
.navitem { position: relative; display: inline-flex }
.navitem > button { min-height: 44px; padding: 0 var(--s4); border-color: transparent;
                    background: transparent; color: var(--muted); font-size: 11.5px;
                    font-weight: 500; font-stretch: 125%; letter-spacing: .16em;
                    text-transform: uppercase; white-space: nowrap }
.navitem > button:hover:not(:disabled) { color: var(--ink); border-color: transparent }
.navitem > button[aria-expanded="true"] { color: var(--ink); box-shadow: inset 0 -2px 0 var(--signal) }
.caret { width: 12px; height: 12px; flex: none; transition: transform .15s ease }
.navitem > button[aria-expanded="true"] .caret { transform: rotate(180deg) }
.menu { position: absolute; top: calc(100% + 6px); left: 0; min-width: 252px; z-index: 4;
        background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
        box-shadow: var(--shadow); padding: var(--s2); display: grid; gap: 2px }
/* Link e botão são o mesmo item do menu, e têm de parecer o mesmo item.
   `Criar novo projeto` virou `<button>` porque abre diálogo em vez de trocar de
   tela — e trouxe junto o `button` global: caixa com borda, fundo próprio,
   `min-height: 44px` e conteúdo centralizado. Aqui os quatro são desfeitos; a
   altura de toque continua vindo do `padding`, que é o mesmo do `<a>`. */
/* 12 px em cima e embaixo dão 45 px de alvo: com 10 px o item media 41, abaixo
   dos 44 da regra de toque (pendência de 2026-09, fechada em 2026-10-15). */
.menu a, .menu button { display: flex; align-items: center; justify-content: flex-start;
          gap: 10px; padding: 12px var(--s3); width: 100%; min-height: 0;
          border: 0; background: none; text-align: left;
          border-radius: var(--radius-sm); text-decoration: none; color: var(--ink);
          font-family: inherit; font-size: 13.5px; font-weight: 550 }
.menu a:hover, .menu button:hover:not(:disabled) { background: var(--accent-soft); color: var(--accent) }
/* O item que ainda não existe fica visível e inerte: esconder a entrada faria
   a seção parecer menor do que ela vai ser. */
.menu .breve { display: flex; align-items: center; gap: 10px; padding: 10px var(--s3);
               font-size: 13.5px; font-weight: 550; color: var(--muted); opacity: .72 }
.menu .breve em { margin-left: auto; font-style: normal; font-size: 11px; font-weight: 700;
                  letter-spacing: .8px; text-transform: uppercase; border: 1px solid var(--line);
                  border-radius: 0; padding: 2px 8px }
@media (max-width: 900px) {
  /* A nav desce para uma fileira própria, em vez de espremer a marca e a conta
     até as três coisas ficarem ilegíveis. Ela não rola: rolagem aqui recortava
     o menu — a nota inteira está em `max-width: 760px`. */
  .topbar { grid-template-columns: 1fr auto; padding: var(--s3) var(--s4) }
  /* As duas fileiras são escritas, não deduzidas. A `.mainnav` ocupa as duas
     colunas e, por colocação automática, empurrava a conta para uma terceira
     fileira: a barra media 189 px de altura em 768 px, com a marca, a nav e a
     conta uma embaixo da outra. */
  .brand { grid-row: 1; grid-column: 1 }
  .topbar-end { grid-row: 1; grid-column: 2 }
  .mainnav { grid-row: 2; grid-column: 1 / -1; justify-self: stretch;
             flex-wrap: wrap; padding-bottom: 2px }
  /* O menu abre ancorado na direita quando o item está no fim da fileira, para
     não nascer metade fora da tela. */
  .navitem:last-child .menu { left: auto; right: 0 }
}
@media (max-width: 640px) {
  .brand-mark { width: 28px; height: 28px }
  .brand-name { font-size: 12px; letter-spacing: .2em; padding-left: 10px }
  .brand-name small { display: none }
  /* O nome da conta é mais largo que a marca inteira e não é o que se procura
     num aparelho estreito; o leitor de tela continua lendo. */
  #who { position: absolute; width: 1px; height: 1px; padding: 0;
         margin: -1px; overflow: hidden; clip: rect(0,0,0,0);
         white-space: nowrap; border: 0 }
  .navitem > a, .navitem > button { padding: 0 var(--s3); font-size: 13.5px }
}
/* Com a letra larga da Meta Acústica, os três itens somam 439 px e a barra
   quebra abaixo de ~480 px de janela: `Dispositivos` desce para a segunda
   fileira, à esquerda, e o menu dele, ancorado pela direita, abria 187 px fora
   da tela (medido a 390 px, 2026-10-15). Aqui ele volta a abrir pela esquerda,
   e nenhum menu passa da largura da tela — o de `Dispositivos` mede 395 px. */
@media (max-width: 480px) {
  .navitem:last-child .menu { left: 0; right: auto }
  .menu { min-width: 0; width: max-content; max-width: calc(100vw - 2 * var(--s4)) }
}
/* Os três rótulos com ícone somam 358,3 px, e um aparelho de 390 px oferece
   358: a barra estourava por 0,3 px, e a 360 px o `Dispositivos` era cortado ao
   meio. O ícone é decorativo (`aria-hidden`) e o rótulo diz a mesma coisa, então
   é ele que sai. A seta fica: é o que avisa que o item abre escolha, não tela. */
@media (max-width: 420px) {
  .navitem > a > .icon, .navitem > button > .icon { display: none }
}

/* ------------------------------------------------------------------ diálogo
   `Criar novo projeto` — o primeiro modal do sistema.

   `<dialog>` nativo com `showModal()`: foco preso, `Esc` que fecha, foco de
   volta em quem abriu e o resto da página inerte, sem uma linha de biblioteca.
   O elemento nasce com borda, padding e `max-width` do agente de usuário; os
   três são zerados aqui para que a caixa seja a nossa.

   O rodapé é barra separada, com fundo próprio: é ele que separa o que se
   digita do que se dispara. */
dialog.dlg {
  width: min(480px, calc(100vw - 2 * var(--s4)));
  max-width: none; max-height: calc(100vh - 2 * var(--s5));
  padding: 0; border: 1px solid var(--line); border-radius: var(--radius);
  /* Só a cor: o atalho `background` apagaria os cantos de mira do painel. */
  background-color: var(--surface); color: var(--ink);
  box-shadow: var(--shadow);
  overflow: visible;   /* o recorte é do formulário, não da caixa */
}
dialog.dlg::backdrop { background: #020d0ed0 }

/* O corpo é a única parte que rola. Teto na caixa e `overflow` no meio: sem o
   par, `max-height` não contém — vaza, como vazou o mini-painel da home. */
dialog.dlg form { display: flex; flex-direction: column;
                  max-height: calc(100vh - 2 * var(--s5)); border-radius: inherit }
.dlg-head { padding: var(--s4) var(--s5); border-bottom: 1px solid var(--line-soft); flex: none }
/* O título do diálogo é a cabeça de painel do acesso (`.cab`): largo, fino, em
   caixa-alta, com o fio curto. */
.dlg-head h2 { margin: 0; font-size: 14px; font-weight: 400; font-stretch: 125%; letter-spacing: .2em;
               text-transform: uppercase; line-height: 1.35;
               border-left: 2px solid var(--frame); padding-left: 10px }
.dlg-head p { padding-left: 12px }
.dlg-head p { margin: 2px 0 0 }
.dlg-body { padding: var(--s2) var(--s5) var(--s5); overflow-y: auto; flex: 1 1 auto }
.dlg-body label { margin-bottom: 0 }
.dlg-body textarea {
  display: block; width: 100%; box-sizing: border-box; min-height: 84px;
  margin-top: var(--s2); padding: 10px 11px; font: inherit; font-size: 13px;
  line-height: 1.5; resize: vertical;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink);
}
.dlg-body textarea:hover { border-color: var(--axis) }
.dlg-body .hint { font-size: 11px; line-height: 1.7; color: var(--muted);
                  margin: 6px 0 0; word-break: break-word }
.dlg-body .hint b { color: var(--ink-2); font-weight: 650 }
.dlg-body .slug { font-family: var(--mono); font-size: 11px }
#novo-projeto .req { color: var(--danger); font-weight: 700; font-size: inherit;
                     letter-spacing: 0; text-transform: none; margin-left: 3px }
/* Erro e pergunta do diálogo são a faixa de condição do `.notice`: fio na cor
   do estado, um véu dela, sem caixa. */
.dlg-error {
  margin: var(--s3) 0 0; padding: 10px var(--s3); font-size: 12px; line-height: 1.6;
  border: 0; border-left: 2px solid var(--warn);
  background: color-mix(in srgb, var(--warn) 8%, transparent); color: var(--warn); border-radius: var(--radius-sm);
}
.dlg-error.grave { border-left-color: var(--danger);
                   background: color-mix(in srgb, var(--danger) 9%, transparent); color: var(--danger) }
.dlg-foot {
  display: flex; justify-content: flex-end; align-items: center; gap: var(--s3);
  padding: var(--s4) var(--s5); border-top: 1px solid var(--line-soft);
  background: var(--surface-2); flex: none;
  border-radius: 0 0 var(--radius) var(--radius);
}
.dlg-state { margin: 0; margin-right: auto }
.dlg-state:empty { display: none }
/* "Descartar o que foi preenchido?" — a faixa do clique fora (plano do Painel,
   §6.7). Ocupa a linha inteira do rodapé, acima dos botões dele. */
.dlg-foot { flex-wrap: wrap }
.dlg-pergunta { flex: 1 1 100%; display: flex; flex-wrap: wrap; align-items: center;
                gap: var(--s2) var(--s3); padding: var(--s3); border: 0; border-left: 2px solid var(--warn);
                border-radius: var(--radius-sm); background: color-mix(in srgb, var(--warn) 8%, transparent) }
.dlg-pergunta p { margin: 0 auto 0 0; font-size: 13px; font-weight: 500; color: var(--warn) }
.dlg-pergunta .perigo { color: var(--danger); border-color: var(--danger-line) }

/* A lista de um tipo de dispositivo, no popup do cartão do Painel. */
#tipo-lista { width: min(720px, calc(100vw - 2 * var(--s3))) }
.lista { list-style: none; margin: var(--s3) 0 0; padding: 0; display: grid; gap: var(--s2) }
.dev { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) auto; gap: var(--s2) var(--s4);
       align-items: center; padding: var(--s3); border: 1px solid var(--line-soft);
       border-left: 2px solid var(--frame); border-radius: var(--radius-sm) }
.dev b { display: block; font-size: 14px; font-weight: 500; overflow-wrap: anywhere }
.dev small { display: block; color: var(--muted); font-size: 11.5px; overflow-wrap: anywhere }
.dev-estado { display: grid; gap: 4px; justify-items: start; font-size: 12px; color: var(--muted) }
.lista-vazia { margin: var(--s4) 0 0; font-size: 13.5px }
@media (max-width: 760px) {
  /* O rodapé empilha de baixo para cima nas telas estreitas; a pergunta vai
     para o topo dele, acima dos botões que ela suspende. */
  .dlg-pergunta { order: 1 }
}
@media (max-width: 640px) {
  .dev { grid-template-columns: 1fr }
  .dev .botao { justify-self: stretch }
}

/* No aparelho estreito o diálogo cola embaixo e vira folha: o polegar alcança o
   botão sem atravessar a tela. O primário fica em cima na pilha — é o que se
   quer apertar, e `column-reverse` o coloca lá sem trocar a ordem do foco. */
@media (max-width: 430px) {
  dialog.dlg {
    width: 100%; max-width: 100%; margin: auto 0 0;
    border-radius: var(--radius) var(--radius) 0 0; border-bottom: 0;
    max-height: 92vh;
  }
  dialog.dlg form { max-height: 92vh }
  .dlg-head, .dlg-body, .dlg-foot { padding-left: var(--s4); padding-right: var(--s4) }
  .dlg-foot { flex-direction: column-reverse; align-items: stretch; border-radius: 0 }
  .dlg-foot button { width: 100% }
  .dlg-state { margin-right: 0; text-align: center }
}

/* ------------------------------------------------ configuração do projeto
   Duas colunas: o que identifica o projeto à esquerda, junto com a tabela de
   dispositivos, e a situação à direita. A direita é estreita de propósito —
   são quatro campos curtos, e esticá-los deixaria a descrição sem largura. */
.cfg-grid { display: grid; grid-template-columns: minmax(0, 1fr) 340px;
            gap: var(--s4); align-items: start }
.cfg-col > .card { margin-bottom: var(--s4) }
.cfg-col .section-heading { margin-bottom: var(--s2) }
.cfg-col label { margin: var(--s4) 0 0 }
.cfg-col .duo { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3) }
.cfg-col textarea {
  display: block; width: 100%; box-sizing: border-box; min-height: 92px;
  margin-top: var(--s2); padding: 10px 11px; font: inherit; font-size: 13px;
  line-height: 1.5; resize: vertical;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink);
}
.cfg-col textarea:hover { border-color: var(--axis) }
.cfg-col .footnote { margin-bottom: 0 }
#cfg-counts { border-top: 1px solid var(--line-soft); margin-bottom: 0;
              grid-template-columns: 1fr 1fr }
#cfg-counts > div { padding: var(--s3) var(--s4); border-right: 1px solid var(--line-soft);
                    border-top: 1px solid var(--line-soft); min-width: 0 }
/* O rótulo pode quebrar: `Implantações` em versalete com 1,4 px de entreletra
   não cabe numa célula de 170 px, e cortar o rótulo esconde o que o número é. */
#cfg-counts .label { white-space: normal; overflow-wrap: anywhere }
#cfg-counts > div:nth-child(2n) { border-right: 0 }
#cfg-counts > div:nth-child(-n+2) { border-top: 0 }
#cfg-history-body { margin-top: var(--s3) }

/* A tabela larga rola dentro do próprio cartão. Rolagem lateral na página
   inteira é onde a informação morre — a nota está em `hist.md` §12.6. */
/* 640, e não 700: medido em 2026-09-11, as sete colunas pediam 701 px e a
   coluna esquerda oferecia 500 — a tabela rolava dentro do cartão e as duas
   últimas colunas, inclusive a das ações, ficavam fora da vista. O que sobrou
   de gordura estava na coluna de ações (174 px de dois botões com texto) e no
   `(UTC−3)` repetido em toda linha, que subiu para o cabeçalho. */
.tablewrap { overflow-x: auto }
.tablewrap table { min-width: 640px }
#cfg-devices-wrap td[data-rotulo="Modelo"] { min-width: 130px }
#cfg-devices-wrap td[data-rotulo="Último registro"] { white-space: nowrap }

/* **A coluna das ações fica ancorada à direita.** Medido em 2026-09-11: as sete
   colunas pedem 826 px de conteúdo, e nenhum encurtamento honesto as traz para
   dentro de 666 — encurtar mais seria esconder o modelo ou a data. Então a
   tabela rola, e o que **não** pode sumir de vista é o botão: rolagem que
   esconde a ação é rolagem que o operador não sabe que precisa fazer.

   O fundo é obrigatório: sem ele o conteúdo que passa por baixo aparece
   através da célula ancorada. A borda à esquerda diz que ali há uma junta. */
#cfg-devices-wrap th:last-child,
#cfg-devices-wrap td[data-rotulo="Ações"] {
  position: sticky; right: 0; z-index: 1;
  background: var(--surface); border-left: 1px solid var(--line-soft);
}
#cfg-devices td { vertical-align: middle }
.dev-nome { font-weight: 500; color: var(--ink) }
.dev-id { font-family: var(--mono); font-size: 11.5px; color: var(--muted) }
.dev-acts { display: flex; gap: var(--s2); justify-content: flex-end; white-space: nowrap }
.dev-acts button { min-height: 36px; padding: 6px 9px; font-size: 11.5px }
.dev-acts .perigo { color: var(--danger); border-color: var(--danger-line) }
.dev-acts .perigo:hover:not(:disabled) { color: var(--danger); border-color: var(--danger) }
/* O marcador do estado é o losango do pino, na cor do estado; a palavra ao
   lado diz o estado, a cor só repete. */
#cfg-devices .idade { display: inline-flex; align-items: center; gap: 8px; font-weight: 500 }
#cfg-devices .idade::before { content: ""; width: 7px; height: 7px; border-radius: 0;
                              transform: rotate(45deg); background: currentColor; flex: none }
#cfg-devices .idade.ok { color: var(--accent) }
#cfg-devices .idade.warn { color: var(--warn) }
#cfg-devices .idade.danger { color: var(--danger) }

/* A barra de salvar gruda embaixo e diz o que vai salvar. Um Salvar só na
   tela: dois é onde se perde edição. Sem alteração, os dois botões apagam. */
/* **A barra só flutua quando há o que salvar.** Grudada o tempo todo, ela
   cobria o cartão de Dispositivos enquanto se rolava a tela — 70 px de faixa
   permanente por cima do conteúdo, dizendo "Nada mudou". Sem alteração ela é
   estática e fica onde termina a página. */
.savebar { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
           padding: var(--s3) var(--s4); margin-bottom: var(--s4);
           background-color: var(--surface-2); border: 1px solid var(--line-soft);
           border-radius: var(--radius); box-shadow: var(--shadow) }
.savebar p { margin: 0; margin-right: auto }
.savebar strong { color: var(--ink) }
.savebar.ativa { position: sticky; bottom: var(--s3); z-index: 2 }

/* Duas colunas só quando a coluna esquerda couber a tabela inteira. A conta,
   a 1200 px de janela: 3% de recuo de cada lado tira 72, a coluna da direita
   leva 340, o vão leva 16 e o recuo do cartão leva 48 — sobram 724 para uma
   tabela de 640. Abaixo disso a tabela não cabe, e empilhar é melhor que
   esconder as ações atrás de rolagem lateral. */
@media (max-width: 1199px) {
  .cfg-grid { grid-template-columns: minmax(0, 1fr) }
}
/* No aparelho estreito a tabela vira lista: seis colunas em 390 px só existem
   atrás de rolagem lateral. O rótulo viaja na própria célula (`data-rotulo`),
   porque sem cabeçalho o valor fica órfão. */
@media (max-width: 700px) {
  #cfg-devices-wrap { overflow-x: visible }
  #cfg-devices-wrap table { min-width: 0 }
  #cfg-devices-wrap thead { position: absolute; width: 1px; height: 1px; padding: 0;
                            margin: -1px; overflow: hidden; clip: rect(0,0,0,0);
                            white-space: nowrap; border: 0 }
  #cfg-devices-wrap table, #cfg-devices-wrap tbody, #cfg-devices tr,
  #cfg-devices td { display: block; width: auto }
  #cfg-devices tr { border: 1px solid var(--line); border-radius: var(--radius-sm);
                    padding: var(--s3); margin-bottom: var(--s3) }
  #cfg-devices td { border: 0; padding: 3px 0; display: flex; justify-content: space-between;
                    gap: var(--s3); white-space: normal; font-size: 12px }
  #cfg-devices td::before { content: attr(data-rotulo); color: var(--muted); font-weight: 600 }
  #cfg-devices td:first-child { font-size: 14px; padding-bottom: var(--s2) }
  #cfg-devices td:first-child::before { content: none }
  #cfg-devices-wrap th:last-child,
  #cfg-devices-wrap td[data-rotulo="Ações"] { position: static; border-left: 0 }
  #cfg-devices td:last-child { padding-top: var(--s3) }
  #cfg-devices td:last-child::before { content: none }
  .dev-acts { flex: 1; justify-content: stretch }
  .dev-acts button { flex: 1 }
  .cfg-col .duo { grid-template-columns: 1fr }
  .savebar { flex-direction: column; align-items: stretch }
  .savebar p { margin-right: 0; text-align: center }
  .savebar button { width: 100% }
}

/* Mapa e dose do NoiseAtWork no Painel do Estudo (hist.md §37). */
.naw-mapa { border-top: 1px solid var(--line-soft); padding: var(--s3) 0 }
.naw-mapa h4 { margin: 0; padding: var(--s2) var(--s5); font-size: 13px }
.naw-svg { display: block; width: 100%; max-height: 440px; background: var(--sunken);
  border: 1px solid var(--line-soft) }
.naw-ponto { fill: var(--ink); opacity: .55 }
.naw-vivo { fill: var(--signal); stroke: var(--band); stroke-width: .3 }
.naw-rotulo { fill: var(--ink); paint-order: stroke; stroke: var(--band); stroke-width: .4 }
.naw-legenda { display: flex; flex-wrap: wrap; gap: var(--s1) var(--s3); list-style: none;
  margin: var(--s2) 0; padding: 0 var(--s5); font-size: 11px; color: var(--muted) }
.naw-cor { display: inline-block; width: 10px; height: 10px; vertical-align: -1px }
@media (max-width: 700px) {
  /* A tabela da dose vira lista, como a dos projetos: rolagem lateral come a informação. */
  .naw-mapa thead { display: none }
  .naw-mapa tbody, .naw-mapa tbody tr, .naw-mapa tbody td { display: block; width: auto }
  .naw-mapa tbody tr { padding: var(--s3) var(--s5); border-bottom: 1px solid var(--line-soft) }
  .naw-mapa tbody td { border: 0; padding: 2px 0; font-size: 13px }
  .naw-mapa tbody td[data-rotulo]::before { content: attr(data-rotulo) ": "; font-family: var(--sans);
    font-size: 11px; font-weight: 500; font-stretch: 125%; letter-spacing: .14em;
    text-transform: uppercase; color: var(--muted) }
}

/* ==========================================================================
 * Os gráficos por tipo de sonda (`plano-graficos.md`, operador 2026-10-20 e
 * 2026-10-21): o quadro por camadas do Tempo real e da aba Séries. A forma diz o
 * que o dado é; a cor do tipo (`--series`, `--series-2`) só reforça.
 * ======================================================================== */
.quadro { min-height: 0; overflow: hidden }
.quadro svg { display: block; width: 100%; height: auto; touch-action: pan-y; cursor: crosshair }
.quadro text { font-family: var(--mono); fill: var(--muted); font-size: 10px; font-variant-numeric: tabular-nums }
.quadro .axis-label { fill: var(--ink-2); font-weight: 500; letter-spacing: .4px }
.quadro .grid { stroke: var(--grid); stroke-width: 1 }
.quadro .axis { stroke: var(--axis); stroke-width: 1 }
.quadro .gap { stroke: var(--gap-line); stroke-width: 1 }
.quadro .gap-bg { fill: var(--gap-fill) }
.quadro .gap-hatch { stroke: var(--gap-line); stroke-width: 1 }
.quadro .series { fill: none; stroke: var(--series); stroke-width: 1.6; stroke-linejoin: round }
.quadro .series.ctx { stroke: var(--series-2) }
.quadro .area { fill: var(--series); fill-opacity: .16; stroke: none }
.quadro .area.ctx { fill: var(--series-2); fill-opacity: .3 }
.quadro .points { fill: none; stroke: var(--series); stroke-width: 2.4; stroke-linecap: round }
.quadro .points.ctx { stroke: var(--series-2) }
.quadro .points.dense { stroke-width: 1.2 }
.quadro .risco { fill: none; stroke: var(--series-2); stroke-width: 1.4; stroke-linecap: round }
.quadro .haste { fill: none; stroke: var(--series); stroke-width: 1 }
.quadro .coluna { fill: var(--series) }
.quadro .seta path { fill: none; stroke: var(--series-2); stroke-width: 1.5; stroke-linecap: round }
.quadro .flags { stroke: var(--warn); stroke-width: 1.4; fill: var(--warn-bg) }
.quadro .anel { fill: none; stroke: var(--ink); stroke-width: 1.6 }
.quadro .destaque { fill: var(--ink); font-family: var(--sans); font-size: 11px; font-weight: 600;
                    paint-order: stroke; stroke: var(--surface); stroke-width: 3px; stroke-linejoin: round }
.quadro .cursor { stroke: var(--accent); stroke-width: 1; stroke-dasharray: 4 3 }
.quadro .cursor-dot { fill: var(--surface); stroke: var(--series); stroke-width: 2.4 }
.quadro .cursor-dot.ctx { stroke: var(--series-2) }
.quadro .selection { fill: var(--accent); opacity: .16 }
.quadro:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px }
.legenda-quadro { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); font-size: 12px; color: var(--ink-2) }
.legenda-quadro li { gap: var(--s2) }
.chave { flex: none; display: inline-block; width: 16px; height: 10px }
.chave.area { background: color-mix(in srgb, var(--series) 28%, transparent); border-top: 2px solid var(--series) }
.chave.area.ctx { background: color-mix(in srgb, var(--series-2) 40%, transparent); border-top-color: var(--series-2) }
.chave.linha { height: 2px; background: var(--series) }
.chave.risco { width: 2px; background: var(--series-2); margin: 0 7px }
.chave.coluna { background: var(--series) }
.chave.ponto { width: 8px; height: 8px; border-radius: 50%; background: var(--series); margin: 0 4px }
.chave.ponto.ctx { background: var(--series-2); opacity: .7 }
.chave.seta { width: auto; height: auto; color: var(--series-2); font-weight: 700; line-height: 1 }
.leitura .faisca { display: block; width: 100%; max-width: 120px; height: 26px; margin-top: 4px }
.faisca-linha { fill: none; stroke: var(--muted); stroke-width: 1.4 }
#live-apoio:empty { display: none }
.quadro .ponto { fill: var(--series) }
.quadro .ponto.ctx { fill: var(--series-2); fill-opacity: .55 }
.mini-cab { margin: var(--s3) 0 2px; font-size: 11px; color: var(--muted) }
.mini-cab strong { font-size: 11px; font-weight: 500; font-stretch: 125%; letter-spacing: .14em;
                   text-transform: uppercase; color: var(--ink-2) }
#tecnicos:empty { display: none }
#tecnicos > .chartcard { margin-top: var(--s4) }
/* O cartão de compartilhar (P5): o quadro do gráfico é desenhado fora da tela e copiado para o canvas. */
.cartao-fora { position: fixed; left: -10000px; top: 0; pointer-events: none }
.chart-top .compartilhar { margin-left: auto }
#live-share-estado:empty { display: none }
/* A aba Relatório do projeto (P4, `hist.md` §47): o que gerar à esquerda, os já gerados à direita. */
.rel-grid { display: grid; grid-template-columns: minmax(0, 380px) minmax(0, 1fr); gap: var(--s4); align-items: start }
.rel-opcoes .duo { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s3) }
.rel-opcoes .duo input { width: 100%; min-width: 0; box-sizing: border-box }
.rel-opcoes .hint { font-size: 11px; line-height: 1.7; color: var(--muted); margin: 0 0 var(--s3) }
.rel-ficha { margin: 0 }
.rel-ficha dt { font-size: 11px; font-weight: 500; font-stretch: 125%; letter-spacing: .19em;
                text-transform: uppercase; color: var(--ink-2); margin-top: var(--s4) }
.rel-ficha dd { margin: var(--s1) 0 0; font-size: 13.5px }
.rel-ficha .lista { margin-top: var(--s1); gap: 2px }
#rel-gerar { width: 100%; margin-top: var(--s5) }
.rel-arquivos { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center }
.rel-arquivos small { flex-basis: 100%; overflow-wrap: anywhere }
.rel-lista .tablewrap table { min-width: 520px }
.verificar { max-width: 760px }
.ver-veredito.ok { border-left-color: var(--accent); background: color-mix(in srgb, var(--accent) 9%, transparent) }
.ver-veredito p { margin: var(--s1) 0 0 }
.ver-veredito .mono { font-family: var(--mono); overflow-wrap: anywhere }
@media (max-width: 760px) {
  .rel-grid, .rel-opcoes .duo { grid-template-columns: minmax(0, 1fr) }
  /* No celular cada relatório empilha, com os botões à vista, sem rolagem lateral. */
  .rel-lista .tablewrap table { min-width: 0 }
  .rel-lista thead { display: none }
  .rel-lista tr { display: grid; gap: 2px; padding: var(--s3) 0; border-bottom: 1px solid var(--line-soft) }
  .rel-lista td { border: 0; padding: 0; white-space: normal }
  .rel-lista td.rel-arquivos { margin-top: var(--s2) }
}

/* ==========================================================================
 * A tela no celular, etapa 1 (estudo e 14 decisões de 2026-10-21, `hist.md` §49)
 *
 * Abaixo de 760 px: a barra numa linha com o Menu e a gaveta, a cabeça compacta, as
 * abas que rolam com a borda esmaecida e o aviso de dose numa linha. Dois consertos
 * valem em toda largura, porque são defeito: a letra dos menus e a nota de versão.
 * ======================================================================== */
/* Link e botão do menu com a mesma letra. Dentro da `.mainnav`, o link herdava do
   `.mainnav a` a caixa alta larga do item da barra, e o botão não. */
.menu a, .menu button { text-transform: none; letter-spacing: normal; font-stretch: 100%;
                        white-space: normal }
.menu-celular, .volta { display: none }
.icone-celular .icon { display: none }

/* A gaveta: um `<dialog>` preso à direita, na altura toda. Só o botão Menu do celular a abre. */
.gaveta { position: fixed; inset: 0 0 0 auto; margin: 0; width: min(340px, 88vw); max-width: none;
          height: 100%; max-height: none; padding: env(safe-area-inset-top, 0px) 0 env(safe-area-inset-bottom, 0px);
          border: 0; border-left: 1px solid var(--line); border-radius: 0;
          background: var(--surface); color: var(--ink); overflow-y: auto }
.gaveta::backdrop { background: color-mix(in srgb, var(--band) 70%, transparent) }
.gaveta-cab { display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
              min-height: 56px; padding: 6px var(--s3) 6px var(--s4); border-bottom: 1px solid var(--line-soft) }
.gaveta-cab .eyebrow { margin: 0 }
.gaveta-cab button { width: 44px; height: 44px; min-height: 44px; padding: 0; display: grid; place-items: center }
.gaveta-nav { display: grid; padding: 0 var(--s4) var(--s2) }
.gaveta-sec { font-size: 11px; font-weight: 500; font-stretch: 125%; letter-spacing: .16em;
              text-transform: uppercase; color: var(--muted); margin: var(--s4) 0 var(--s1) }
.gaveta-item { display: flex; align-items: center; justify-content: space-between; gap: var(--s2);
               width: 100%; min-height: 48px; padding: 0 var(--s2); border: 0; border-radius: 0;
               border-bottom: 1px solid var(--line-soft); background: none; color: var(--ink);
               font: inherit; font-size: 15px; text-align: left; text-decoration: none }
.gaveta-item[aria-current="page"] { box-shadow: inset 2px 0 0 var(--signal); padding-left: var(--s3) }
.gaveta-item.breve { color: var(--muted) }
.gaveta-item.breve em { flex: none; font-style: normal; font-size: 11px; letter-spacing: .08em;
                        text-transform: uppercase; white-space: nowrap; border: 1px solid var(--line); padding: 2px 6px }
.gaveta-bloco { padding: 0 var(--s4) var(--s3) }
.segmento { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); border: 1px solid var(--line) }
.segmento button { min-height: 44px; border: 0; border-radius: 0; background: none; color: var(--ink-2); font-size: 14px }
.segmento button + button { border-left: 1px solid var(--line) }
.segmento button[aria-pressed="true"] { background: var(--accent-soft); color: var(--accent);
                                        box-shadow: inset 0 -2px 0 var(--signal) }
/* Os botões X, Y e Z da dispersão (§55): o mesmo segmento do tema, na ponta do título. */
.eixos-disp { width: min(100%, 300px); margin-left: auto }
.gaveta-who { font-size: 14px; margin: 0 0 var(--s2); overflow-wrap: anywhere }
#gaveta-sair, #gaveta-perfil { width: 100% }
#gaveta-perfil { margin-bottom: var(--s2) }
/* O Menu das três barras em toda largura (§63): logado, o tema e a conta saem da barra e
   moram nele. No acesso não há menu, e o botão de tema fica na barra. No computador as
   seções estão na barra; a gaveta leva só o tema e a conta. */
.topbar-end:has(> #abre-menu:not([hidden])) > :is(#tema, .account) { display: none }
.menu-celular:not([hidden]) { display: inline-flex; align-items: center; gap: var(--s2);
                              min-height: 44px; padding: 0 var(--s3) }
@media (min-width: 761px) { .gaveta-nav { display: none } }
/* A tela de perfil. */
.perfil-dados { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 6px var(--s4);
                margin: var(--s3) 0 0; font-size: 14px }
.perfil-dados dt { color: var(--muted) }
.perfil-dados dd { margin: 0; overflow-wrap: anywhere }
.perfil-bloco { display: grid; gap: var(--s2); margin-top: var(--s3) }
.perfil-bloco label { display: grid; gap: 4px; font-size: 13px; color: var(--ink-2) }
.perfil-bloco input { min-height: 44px; padding: 0 11px; font: inherit; font-size: 14px;
                      border: 1px solid var(--line); border-radius: var(--radius-sm);
                      background: var(--surface); color: var(--ink) }
.perfil-bloco button { justify-self: start }
.perfil-bloco p { margin: 0 }
.perfil-bloco .erro { color: var(--warn) }
/* O sino e o painel das notificações (§64). O painel abre sob a barra, à direita. */
.sino:not([hidden]) { position: relative; display: inline-grid; place-items: center;
                      width: 44px; min-width: 44px; padding: 0 }
.sino .icon { width: 20px; height: 20px }
.sino-n { position: absolute; top: 4px; right: 3px; min-width: 18px; height: 18px; padding: 0 5px;
          border-radius: 9px; background: var(--signal); color: var(--band);
          font: 600 11px/18px var(--mono); text-align: center }
.notif-dlg { position: fixed; inset: 64px 16px auto auto; margin: 0; padding: 0; max-width: none;
             width: min(440px, calc(100vw - 32px)); max-height: calc(100vh - 88px); overflow-y: auto;
             border: 1px solid var(--line); border-radius: var(--radius);
             background-color: var(--surface); color: var(--ink); box-shadow: var(--shadow) }
.notif-dlg::backdrop { background: color-mix(in srgb, var(--band) 35%, transparent) }
.notif-topo { display: grid; gap: var(--s2); padding: var(--s3) var(--s4) }
.notif-topo #notif-todas-lidas { justify-self: end }
.notif-lista { list-style: none; margin: 0; padding: 0 }
.notif-item { padding: var(--s3) var(--s4); border-top: 1px solid var(--line-soft) }
.notif-item.nova { box-shadow: inset 2px 0 0 var(--signal); background: color-mix(in srgb, var(--signal) 5%, transparent) }
.notif-item.grave.nova { box-shadow: inset 2px 0 0 var(--danger) }
.notif-cab { display: flex; justify-content: space-between; gap: var(--s2); margin: 0 0 4px;
             font-size: 11px; color: var(--muted) }
.notif-tag { letter-spacing: .14em; text-transform: uppercase; font-stretch: 125% }
.notif-quando { font-family: var(--mono) }
.notif-titulo { display: block; font-size: 14px; font-weight: 600 }
.notif-texto { margin: 4px 0 0; font-size: 13px; color: var(--ink-2) }
.notif-item:not(.nova) .notif-titulo { font-weight: 400; color: var(--ink-2) }
.notif-acoes { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: var(--s2) }
.notif-acoes a { text-decoration: none; display: inline-flex; align-items: center; min-height: 44px;
                 padding: 0 var(--s3); border: 1px solid var(--line); border-radius: var(--radius-sm); color: var(--ink) }
.notif-vazio { padding: var(--s4); color: var(--muted); font-size: 14px }
.notif-publicar { padding: var(--s3) var(--s4); border-top: 1px solid var(--line) }
.notif-publicar summary { cursor: pointer; min-height: 44px; display: flex; align-items: center }
.notif-publicar form { display: grid; gap: var(--s2) }
.notif-publicar label { display: grid; gap: 4px; font-size: 13px; color: var(--ink-2) }
.notif-publicar input, .notif-publicar textarea { min-height: 44px; padding: 8px 11px; font: inherit; font-size: 14px;
                 border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface); color: var(--ink) }
.notif-publicar button { justify-self: start }
.notif-publicar .erro { color: var(--warn) }
@media (max-width: 760px) { .notif-dlg { inset: 56px 8px auto 8px; width: auto } }

@media (max-width: 760px) {
  /* A barra numa linha (~56 px): a marca e o Menu. Logado, o tema e a conta vão para a
     gaveta; no acesso o botão de tema continua na barra, porque não há gaveta. */
  .topbar { grid-template-columns: minmax(0, 1fr) auto; padding: 6px var(--s4) }
  .mainnav { display: none }

  /* A cabeça: a migalha vira o link de volta numa linha, o título fica em 20 px, e os
     botões de detalhes e de atualizar viram ícones na ponta da linha das pastilhas. */
  .tela-titulo .crumb { display: none }
  .volta:not([hidden]) { display: block; max-width: 100%; min-height: 44px; line-height: 44px;
                         margin: calc(-1 * var(--s3)) 0 0; font-size: 13px; color: var(--accent);
                         text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
  .page .tela-titulo h1 { font-size: 20px; line-height: 1.25 }
  .head { margin-bottom: var(--s2) }
  .head .right { width: auto; margin-left: auto; flex-wrap: nowrap }
  /* Na sonda os ícones sobem para a linha do título (§57): na linha das pastilhas eles
     não cabiam e ficavam numa linha só deles. */
  #sonda-view .head h1 { width: auto; flex: 1 1 0; min-width: 0 }
  #sonda-view .head .right { order: 1; align-self: flex-start }
  #sonda-view .head .chips { order: 2; flex-basis: 100% }
  .icone-celular { width: 44px; min-height: 44px; padding: 0; display: inline-grid; place-items: center }
  .icone-celular .icon { display: block; width: 18px; height: 18px }
  .icone-celular .txt { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
                        overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0 }

  /* As abas: rolam como antes, e a borda esmaece do lado que tem mais (`ajustaFaixa`). Na
     direita, o esmaecido começa na borda da aba cortada (`--corte`), e o corte cai entre
     duas abas: da cortada só aparece o começo, sumindo em 36 px. */
  .tabs a { padding: 12px var(--s3); letter-spacing: .12em }
  .tabs.mais-direita {
    -webkit-mask-image: linear-gradient(90deg, #000 var(--corte, calc(100% - 56px)), transparent calc(var(--corte, calc(100% - 56px)) + 36px));
    mask-image: linear-gradient(90deg, #000 var(--corte, calc(100% - 56px)), transparent calc(var(--corte, calc(100% - 56px)) + 36px)) }
  .tabs.mais-esquerda {
    -webkit-mask-image: linear-gradient(90deg, transparent calc(var(--corte-esq, 40px) - 36px), #000 var(--corte-esq, 40px));
    mask-image: linear-gradient(90deg, transparent calc(var(--corte-esq, 40px) - 36px), #000 var(--corte-esq, 40px)) }
  .tabs.mais-esquerda.mais-direita {
    -webkit-mask-image: linear-gradient(90deg, transparent calc(var(--corte-esq, 40px) - 36px), #000 var(--corte-esq, 40px), #000 var(--corte, calc(100% - 56px)), transparent calc(var(--corte, calc(100% - 56px)) + 36px));
    mask-image: linear-gradient(90deg, transparent calc(var(--corte-esq, 40px) - 36px), #000 var(--corte-esq, 40px), #000 var(--corte, calc(100% - 56px)), transparent calc(var(--corte, calc(100% - 56px)) + 36px)) }


  /* Os consertos: o botão de uma seção desce para baixo do título, na largura toda, e a
     nota desce também; "Configurar", "Remover" e "Ver origem" com 44 px de alvo. */
  .section-heading { flex-wrap: wrap }
  .section-heading > span { order: 2; flex-basis: 100% }
  .section-heading > button { order: 3; flex: 1 1 100% }
  #config-version, #config-site-version { text-align: left }
  .geo-head { flex-wrap: wrap; gap: var(--s2) }
  .geo-head button { flex: 1 1 100% }
  .dev-acts button { flex: 1 1 0; min-height: 44px }
  #observations summary { display: inline-flex; align-items: center; min-height: 44px }
}

/* ==========================================================================
 * A tela no celular, etapa 2 (decisões 7, 8, 9 e 13 de 2026-10-21, `hist.md` §50)
 *
 * Abaixo de 760 px: a faixa de filtros nasce recolhida, com o recorte em uso e os atalhos
 * de período; Dados e Equipamentos em linhas de dois andares. A frase e a altura dos
 * gráficos mudam no `app.mjs`. No computador, nada muda.
 * ======================================================================== */
.filtro-resumo, .periodos { display: none }

@media (max-width: 760px) {
  /* A faixa recolhida: o recorte em uso e o Alterar numa linha, os atalhos embaixo. O
     Alterar abre a faixa de sempre; aplicar recolhe de novo. */
  .filtro-resumo { display: flex; align-items: center; justify-content: space-between; gap: var(--s3) }
  #filtro-resumo { margin: 0; min-width: 0; font-size: 13px; line-height: 1.4; color: var(--ink-2) }
  #filtro-alterar { flex: none; min-height: 44px }
  .periodos { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s2); margin-top: var(--s2) }
  .periodos button { min-height: 44px; padding: 0 var(--s1); white-space: nowrap }
  .periodos button[aria-pressed="true"] { border-color: var(--accent); color: var(--accent);
                                          box-shadow: inset 0 -2px 0 var(--signal) }
  #filters:not(.aberto) > :is(.filterbar-line, #filter-state) { display: none }
  #filters.aberto > #filter-main { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--line-soft) }

  /* As duas tabelas em linhas de dois andares, sem rolar de lado. O cabeçalho sai da tela,
     mas não do leitor de tela. */
  :is(#tab-dados, #equipamentos-view) thead { position: absolute; width: 1px; height: 1px; overflow: hidden;
                                              clip: rect(0,0,0,0); white-space: nowrap }
  :is(#tab-dados, #equipamentos-view) .table-scroll { overflow-x: hidden; margin: 0 }

  /* Dados: horário e valor em cima; embaixo, a natureza à esquerda e o "Ver origem" (ou as
     sinalizações) na linha inteira, que abre por baixo. Comparando, a letra da série vem
     antes do horário. */
  #observations tr { display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
                     padding-top: var(--s2); border-bottom: 1px solid var(--line-soft) }
  #observations td { border: 0; padding: 0; white-space: normal }
  #observations td.side { grid-column: 1; width: auto; padding-right: var(--s2) }
  #observations td:nth-last-child(4) { grid-column: 2 }
  #observations td:nth-last-child(3) { grid-column: 3; padding-left: var(--s3); text-align: right }
  #observations td:nth-last-child(2) { grid-column: 1 / 3; grid-row: 2; align-self: start;
                                        line-height: 44px; color: var(--muted) }
  #observations td:last-child { grid-column: 1 / -1; grid-row: 2 }
  #observations summary { display: flex; align-items: center; justify-content: flex-end; min-height: 44px }

  /* Equipamentos: o identificador, o fabricante e o modelo em cima; a série e o estado
     embaixo; as unidades sensoras, quando há, numa linha miúda por último. */
  #equipamentos tr { display: grid; grid-template-columns: auto auto minmax(0, 1fr); column-gap: var(--s2);
                     row-gap: 2px; padding: var(--s3) 0; border-bottom: 1px solid var(--line-soft) }
  #equipamentos td { border: 0; padding: 0; white-space: normal }
  #equipamentos td:nth-child(4) { grid-column: 1 / 3 }
  #equipamentos td:nth-child(4)::before { content: "Série "; font-family: var(--sans) }
  #equipamentos td:nth-child(6) { grid-column: 3; grid-row: 2; text-align: right }
  #equipamentos td:nth-child(5) { grid-column: 1 / -1; font-size: 12px; color: var(--muted) }
  #equipamentos td:nth-child(5)::before { content: "Unidades: " }
  #equipamentos td[colspan] { grid-column: 1 / -1 }
}
