:root {
    --color-bg: #f4f6fb;
    --color-surface: #ffffff;
    --color-border: #e3e8ef;
    --color-border-strong: #cbd5e1;
    --color-text-primary: #17233d;
    --color-text-secondary: #64748b;
    --color-text-muted: #94a3b8;

    --color-primary: #2563eb;
    --color-primary-bg: #eaf1ff;
    --color-primary-dark: #1d4ed8;

    --color-success: #15803d;
    --color-success-bg: #dcfce7;
    --color-warning: #a16207;
    --color-warning-bg: #fef3c7;
    --color-danger: #b91c1c;
    --color-danger-bg: #fee2e2;
    --color-neutral: #64748b;
    --color-neutral-bg: #eef2f6;

    --color-chart-x: #2563eb;
    --color-chart-y: #0d9488;

    /* Tokens de marca MMF (Dia 12, checkpoints 12C-B a 12C-D) — separados
       deliberadamente de --color-primary: o azul continua representando
       tecnologia/hardware/dados (sensores, gateways, gráficos, "info"),
       nunca reaproveitado para marca. Laranja é usado com moderação —
       identifica/destaca ação, nunca domina a tela (neutros continuam a
       base visual predominante do app autenticado).
         --mmf-orange       acento/borda/foco — uso pontual, nunca texto
                             de corpo sobre branco (contraste limitado).
         --mmf-orange-dark  fundo sólido de CTA (.btn--primary) — melhor
                             contraste que --mmf-orange para texto branco.
         --mmf-orange-darker hover do CTA sólido.
         --mmf-orange-bg    tinta MUITO sutil para estados selecionados
                             (sidebar ativa, tabs/toggles) — calibrada para
                             ficar discreta sobre branco, nunca um bloco
                             sólido de cor. */
    --mmf-orange: #ed702d;
    --mmf-orange-dark: #ae3505;
    --mmf-orange-darker: #8b2a04;
    --mmf-orange-bg: #fdf1ea;

    --radius-md: 10px;
    --radius-lg: 14px;
    --shadow-card: 0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 1px rgba(15, 23, 42, 0.04);
    --sidebar-width: 232px;
    --header-height: 64px;
}

* {
    box-sizing: border-box;
}

html, body {
    margin: 0;
    padding: 0;
    height: 100%;
}

body {
    font-family: "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, Arial, sans-serif;
    background: var(--color-bg);
    color: var(--color-text-primary);
    -webkit-font-smoothing: antialiased;
}

a {
    color: inherit;
}

.app {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

/* Header */

.app-header {
    height: var(--header-height);
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 28px;
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    position: sticky;
    top: 0;
    z-index: 20;
}

.app-header__brand {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 17px;
    font-weight: 600;
}

.brand-icon {
    /* Dia 12, checkpoint 12C-D: único acento de marca do header — o
       restante permanece neutro/branco (ver .refresh-indicator abaixo). */
    display: flex;
    color: var(--mmf-orange);
}

.brand-icon svg {
    width: 26px;
    height: 26px;
}

.app-header__actions {
    display: flex;
    align-items: center;
    gap: 18px;
}

.refresh-indicator {
    /* Dia 12, checkpoint 12C-D: fundo neutro — o azul aqui era só
       decorativo, sem significado (o dot verde/vermelho dentro é que é
       semântico e continua inalterado abaixo). */
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--color-text-secondary);
    background: var(--color-neutral-bg);
    padding: 6px 12px;
    border-radius: 999px;
    white-space: nowrap;
}

.refresh-indicator__dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--color-success);
    flex-shrink: 0;
}

.refresh-indicator__dot--error {
    background: var(--color-danger);
}

/* Client atual/seletor (Dia 12, checkpoint 12C-B). Seleção de CONTEXTO,
   não de autorização — a lista renderizada aqui já vem filtrada pelo
   backend (get_allowed_clients via _pagina_contexto), nunca uma lista
   global de Clients. */

.client-badge {
    display: inline-flex;
    align-items: center;
    padding: 6px 12px;
    border-radius: 999px;
    background: var(--color-neutral-bg);
    border: 1px solid var(--color-border);
    border-left: 3px solid var(--mmf-orange);
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-primary);
    white-space: nowrap;
}

/* position:relative no wrapper é o que permite ao menu (position:absolute
   logo abaixo) se posicionar relativo a ESTE elemento, não à página —
   sem isso o menu flutuaria a partir do <body>. */
.client-switcher {
    position: relative;
}

.client-switcher__current {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border-radius: 999px;
    background: var(--color-neutral-bg);
    border: 1px solid var(--color-border);
    border-left: 3px solid var(--mmf-orange);
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-primary);
    white-space: nowrap;
    cursor: pointer;
    list-style: none;
    user-select: none;
}

/* Suprime o marcador nativo de <summary> (triângulo/bullet) nas duas
   famílias de motor de renderização: WebKit/Blink usa o pseudo-elemento
   proprietário abaixo; Firefox (e navegadores que seguem o spec de
   ::marker) usa list-style — reforçado aqui além do list-style:none já
   aplicado no seletor acima, para não depender só de herança. */
.client-switcher__current::-webkit-details-marker {
    display: none;
}

.client-switcher__current::marker {
    content: "";
}

.client-switcher__current::after {
    content: "";
    width: 6px;
    height: 6px;
    margin-left: 2px;
    border-right: 1.5px solid var(--color-text-secondary);
    border-bottom: 1.5px solid var(--color-text-secondary);
    transform: rotate(45deg);
    transition: transform 0.15s ease;
}

.client-switcher[open] .client-switcher__current::after {
    transform: rotate(-135deg);
}

.client-switcher__current:hover {
    background: var(--color-border);
}

.client-switcher__current:focus-visible {
    outline: 2px solid var(--mmf-orange);
    outline-offset: 2px;
}

/* Menu do dropdown: container real (fundo/borda/sombra/raio), nunca a
   aparência crua de <ul> de navegador. right:0 alinha a borda direita do
   menu à borda direita do PRÓPRIO .client-switcher (âncora relativa, não
   o viewport) — como o switcher nunca é o último item da linha (username
   e Sair vêm depois dele), isso mantém o menu dentro da área visível do
   header. max-width é uma rede de segurança adicional contra corte em
   janelas muito estreitas, independente de onde o switcher esteja. */
.client-switcher__menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    left: auto;
    z-index: 30;
    min-width: 160px;
    max-width: min(240px, calc(100vw - 24px));
    margin: 0;
    padding: 6px;
    list-style: none;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
}

/* Reset explícito do <li>, além do list-style:none herdado do <ul> acima
   — não depender só de herança para o menu nunca aparecer com bullets ou
   com o recuo padrão de lista (padding-inline-start do user-agent). */
.client-switcher__menu li {
    list-style: none;
    margin: 0;
    padding: 0;
}

.client-switcher__option {
    display: block;
    padding: 8px 10px;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 500;
    color: var(--color-text-primary);
    text-decoration: none;
}

.client-switcher__option:hover {
    background: var(--color-neutral-bg);
}

.client-switcher__option:focus-visible {
    outline: 2px solid var(--mmf-orange);
    outline-offset: -2px;
}

.client-switcher__option--active {
    color: var(--mmf-orange-dark);
    font-weight: 700;
}

/* Estado central de contexto de Client (Dia 12, checkpoint 12C-C) —
   substitui os cards "Carregando..."/tabelas/painéis quando estado_cliente
   != "ok" (ver base.html/_pagina_contexto). Visual deliberadamente simples
   (ainda sem harmonização completa da paleta): mesmo vocabulário de
   .empty-state (borda, raio, texto centralizado), com um passo a mais de
   estrutura (ícone/título/descrição/ação) e o laranja MMF só no ícone. */

.client-state {
    max-width: 420px;
    margin: 48px auto;
    padding: 48px 32px;
    text-align: center;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
}

.client-state__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    margin-bottom: 18px;
    border-radius: 50%;
    background: var(--color-neutral-bg);
    color: var(--mmf-orange);
}

.client-state__icon svg {
    width: 24px;
    height: 24px;
}

.client-state__title {
    margin: 0 0 8px;
    font-size: 18px;
    font-weight: 700;
    color: var(--color-text-primary);
}

.client-state__description {
    margin: 0;
    font-size: 14px;
    line-height: 1.5;
    color: var(--color-text-secondary);
}

.client-state__hint {
    margin: 10px 0 0;
    font-size: 13px;
    color: var(--color-text-muted);
}

.client-state__action {
    /* A ação é renderizada num <a> (base.html) — inline por padrão do HTML,
       e margin-top não tem efeito de layout em elementos inline (a causa
       exata do botão colar na descrição). inline-block faz o navegador
       respeitar a margem vertical, sem precisar de flex/gap no container
       nem de posicionamento artificial — mantém o centralizado herdado de
       .client-state { text-align: center } e não altera .client-state,
       .client-state__icon/__title/__description/__hint. */
    display: inline-block;
    margin-top: 20px;
}

/* Body layout */

.app-body {
    display: flex;
    flex: 1;
    min-height: 0;
}

.sidebar {
    width: var(--sidebar-width);
    flex-shrink: 0;
    background: var(--color-surface);
    border-right: 1px solid var(--color-border);
    padding: 18px 12px;
}

.sidebar-nav {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.nav-item {
    display: flex;
    align-items: center;
    gap: 12px;
    /* padding-left 11px + filete de 3px (transparente quando inativo) =
       14px, igual às outras bordas — o item ativo ganha o filete sem
       deslocar nenhum item (ativo ou não) lateralmente. */
    padding: 10px 14px 10px 11px;
    border-left: 3px solid transparent;
    border-radius: 8px;
    font-size: 14px;
    text-decoration: none;
    color: var(--color-text-secondary);
}

.nav-item__icon {
    display: flex;
    flex-shrink: 0;
}

.nav-item__icon svg {
    width: 18px;
    height: 18px;
}

/* Dia 12, checkpoint 12C-D: fundo laranja muito suave + filete lateral +
   ícone laranja, texto grafite (não laranja) — item ativo identifica a
   marca sem virar um bloco sólido de cor. */
.nav-item--active {
    background: var(--mmf-orange-bg);
    border-left-color: var(--mmf-orange);
    color: var(--color-text-primary);
    font-weight: 600;
}

.nav-item--active .nav-item__icon {
    color: var(--mmf-orange);
}

.nav-item--disabled {
    color: var(--color-text-muted);
    cursor: default;
}

.nav-item--disabled .nav-item__label {
    display: flex;
    flex-direction: column;
    line-height: 1.3;
}

.nav-item--disabled .nav-item__label::after {
    content: "em breve";
    font-size: 10px;
    color: var(--color-text-muted);
    font-weight: 400;
}

.content {
    flex: 1;
    padding: 28px 32px 40px;
    overflow-y: auto;
    min-width: 0;
}

.page-header {
    margin-bottom: 22px;
}

.page-header h1 {
    margin: 0 0 4px;
    font-size: 24px;
    font-weight: 700;
}

.page-subtitle {
    margin: 0;
    color: var(--color-text-secondary);
    font-size: 14px;
}

/* Cards */

.card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
}

.summary-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 16px;
    margin-bottom: 24px;
}

.summary-card {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    padding: 18px 20px;
}

.summary-card[hidden] {
    /* Mesmo conflito de cascata já documentado em .filter-field/.view-toggle/
       etc.: a regra de autor ("display: flex") venceria o "display: none" de
       user-agent para [hidden] — necessário para o card 3 do resumo, oculto
       no modo Pluviometria da página Leituras (ver leituras.js:
       renderResumoPluviometria()). */
    display: none;
}

.summary-card__icon {
    width: 40px;
    height: 40px;
    border-radius: 10px;
    background: var(--color-primary-bg);
    color: var(--color-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.summary-card__icon svg {
    width: 20px;
    height: 20px;
}

.summary-card__label {
    display: block;
    font-size: 13px;
    color: var(--color-text-secondary);
}

.summary-card__value {
    display: block;
    font-size: 18px;
    font-weight: 700;
    margin-top: 2px;
    word-break: break-word;
}

.summary-card__value--small {
    font-size: 13px;
    font-weight: 600;
}

.summary-card__hint {
    display: block;
    font-size: 12px;
    color: var(--color-text-muted);
    margin-top: 2px;
}

/* Gateway groups */

.gateway-group {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: 18px 20px 4px;
    margin-bottom: 20px;
}

.gateway-group__header {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 16px;
    font-weight: 700;
    color: var(--color-text-primary);
    padding-bottom: 14px;
    margin-bottom: 18px;
    border-bottom: 1px solid var(--color-border);
}

.gateway-group__header svg {
    width: 18px;
    height: 18px;
    padding: 6px;
    box-sizing: content-box;
    border-radius: 8px;
    background: var(--color-primary-bg);
    color: var(--color-primary);
}

/* Resumo geotécnico compacto do Gateway ("existe algum problema?") —
   mesma consolidação já usada pela página Análises
   (/api/analises/resumo-gateway/), nunca recalculada aqui; ver
   dashboard.js:updateGeoSummary(). */
.gateway-group__geo-summary {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 14px;
    font-size: 12.5px;
    color: var(--color-text-secondary);
    padding-bottom: 14px;
    margin-bottom: 18px;
    border-bottom: 1px solid var(--color-border);
}

/* Cobertura incompleta precisa ficar explicitamente visível — mesma regra
   visual de .analysis-condition-card__coverage--incompleta em Análises,
   nunca silenciosa/neutra. */
.gateway-group__geo-coverage--incompleta {
    color: var(--color-warning);
    font-weight: 600;
}

.gateway-group__geo-link {
    margin-left: auto;
    color: var(--color-primary);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}

.gateway-group__geo-link:hover {
    text-decoration: underline;
}

/* Pluviometria: card do pluviômetro OFICIAL do Gateway (gateway.rainfall_sensor)
   — sempre no nível do Gateway, nunca dentro de um Datalogger (ver
   dashboard.js:updatePluviometria()). Some inteiro (hidden) quando nenhum
   pluviômetro está configurado — nunca aparece vazio/fabricado. */
.gateway-group__pluviometria {
    margin-bottom: 20px;
}

.gateway-group__pluviometria-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    font-weight: 600;
    color: var(--color-primary-dark);
    margin-bottom: 12px;
}

.gateway-group__pluviometria-title svg {
    width: 16px;
    height: 16px;
    color: var(--color-primary);
}

.pluv-card {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 16px;
    padding: 14px 18px;
    max-width: 360px;
}

.pluv-card__uid {
    font-weight: 600;
    font-size: 14px;
    flex-basis: 100%;
}

.pluv-card__reading {
    font-size: 13px;
    color: var(--color-text-secondary);
}

.pluv-card__timestamp {
    font-size: 12px;
    color: var(--color-text-muted);
}

/* Datalogger groups */

.datalogger-group {
    margin-bottom: 22px;
}

.datalogger-group__header {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 15px;
    font-weight: 600;
    color: var(--color-primary-dark);
    margin-bottom: 12px;
}

.datalogger-group__header svg {
    width: 18px;
    height: 18px;
    color: var(--color-primary);
}

/* Badge de comunicação (Online/Offline) de Gateway/Datalogger: empurrado
   para a extremidade direita do cabeçalho, mantendo ícone+nome à esquerda. */
.gateway-group__header .status-badge,
.datalogger-group__header .status-badge {
    margin-left: auto;
}

/* Ponto de status (Online/Offline), só nos badges de comunicação
   ([data-role="comm-status"]) — nunca nos badges de medição do Sensor
   (Válido/Inválido/Sem dados), que reaproveitam as mesmas classes de cor
   mas não devem ganhar o ponto. "Status..." (neutro) permanece sem ponto. */
.status-badge[data-role="comm-status"] {
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

.status-badge[data-role="comm-status"].status-badge--success::before,
.status-badge[data-role="comm-status"].status-badge--danger::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}

/* Só o ponto Online pulsa ("vivo/comunicando") — o ponto Offline é estático,
   sem nenhum efeito de alerta piscando. */
.status-badge[data-role="comm-status"].status-badge--success::before {
    animation: status-dot-pulse 1.6s ease-in-out infinite;
}

@keyframes status-dot-pulse {
    0%, 100% {
        opacity: 1;
        transform: scale(1);
    }
    50% {
        opacity: 0.5;
        transform: scale(0.85);
    }
}

@media (prefers-reduced-motion: reduce) {
    .status-badge[data-role="comm-status"].status-badge--success::before {
        animation: none;
    }
}

.sensor-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 14px;
}

.sensor-card {
    padding: 16px 18px;
}

.sensor-card__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 12px;
}

.sensor-card__uid {
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 600;
    font-size: 14px;
}

.sensor-card__uid svg {
    width: 16px;
    height: 16px;
    color: var(--color-primary);
    flex-shrink: 0;
}

.status-badge {
    font-size: 11px;
    font-weight: 600;
    padding: 3px 9px;
    border-radius: 999px;
    white-space: nowrap;
}

.status-badge--success {
    color: var(--color-success);
    background: var(--color-success-bg);
}

.status-badge--danger {
    color: var(--color-danger);
    background: var(--color-danger-bg);
}

.status-badge--neutral {
    color: var(--color-neutral);
    background: var(--color-neutral-bg);
}

.status-badge--warning {
    color: var(--color-warning);
    background: var(--color-warning-bg);
}

.status-badge--info {
    color: var(--color-primary-dark);
    background: var(--color-primary-bg);
}

.sensor-card__readings {
    display: flex;
    gap: 18px;
    margin-bottom: 10px;
}

.sensor-card__reading-label {
    display: block;
    font-size: 11px;
    color: var(--color-text-secondary);
}

.sensor-card__reading-value {
    display: block;
    font-size: 17px;
    font-weight: 700;
}

.sensor-card__timestamp {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--color-text-muted);
}

.sensor-card__timestamp svg {
    width: 13px;
    height: 13px;
    flex-shrink: 0;
}

/* States */

.api-error-banner {
    background: var(--color-danger-bg);
    color: var(--color-danger);
    border: 1px solid rgba(185, 28, 28, 0.25);
    padding: 10px 16px;
    border-radius: var(--radius-md);
    font-size: 13px;
    margin-bottom: 18px;
}

.empty-state {
    background: var(--color-surface);
    border: 1px dashed var(--color-border-strong);
    border-radius: var(--radius-lg);
    padding: 32px;
    text-align: center;
    color: var(--color-text-secondary);
    font-size: 14px;
}

.timezone-note {
    margin-top: 28px;
    font-size: 12px;
    color: var(--color-text-muted);
    text-align: center;
}

/* Leituras — filtros */

.filters-card {
    padding: 20px 22px;
    margin-bottom: 20px;
}

.preset-periodo {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px 12px;
    margin-bottom: 16px;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--color-border);
}

.preset-periodo__label {
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text-secondary);
}

.preset-periodo__buttons {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.preset-btn {
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    padding: 6px 14px;
    font-size: 13px;
    font-weight: 600;
    font-family: inherit;
    border-radius: 8px;
    cursor: pointer;
}

/* Dia 12, checkpoint 12C-D: controles de estado (preset/tab/toggle/
   segmented) usam a tinta MUITO suave --mmf-orange-bg, nunca fundo
   sólido — só o CTA principal (.btn--primary) tem esse peso visual. */
.preset-btn--active {
    background: var(--mmf-orange-bg);
    border-color: var(--mmf-orange);
    color: var(--mmf-orange-dark);
}

/* Leituras — alternância Inclinação/Pluviometria (nível do Gateway) —
   mesmo vocabulário visual de .preset-btn/.view-toggle__btn (tinta suave
   --mmf-orange-bg no selecionado, nunca fundo sólido). O wrapper reaproveita
   .filter-field (já com o fix de cascata para [hidden] abaixo). */
.modo-leitura__buttons {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.modo-leitura__btn {
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    padding: 8px 14px;
    font-size: 14px;
    font-weight: 600;
    font-family: inherit;
    border-radius: 8px;
    cursor: pointer;
}

.modo-leitura__btn--active {
    background: var(--mmf-orange-bg);
    border-color: var(--mmf-orange);
    color: var(--mmf-orange-dark);
}

.filters-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 16px 20px;
    align-items: flex-end;
}

.filter-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 160px;
}

.filter-field[hidden] {
    /* Mesmo conflito de cascata resolvido em .view-toggle/.updating-banner:
       o "display: flex" acima venceria o "display: none" de user-agent
       para [hidden]. Necessário aqui porque a Exportação esconde/mostra o
       campo Sensor ao trocar de modo (Individualizada vs. Agrupada). */
    display: none;
}

.filter-field label {
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text-secondary);
}

.filter-field select,
.filter-field input[type="datetime-local"] {
    padding: 8px 10px;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    font-size: 14px;
    font-family: inherit;
    color: var(--color-text-primary);
    background: var(--color-surface);
}

.filter-field select:disabled {
    background: var(--color-neutral-bg);
    color: var(--color-text-muted);
    cursor: not-allowed;
}

.filter-actions {
    display: flex;
    gap: 10px;
    margin-left: auto;
}

.btn {
    border: none;
    border-radius: 8px;
    padding: 9px 16px;
    font-size: 14px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
}

/* Dia 12, checkpoint 12C-D: CTA institucional em laranja MMF —
   --mmf-orange-dark (não --mmf-orange) como fundo sólido: melhor
   contraste para texto branco pequeno; --mmf-orange fica reservado para
   o focus ring/acentos. Único botão com peso visual alto desta
   harmonização (ver .btn--outline/controles selecionados abaixo, que
   ficam deliberadamente mais discretos). */
.btn--primary {
    background: var(--mmf-orange-dark);
    color: #ffffff;
}

.btn--primary:hover:not(:disabled) {
    background: var(--mmf-orange-darker);
}

.btn--primary:focus-visible {
    outline: 2px solid var(--mmf-orange);
    outline-offset: 2px;
}

.btn--secondary {
    background: var(--color-neutral-bg);
    color: var(--color-text-secondary);
}

.btn:disabled {
    background: var(--color-border-strong);
    color: var(--color-text-muted);
    cursor: not-allowed;
}

.filter-error {
    color: var(--color-danger);
    font-size: 13px;
    margin: 12px 0 0;
}

/* Leituras — tabs e alternância de série */

.tabs {
    display: flex;
    gap: 4px;
    border-bottom: 1px solid var(--color-border);
    margin-bottom: 16px;
}

.tab-button {
    background: none;
    border: none;
    padding: 10px 16px;
    font-size: 14px;
    font-weight: 600;
    font-family: inherit;
    color: var(--color-text-secondary);
    cursor: pointer;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}

/* Dia 12, checkpoint 12C-D: texto/borda em laranja MMF (sem fundo — a
   tab já é só texto+sublinhado, não precisa de tinta de fundo). */
.tab-button--active {
    color: var(--mmf-orange-dark);
    border-bottom-color: var(--mmf-orange);
}

.view-toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 16px;
}

.view-toggle[hidden] {
    /* Regra de autor com "display: flex" acima vence o "display: none" do
       user-agent para [hidden] (origem de autor sempre tem prioridade sobre
       a folha de estilos padrão do navegador, independente de
       especificidade) — sem isto, o controle permanecia visível mesmo com
       o atributo "hidden" definido corretamente pelo JavaScript. */
    display: none;
}

.view-toggle__label {
    font-size: 13px;
    color: var(--color-text-secondary);
}

.view-toggle__btn {
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    padding: 6px 14px;
    font-size: 13px;
    font-weight: 600;
    font-family: inherit;
    border-radius: 8px;
    cursor: pointer;
}

/* Dia 12, checkpoint 12C-D: mesma tinta suave dos demais controles
   selecionados (ver .preset-btn--active) — nunca fundo sólido aqui. */
.view-toggle__btn--active {
    background: var(--mmf-orange-bg);
    border-color: var(--mmf-orange);
    color: var(--mmf-orange-dark);
}

/* Leituras — tabela */

.table-container {
    overflow-x: auto;
    overflow-y: auto;
    max-height: 460px;
    padding: 0;
}

.table-container .data-table thead th {
    position: sticky;
    top: 0;
    background: var(--color-surface);
    z-index: 1;
}

.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
    min-width: 760px;
}

.data-table th {
    text-align: left;
    padding: 12px 16px;
    font-size: 12px;
    color: var(--color-text-secondary);
    border-bottom: 1px solid var(--color-border);
    white-space: nowrap;
}

.data-table td {
    padding: 10px 16px;
    border-bottom: 1px solid var(--color-border);
    white-space: nowrap;
}

.data-table tbody tr:last-child td {
    border-bottom: none;
}

.pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 16px;
    font-size: 13px;
    color: var(--color-text-secondary);
    flex-wrap: wrap;
    gap: 10px;
}

.pager__buttons {
    display: flex;
    gap: 8px;
}

/* Leituras — gráfico */

.chart-card {
    padding: 20px;
    position: relative;
}

.chart-tooltip {
    position: absolute;
    z-index: 5;
    /* Nunca intercepta o próprio hover/arraste — a detecção acontece nos
       pontos do SVG, não no tooltip. */
    pointer-events: none;
    background: var(--color-text-primary);
    color: #ffffff;
    padding: 8px 10px;
    border-radius: 8px;
    font-size: 12px;
    line-height: 1.5;
    box-shadow: var(--shadow-card);
    white-space: nowrap;
    max-width: 220px;
}

.chart-tooltip__sensor {
    font-weight: 700;
    margin-bottom: 2px;
}

.chart-tooltip__tempo {
    color: rgba(255, 255, 255, 0.7);
    margin-bottom: 4px;
}

.chart-empty {
    text-align: center;
    color: var(--color-text-secondary);
    padding: 48px 0;
    font-size: 14px;
}

.chart-svg {
    width: 100%;
    height: 400px;
    display: block;
    cursor: crosshair;
    /* Reserva apenas o gesto HORIZONTAL para a seleção (o JS trata isso via
       Pointer Events) e preserva a rolagem vertical nativa da página em
       telas de toque — "none" bloquearia os dois eixos desnecessariamente,
       impedindo o usuário de rolar a página a partir de cima do gráfico. */
    touch-action: pan-y;
}

.chart-selection-overlay {
    fill: var(--color-primary);
    fill-opacity: 0.12;
    stroke: var(--color-primary);
    stroke-opacity: 0.4;
    stroke-width: 1;
    pointer-events: none;
}

.chart-hint {
    margin: 0 0 12px;
    font-size: 12px;
    color: var(--color-text-muted);
}

.chart-zoom-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 10px 14px;
    margin-bottom: 12px;
    padding: 8px 14px;
    background: var(--color-primary-bg);
    border: 1px solid rgba(37, 99, 235, 0.18);
    border-radius: var(--radius-md);
    font-size: 13px;
    color: var(--color-primary-dark);
}

.chart-zoom-bar[hidden] {
    /* Mesmo conflito de cascata do .updating-banner/.view-toggle: a regra
       de autor acima ("display: flex") vence o "display: none" de
       user-agent para [hidden]. */
    display: none;
}

.chart-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 18px;
    margin-bottom: 12px;
    font-size: 13px;
    color: var(--color-text-secondary);
}

.chart-legend__item {
    display: flex;
    align-items: center;
    gap: 6px;
}

/* Leituras — transições discretas (entrada de painéis/resultado) */

@keyframes leituraFadeIn {
    from {
        opacity: 0;
        transform: translateY(6px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.leitura-fade-in {
    animation: leituraFadeIn 200ms ease-out;
}

/* Leituras — indicador de atualização (mantém resultado anterior visível) */

.updating-banner {
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--color-primary-bg);
    color: var(--color-primary-dark);
    border: 1px solid rgba(37, 99, 235, 0.18);
    padding: 10px 16px;
    border-radius: var(--radius-md);
    font-size: 13px;
    margin-bottom: 18px;
}

.updating-banner[hidden] {
    /* Mesmo conflito de cascata do .view-toggle: a regra de autor acima
       ("display: flex") vence o "display: none" de user-agent para
       [hidden] — sem isto, o banner ficava sempre visível. */
    display: none;
}

/* Leituras — aviso de consulta "Todos os sensores" (parcial/gaps/sem dados) */

.aviso-banner {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 16px;
    border-radius: var(--radius-md);
    font-size: 13px;
    margin-bottom: 14px;
}

.aviso-banner[hidden] {
    /* Mesmo conflito de cascata do .updating-banner/.view-toggle: a regra
       de autor acima ("display: flex") vence o "display: none" de
       user-agent para [hidden]. */
    display: none;
}

.aviso-banner--warning {
    background: var(--color-warning-bg);
    color: var(--color-warning);
    border: 1px solid rgba(161, 98, 7, 0.25);
}

.aviso-banner--danger {
    background: var(--color-danger-bg);
    color: var(--color-danger);
    border: 1px solid rgba(185, 28, 28, 0.25);
}

/* Leituras — detalhamento por sensor (modo "Todos os sensores") */

.sensor-status-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 18px;
    padding: 12px 16px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
}

.sensor-status-list[hidden] {
    /* Mesmo conflito de cascata do .updating-banner/.view-toggle. */
    display: none;
}

.sensor-status-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    font-size: 13px;
}

.sensor-status-item__uid {
    font-weight: 600;
    color: var(--color-text-primary);
}

.updating-banner__dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--color-primary);
    flex-shrink: 0;
    animation: updatingPulse 1.1s ease-in-out infinite;
}

@keyframes updatingPulse {
    0%, 100% {
        opacity: 1;
    }
    50% {
        opacity: 0.35;
    }
}

.chart-legend__swatch {
    width: 12px;
    height: 3px;
    border-radius: 2px;
    display: inline-block;
}

.chart-legend__swatch--x {
    background: var(--color-chart-x);
}

.chart-legend__swatch--y {
    background: var(--color-chart-y);
}

/* Exportação — segmented control (Individualizada / Agrupada) */

.segmented-control {
    display: flex;
    background: var(--color-neutral-bg);
    border-radius: 10px;
    padding: 4px;
    gap: 4px;
    margin-bottom: 22px;
}

.segmented-control__btn {
    flex: 1;
    border: none;
    background: transparent;
    color: var(--color-text-secondary);
    font-size: 14px;
    font-weight: 600;
    font-family: inherit;
    padding: 10px 16px;
    border-radius: 8px;
    cursor: pointer;
}

/* Dia 12, checkpoint 12C-D: mesma tinta suave dos demais controles
   selecionados (ver .preset-btn--active) — nunca fundo sólido aqui. */
.segmented-control__btn--active {
    background: var(--mmf-orange-bg);
    color: var(--mmf-orange-dark);
    box-shadow: var(--shadow-card);
}

.segmented-control[hidden] {
    /* Mesmo conflito de cascata resolvido em .filter-field/.view-toggle: o
       "display: flex" acima venceria o "display: none" de user-agent para
       [hidden]. Necessário porque a Exportação esconde este controle
       (Individualizada/Agrupada) no modo Pluviometria. */
    display: none;
}

/* Exportação — seções do formulário (Formato / Período / Campos incluídos) */

.form-section {
    margin-bottom: 20px;
}

.form-section__label {
    display: block;
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text-secondary);
    margin-bottom: 10px;
}

.radio-group,
.checkbox-group {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 24px;
}

.radio-option,
.checkbox-option {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    color: var(--color-text-primary);
    cursor: pointer;
}

.radio-option input,
.checkbox-option input {
    width: 16px;
    height: 16px;
    accent-color: var(--color-primary);
    cursor: pointer;
}

.export-actions {
    display: flex;
    gap: 10px;
    margin-top: 4px;
    flex-wrap: wrap;
}

.form-note {
    font-size: 12px;
    color: var(--color-text-muted);
    margin: 14px 0 0;
}

/* Exportação — grid próprio do formulário (não reaproveita .filters-grid,
   que é compartilhado com a página Leituras e deve permanecer intocado) */

.export-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 16px 20px;
    margin-bottom: 20px;
}

.filter-field__label {
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text-secondary);
}

/* Exportação — botão "Visualizar prévia": ação secundária, mas claramente
   clicável (evita a aparência de botão desabilitado do .btn--secondary
   padrão, que fica igual demais ao estado :disabled). Classe própria para
   não alterar .btn--secondary, usado também em Leituras. */

/* Dia 12, checkpoint 12C-D: neutro/discreto (era azul-tintado, o que
   destoava da própria intenção documentada acima — ação secundária não
   deve competir com o CTA principal). */
.btn--outline {
    background: var(--color-surface);
    color: var(--color-text-primary);
    border: 1px solid var(--color-border-strong);
}

.btn--outline:hover:not(:disabled) {
    background: var(--color-neutral-bg);
    border-color: var(--color-border-strong);
}

/* Exportação — prévia: área rolável com cabeçalho fixo e rodapé sempre
   visível fora da rolagem. */

.previa-scroll {
    max-height: 460px;
    overflow-y: auto;
    overflow-x: auto;
}

.previa-scroll .data-table thead th {
    position: sticky;
    top: 0;
    background: var(--color-surface);
    z-index: 1;
}

.previa-info {
    margin: 0;
    padding: 12px 16px 16px;
    font-size: 12px;
    color: var(--color-text-muted);
    border-top: 1px solid var(--color-border);
}

/* Eventos — filtros compactos e centralizados (refinamento visual, Dia 11
   checkpoints 11B-1/11B-2). Classe adicional (nunca sobrepõe
   .filters-card/.filters-grid diretamente) para não afetar Leituras, que
   também usa essas classes. */

.filters-card--compact {
    padding: 14px 18px;
}

.filters-card--compact .filters-grid {
    gap: 12px 16px;
    /* Centraliza o bloco inteiro de campos + ações no card em vez de
       concentrá-lo à esquerda — o próprio flex-wrap já quebra normalmente
       em telas estreitas, então isto não afeta a responsividade existente
       (em mobile cada campo ocupa 100% da linha, ver @media abaixo, e
       "centralizar" um item de largura total não tem efeito visível). */
    justify-content: center;
}

.filters-card--compact .filter-actions {
    /* Sem margin-left:auto: os botões ficam logo após o último campo,
       como parte do mesmo bloco centralizado, não empurrados para a borda
       direita do card. */
    margin-left: 0;
}

/* Eventos — painel do histórico: card único com cabeçalho fixo e lista
   rolável internamente (não é mais uma pilha de cards separados — ver
   docs/estado-atual.md, Dia 11 checkpoint 11B-1). Separa visualmente
   Filtros de Histórico, como pedido no refinamento. */

.events-panel {
    display: flex;
    flex-direction: column;
    margin-bottom: 20px;
    overflow: hidden;
}

/* Cabeçalho do painel: título + status de atualização na mesma linha,
   sempre nos dois extremos (space-between fixa a posição de cada um nas
   bordas do card, então o texto do status pode trocar de tamanho sem
   deslocar o título). Altura fixa pelo padding + uma única linha de texto
   em cada lado — nunca muda entre "↻ Atualizando..." e "✓ Atualizado
   agora", então o painel/lista abaixo nunca se movem durante o polling
   (refinamento visual, Dia 11 checkpoint 11B-2 — ver eventos.js). */

.events-panel__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 12px 18px;
    font-size: 13px;
    font-weight: 700;
    color: var(--color-text-secondary);
    border-bottom: 1px solid var(--color-border);
    flex-shrink: 0;
}

.events-panel__title {
    white-space: nowrap;
}

.events-panel__status {
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}

.events-panel__status--updating {
    color: var(--color-primary-dark);
}

.events-panel__status--success {
    color: var(--color-success);
}

.events-panel__status--error {
    color: var(--color-danger);
}

/* Lista/timeline compacta dentro do painel — linhas separadas por borda
   (mesmo padrão de .data-table/.sensor-status-item), não caixas
   individuais: reduz drasticamente a altura por Event. max-height é uma
   estimativa para ~6 linhas compactas visíveis por vez (auditar no
   navegador real e ajustar se necessário); overflow-y cria a rolagem
   interna só nesta área, sem depender da rolagem da página inteira. */

.event-list {
    display: flex;
    flex-direction: column;
    max-height: 560px;
    overflow-y: auto;
}

.event-item {
    padding: 10px 18px;
    border-bottom: 1px solid var(--color-border);
}

.event-item:last-child {
    border-bottom: none;
}

.event-item__top {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 3px 10px;
    margin-bottom: 4px;
    font-size: 12px;
}

.event-item__time {
    font-weight: 700;
    color: var(--color-text-primary);
}

.event-item__context {
    color: var(--color-text-muted);
}

.event-item__body {
    display: flex;
    align-items: flex-start;
    gap: 8px;
}

.event-item__dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-top: 6px;
    flex-shrink: 0;
}

.event-item__dot--info {
    background: var(--color-primary);
}

.event-item__dot--warning {
    background: var(--color-warning);
}

.event-item__dot--error {
    background: var(--color-danger);
}

.event-item__dot--neutral {
    background: var(--color-neutral);
}

.event-item__message {
    margin: 0;
    font-size: 14px;
    font-weight: 600;
    line-height: 1.35;
}

.event-item__footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-top: 6px;
}

.event-item__type {
    font-size: 12px;
    color: var(--color-text-muted);
}

.event-item__type code {
    font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
    font-size: 11px;
    color: var(--color-text-secondary);
    background: var(--color-neutral-bg);
    padding: 1px 6px;
    border-radius: 4px;
}

/* Responsive */

@media (max-width: 860px) {
    .sidebar {
        width: 72px;
        padding: 18px 8px;
    }

    .nav-item__label {
        display: none;
    }

    .nav-item {
        justify-content: center;
    }

    .content {
        padding: 20px;
    }

    .app-header {
        padding: 0 16px;
    }

    .app-header__brand .brand-name {
        display: none;
    }

    .filter-actions {
        margin-left: 0;
        width: 100%;
    }
}

@media (max-width: 560px) {
    .refresh-indicator span:not(.refresh-indicator__dot) {
        display: none;
    }

    .filter-field {
        min-width: 0;
        flex: 1 1 100%;
    }

    .event-item__footer {
        flex-wrap: wrap;
    }

    .event-item__type {
        word-break: break-word;
    }
}

/* Login (Dia 12, checkpoint 12C-A): layout visual próprio, migrado para
   telemetria/static/telemetria/css/login.css — ver esse arquivo. login.html
   continua standalone (não estende base.html) e carrega app.css só para
   aproveitar :root/reset. */

/* Header — usuário logado + Sair (Dia 12, checkpoint 12B). Integração
   mínima; Client atual e redesenho maior ficam para etapa visual futura. */

.header-user {
    font-size: 13px;
    color: var(--color-text-secondary);
    white-space: nowrap;
}

.header-logout-form {
    display: inline-flex;
    margin: 0;
}

.header-logout-btn {
    /* Dia 12, checkpoint 12C-D: neutro/discreto de propósito — logout é
       uma ação utilitária, não uma CTA de marca. */
    border: none;
    background: none;
    padding: 6px 4px;
    font-size: 13px;
    font-family: inherit;
    font-weight: 600;
    color: var(--color-text-secondary);
    cursor: pointer;
}

.header-logout-btn:hover {
    text-decoration: underline;
}

/* Análises (checkpoint: primeira versão da página /analises/). Classes
   próprias com prefixo "analysis-" (nunca reaproveitam .filters-card/
   .sensor-grid/etc. de outras páginas com semântica diferente), mas
   reaproveitam os tokens/vocabulário visual existentes (.card,
   .status-badge, --color-*, --mmf-orange, --radius-*, --shadow-card) —
   sem refatorar o restante do arquivo. */

.analysis-gateway-selector {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 16px 20px;
    margin-bottom: 20px;
}

.analysis-gateway-selector label {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-secondary);
}

.analysis-gateway-selector select {
    padding: 8px 10px;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    font-size: 14px;
    font-family: inherit;
    color: var(--color-text-primary);
    background: var(--color-surface);
    min-width: 180px;
}

.analysis-condition-card {
    padding: 20px 22px;
    margin-bottom: 20px;
}

.analysis-condition-card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

.analysis-condition-card__header h2 {
    margin: 0;
    font-size: 16px;
    font-weight: 700;
    color: var(--color-text-primary);
}

.analysis-condition-card__coverage {
    margin: 6px 0 18px;
    font-size: 13px;
    color: var(--color-text-secondary);
}

/* Cobertura incompleta precisa ficar explicitamente visível (pedido do
   checkpoint) — cor de atenção, nunca silenciosa/neutra. */
.analysis-condition-card__coverage--incompleta {
    color: var(--color-warning);
    font-weight: 600;
}

.analysis-dtg-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 12px;
}

.analysis-dtg-card {
    display: flex;
    flex-direction: column;
    gap: 4px;
    align-items: flex-start;
    padding: 14px 16px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    font-family: inherit;
    text-align: left;
    cursor: pointer;
}

.analysis-dtg-card:hover {
    border-color: var(--color-border-strong);
}

.analysis-dtg-card:focus-visible {
    outline: 2px solid var(--mmf-orange);
    outline-offset: 2px;
}

/* DTG sem análise (has_analysis=false, checkpoint refinamento): card nativo
   <button disabled> — nunca focável/clicável (navegador já remove do tab
   order e ignora clique/Enter/Espaço), então basta reforçar visualmente que
   não é interativo. Dinâmico: volta a ficar normal sozinho assim que
   has_analysis passar a true na próxima renderização (nunca um estado
   fixo por uid). */
.analysis-dtg-card:disabled {
    cursor: not-allowed;
    opacity: 0.75;
}

.analysis-dtg-card:disabled:hover {
    border-color: var(--color-border);
}

/* Card selecionado: mesmo vocabulário de ".nav-item--active"/".preset-btn--active"
   (filete + fundo suave laranja) — indicação clara sem depender de cor
   semântica (que já está em uso para o nível). */
.analysis-dtg-card--selected {
    border-color: var(--mmf-orange);
    box-shadow: 0 0 0 1px var(--mmf-orange);
}

.analysis-dtg-card__uid {
    font-size: 13px;
    font-weight: 700;
    color: var(--color-text-primary);
}

.analysis-dtg-card__status {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.02em;
}

.analysis-dtg-card__axis {
    font-size: 12px;
    color: var(--color-text-secondary);
}

.analysis-dtg-card__slope {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-primary);
}

.analysis-dtg-card__hint {
    font-size: 12px;
    color: var(--color-text-secondary);
}

.analysis-dtg-card--normal .analysis-dtg-card__status {
    color: var(--color-success);
}

.analysis-dtg-card--alerta .analysis-dtg-card__status {
    color: var(--color-warning);
}

.analysis-dtg-card--emergencia .analysis-dtg-card__status {
    color: var(--color-danger);
}

/* Crítico: mesma cor-base de Emergência (a paleta do projeto não tem um
   quinto tom semântico), diferenciado por peso — borda sólida na cor de
   perigo, para não inventar uma cor nova fora do vocabulário existente. */
.analysis-dtg-card--critico {
    border-color: var(--color-danger);
}

.analysis-dtg-card--critico .analysis-dtg-card__status {
    color: var(--color-danger);
}

.analysis-dtg-card--sem-dado .analysis-dtg-card__status {
    color: var(--color-neutral);
}

.analysis-dtg-card--sem-analise {
    border-style: dashed;
}

.analysis-dtg-card--sem-analise .analysis-dtg-card__status {
    color: var(--color-text-muted);
}

.analysis-detail-card {
    padding: 20px 22px;
    margin-bottom: 20px;
}

.analysis-detail-card h2 {
    margin: 0 0 14px;
    font-size: 16px;
    font-weight: 700;
    color: var(--color-text-primary);
}

.analysis-detail-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 14px 20px;
}

.analysis-detail-item {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.analysis-detail-item__label {
    font-size: 12px;
    color: var(--color-text-secondary);
}

.analysis-detail-item__value {
    font-size: 15px;
    font-weight: 600;
    color: var(--color-text-primary);
}

.analysis-axes-card {
    padding: 20px 22px;
    margin-bottom: 20px;
}

.analysis-axes-card h2 {
    margin: 0 0 14px;
    font-size: 16px;
    font-weight: 700;
    color: var(--color-text-primary);
}

/* Distribuição uniforme (checkpoint refinamento): "auto-fill" deixava
   colunas fantasmas sem conteúdo à direita sempre que a tela era larga o
   bastante para mais de 8 faixas de 150px, empurrando os 8 chips reais
   para a esquerda. Contagem fixa de colunas por breakpoint (mesmo
   vocabulário de media query já usado por .analysis-charts/.sensor-grid
   acima) garante 8 colunas de mesma largura ocupando toda a largura no
   desktop, com quebra previsível em telas menores. */
.analysis-axes-grid {
    display: grid;
    grid-template-columns: repeat(8, minmax(0, 1fr));
    gap: 10px;
}

@media (max-width: 1100px) {
    .analysis-axes-grid {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

@media (max-width: 700px) {
    .analysis-axes-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 420px) {
    .analysis-axes-grid {
        grid-template-columns: minmax(0, 1fr);
    }
}

.analysis-axis-chip {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 10px 12px;
    border-radius: var(--radius-md);
    background: var(--color-neutral-bg);
    border: 1px solid var(--color-border);
}

.analysis-axis-chip--invalid {
    background: var(--color-danger-bg);
    border-color: rgba(185, 28, 28, 0.25);
}

.analysis-axis-chip__name {
    font-size: 12px;
    font-weight: 700;
    color: var(--color-text-primary);
}

.analysis-axis-chip--invalid .analysis-axis-chip__name {
    color: var(--color-danger);
}

.analysis-axis-chip__slope {
    font-size: 14px;
    font-weight: 600;
}

.analysis-axis-chip__validity {
    font-size: 11px;
    color: var(--color-text-secondary);
}

.analysis-axis-chip__reason {
    font-size: 11px;
    color: var(--color-text-muted);
}

/* Análises — Figura 3 (Evolução das velocidades) + Ábaco (checkpoint dos
   gráficos). ~60/40 no desktop (Figura 3 naturalmente mais larga), 1
   coluna em telas menores — mesmo breakpoint de .analysis-charts abaixo. */

.analysis-charts {
    display: grid;
    grid-template-columns: 3fr 2fr;
    gap: 16px;
    margin-bottom: 20px;
}

.analysis-chart-card {
    padding: 18px 20px;
    display: flex;
    flex-direction: column;
}

/* Header refinado (checkpoint refinamento): o título do Ábaco ("Critérios
   de alerta — Chuva acumulada [72h] × Velocidade [12h]") é longo demais
   para a coluna ~40% em uma única linha ao lado do hint. flex-wrap permite
   o <h2> quebrar em até 2 linhas dentro do próprio espaço (flex-basis
   mínima), e o hint — que precisa continuar curto ("Últimas 72 h" /
   "Anterior → atual · N min") — reorganiza para uma linha própria quando
   não há espaço, sem nunca sobrepor o título. */
.analysis-chart-card__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 4px 10px;
    margin-bottom: 12px;
}

.analysis-chart-card__header h2 {
    margin: 0;
    font-size: 16px;
    font-weight: 700;
    line-height: 1.3;
    color: var(--color-text-primary);
    flex: 1 1 200px;
    min-width: 0;
}

.analysis-chart-card__hint {
    font-size: 12px;
    color: var(--color-text-secondary);
    white-space: nowrap;
    flex: 0 0 auto;
}

/* Altura fixa (nunca gigantesca) via wrapper — Chart.js usa
   maintainAspectRatio:false e respeita 100% deste container. O Ábaco tende
   a quadrado porque sua coluna (40%) já fica naturalmente mais estreita. */
.analysis-chart-card__body {
    position: relative;
    height: 320px;
}

.analysis-chart-card__body canvas {
    width: 100% !important;
    height: 100% !important;
}

.analysis-chart-card__body .empty-state {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0;
}

/* Correção identificada no Labrador: sem esta regra, "display: flex" acima
   vence o "display: none" de user-agent para [hidden] (mesmo conflito de
   cascata já documentado para .view-toggle/.updating-banner/etc.) — o
   estado vazio ficava visível mesmo com o atributo hidden setado pelo JS. */
.analysis-chart-card__body .empty-state[hidden] {
    display: none;
}

@media (max-width: 900px) {
    .analysis-charts {
        grid-template-columns: 1fr;
    }

    .analysis-chart-card__body {
        height: 280px;
    }
}

@media (max-width: 560px) {
    .analysis-gateway-selector {
        flex-wrap: wrap;
    }

    .analysis-gateway-selector select {
        flex: 1 1 100%;
    }
}

/* Análises — transição suave ao trocar Gateway/DTG (checkpoint
   refinamento). Mesmo vocabulário de .leitura-fade-in (Leituras): atenuar
   o conteúdo antigo assim que uma nova seleção começa a carregar, depois
   reanimar a entrada quando o conteúdo novo substitui o antigo. */
.analysis-is-loading {
    opacity: 0.45;
    transition: opacity 200ms ease;
}

@keyframes analysisFadeIn {
    from {
        opacity: 0;
        transform: translateY(6px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.analysis-fade-in {
    animation: analysisFadeIn 200ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
    .analysis-is-loading {
        transition: none;
    }

    .analysis-fade-in {
        animation: none;
    }
}
