:root{
  --bg:#f4f6f9; --panel:#fff; --ink:#1c2430; --muted:#6b7684; --line:#e3e8ef;
  --brand:#2f6fed; --brand-ink:#1e51bf; --ok:#1f9d55; --erro:#e0413a; --aviso:#d9822b; --amarelo:#e3b100;
  --rodando:#2f6fed; --shadow:0 1px 3px rgba(20,30,50,.08),0 1px 2px rgba(20,30,50,.06);
  --radius:10px;
}
/* ⬇️ `[hidden]` PRECISA GANHAR DE QUALQUER `display` DO AUTOR — regra global, uma vez só.
   A folha do NAVEGADOR traz `[hidden]{display:none}`, mas ela é vencida por qualquer seletor de
   classe que declare `display` (especificidade 0,1,0 contra 0,0,1). Toda vez que alguém escreveu
   `.algo{display:flex}` e escondeu o elemento com o atributo `hidden`, o elemento continuou na
   tela — e o remendo era acrescentar `.algo[hidden]{display:none}`, um por classe.
   Isso já foi feito TRÊS vezes aqui (`.modal-overlay`, `.rb-confirm`, `.res-presets`) e o quarto
   caso passou: o `.badge{display:inline-flex}` do sininho de alertas, que exibia um "0" em
   vermelho quando não havia alerta nenhum — relatado pelo Fernando em 04/08/2026. Remendar classe
   a classe não escala; a regra global mata a família inteira, inclusive as que ainda não existem.
   `!important` é deliberado: `hidden` é uma afirmação sobre o DOM, e nenhum estilo deve vencê-la. */
[hidden]{display:none !important}

*{box-sizing:border-box}
html,body{margin:0}
body{font:15px/1.5 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;color:var(--ink);background:var(--bg)}
a{color:var(--brand);text-decoration:none}
a:hover{text-decoration:underline}
code{font:12px/1 ui-monospace,"SFMono-Regular",Menlo,monospace;background:#eef1f6;padding:.1em .4em;border-radius:4px}
.mono{font-family:ui-monospace,"SFMono-Regular",Menlo,monospace}
.muted{color:var(--muted)}
.small{font-size:12px}
h1{font-size:20px;margin:0}
h2{font-size:16px;margin:0 0 12px}
h3{font-size:15px;margin:0 0 12px}
h4{font-size:13px;margin:0 0 8px;color:var(--muted);text-transform:uppercase;letter-spacing:.03em}

/* ---- Layout ---- */
.sidebar{position:fixed;top:0;left:0;bottom:0;width:232px;background:#0f1b2d;color:#c6d2e2;display:flex;flex-direction:column;z-index:40}
.brand{font-size:20px;font-weight:700;padding:18px 20px;color:#fff;letter-spacing:-.02em}
.brand span{color:var(--brand)}
.sidebar nav{display:flex;flex-direction:column;padding:6px 10px;gap:2px;overflow:auto}
.sidebar nav a{display:flex;align-items:center;gap:10px;color:#c6d2e2;padding:9px 12px;border-radius:8px;font-size:14px}
.sidebar nav a:hover{background:rgba(255,255,255,.06);text-decoration:none}
.sidebar nav a.active{background:var(--brand);color:#fff}
.sidebar nav a .ic{width:20px;text-align:center;opacity:.9}
.sidebar nav a .badge{margin-left:auto}
.sidebar-foot{margin-top:auto;padding:14px 20px;border-top:1px solid rgba(255,255,255,.08);display:flex;justify-content:space-between;align-items:center}
.sidebar-foot .link{color:#9fb0c6}
.content{margin-left:232px;min-height:100vh;display:flex;flex-direction:column}
.topbar{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;background:var(--panel);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:20}
.page{padding:24px;max-width:1100px;width:100%}
.menu-toggle{display:none}
.scrim{display:none}

/* ---- Cards ---- */
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:14px;margin-bottom:22px}
.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:16px 18px;box-shadow:var(--shadow)}
.card-n{font-size:30px;font-weight:700;line-height:1}
.card-l{color:var(--muted);font-size:13px;margin-top:6px}
.card-alert{border-color:#f3c0bd;background:#fff6f5}
.card-alert .card-n{color:var(--erro)}

/* ---- Panels / tables ---- */
.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:18px 20px;box-shadow:var(--shadow);margin-bottom:20px}
.panel-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:12px}
.panel-head h3{margin:0}
.tbl-wrap{overflow-x:auto}
.tbl{width:100%;border-collapse:collapse;font-size:14px}
.tbl th{text-align:left;color:var(--muted);font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.03em;padding:8px 10px;border-bottom:1px solid var(--line)}
.tbl td{padding:10px;border-bottom:1px solid var(--line);vertical-align:middle}
.tbl tr:last-child td{border-bottom:none}

/* ---- Bits ---- */
.pill{display:inline-block;background:#eef1f6;color:#42506a;border-radius:20px;padding:2px 9px;font-size:12px;font-weight:600}
.st{display:inline-block;padding:2px 9px;border-radius:20px;font-size:12px;font-weight:600;text-transform:capitalize}
.st-sucesso{background:#e2f5ea;color:var(--ok)}
.st-erro{background:#fde8e7;color:var(--erro)}
.st-rodando,.st-na_fila{background:#e6eefb;color:var(--rodando)}
.sev{display:inline-block;padding:2px 9px;border-radius:20px;font-size:12px;font-weight:600}
.sev-info{background:#e6eefb;color:var(--rodando)}
.sev-aviso{background:#fcefdc;color:var(--aviso)}
.sev-critico{background:#fde8e7;color:var(--erro)}
.badge{display:inline-flex;min-width:18px;height:18px;padding:0 5px;align-items:center;justify-content:center;background:var(--erro);color:#fff;border-radius:20px;font-size:11px;font-weight:700}
.banner-erro{background:#fde8e7;border:1px solid #f3c0bd;color:#9b2820;padding:12px 16px;border-radius:var(--radius);margin-bottom:18px}
/* Contador de ESPERA, não de erro. Badge vermelho diz "quebrou"; proposta de preço aguardando
   decisão não quebrou nada — está esperando alguém. A cor também é o que separa os dois chips do
   topo de relance, sem precisar ler o glifo. */
.badge-espera{background:var(--aviso)}
.alert-chip{display:inline-flex;align-items:center;gap:6px;color:var(--ink)}
.alert-chip:hover{text-decoration:none;opacity:.72}
.topbar-chips{display:flex;align-items:center;gap:16px;flex:0 0 auto}
/* O h1 é quem cede espaço quando o título é longo e a tela é estreita — sem isto ele empurra os
   chips para fora da barra em vez de encolher (a topbar é flex, e o default `min-width:auto` faz
   o item se recusar a ficar menor que o conteúdo). */
.topbar h1{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
tr.ack{opacity:.55}
tr.row-erro td:first-child{box-shadow:inset 3px 0 0 var(--erro)}

/* ---- Buttons ---- */
.btn{cursor:pointer;border:1px solid var(--line);background:#fff;color:var(--ink);padding:8px 14px;border-radius:8px;font-size:14px;font-weight:600}
.btn:hover{border-color:#c9d2df;background:#fafbfc}
.btn-primary{background:var(--brand);border-color:var(--brand);color:#fff}
.btn-primary:hover{background:var(--brand-ink);border-color:var(--brand-ink)}
.btn-sm{padding:4px 10px;font-size:12.5px}
.btn:disabled{opacity:.6;cursor:default}
.link{font-weight:600}

/* ---- Switch ---- */
.switch{position:relative;display:inline-block;width:40px;height:22px;cursor:pointer}
.switch input{opacity:0;width:0;height:0}
.switch span{position:absolute;inset:0;background:#cbd3df;border-radius:20px;transition:.15s}
.switch span:before{content:"";position:absolute;width:16px;height:16px;left:3px;top:3px;background:#fff;border-radius:50%;transition:.15s;box-shadow:0 1px 2px rgba(0,0,0,.2)}
.switch input:checked+span{background:var(--brand)}
.switch input:checked+span:before{transform:translateX(18px)}

/* ---- Forms ---- */
.field{margin-bottom:14px}
.field-label{display:block;font-weight:600;font-size:13px;margin-bottom:5px}
/* Cobre QUALQUER campo de digitação — inclusive os SEM atributo `type` e os tipos que ainda não
   usamos (email, tel, search). A lista é de EXCLUSÃO: enumera o que NÃO leva a caixa.
   A versão antiga era uma lista de INCLUSÃO (`[type=text]`, `[type=number]`, `[type=time]`,
   `[type=date]`) e não casava com `<input name="x">` sem type nem com `type=password` — 14 campos
   de temporadas/estratégia/unidades e os dois do login caíam no visual PADRÃO DO NAVEGADOR, com
   borda cinza de 2px e sem raio. O login foi corrigido em 02/08 de forma escopada; isto fecha o
   resto e impede a próxima recaída.
   ⚠️ `:not()` com LISTA (e não cinco `:not()` encadeados) é proposital: a especificidade de `:not()`
   é a do seu argumento MAIS específico, então esta regra fica em (0,1,1) — igual à antiga. Cinco
   `:not()` encadeados dariam (0,5,1) e atropelariam `.login-card input:not([type=hidden])`, que é
   (0,2,1), desfazendo a tela de login. */
input:not([type=checkbox],[type=radio],[type=hidden],[type=submit],[type=button]),select{
  font:14px system-ui;padding:8px 10px;border:1px solid var(--line);border-radius:8px;background:#fff;color:var(--ink);max-width:100%}
input:focus,select:focus{outline:2px solid #cfe0ff;border-color:var(--brand)}
/* Campo dentro de TABELA densa é compacto por natureza: unidades tem 216 e estratégia 672, e o
   padding cheio (8px 10px) somado ao `box-sizing:border-box` deixaria só 30 px de conteúdo num
   `.uplift-f` de 52 px — o "91,3" não caberia. Medido antes de escolher o número. */
.tbl input:not([type=checkbox],[type=radio],[type=hidden],[type=submit],[type=button]){
  padding:5px 6px;font-size:13px}
.input-suffix{display:inline-flex;align-items:center;gap:6px}
.ajuda{color:var(--muted);font-size:12px;margin-top:4px}
.field-erro{color:var(--erro);font-size:12px;margin-top:4px;min-height:0}
.form-foot{display:flex;align-items:center;gap:12px;margin-top:8px}
.save-msg{font-size:13px}
.save-msg.ok{color:var(--ok)}
.save-msg.erro{color:var(--erro)}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:20px}
.bot-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;gap:12px;flex-wrap:wrap}
.bot-head h2{display:flex;gap:10px;align-items:baseline}

/* ---- Abas (Bot detalhe) ---- */
.tabs{display:flex;gap:4px;border-bottom:1px solid var(--line);margin-bottom:18px}
.tab{cursor:pointer;border:none;background:none;color:var(--muted);font:600 14px system-ui;padding:9px 14px;border-bottom:2px solid transparent;margin-bottom:-1px}
.tab:hover{color:var(--ink)}
.tab.is-active{color:var(--brand-ink);border-bottom-color:var(--brand)}

/* ---- Documentação de bot ---- */
.doc-byline{margin:0 0 14px;padding-bottom:10px;border-bottom:1px solid var(--line);color:var(--muted);font-size:13px}
.doc-body{max-width:760px;font-size:14.5px;line-height:1.6}
.doc-body h1{font-size:20px;margin:0 0 14px}
.doc-body h2{font-size:16px;margin:24px 0 8px;padding-bottom:6px;border-bottom:1px solid var(--line)}
.doc-body h3{font-size:14px;margin:18px 0 4px;color:var(--brand-ink)}
.doc-body h1:first-child,.doc-body h2:first-child,.doc-body h3:first-child{margin-top:0}
.doc-body p{margin:0 0 10px}
.doc-body ul{margin:0 0 12px;padding-left:22px}
.doc-body li{margin:4px 0}
.doc-body strong{color:var(--ink)}

/* ---- Scheduler ---- */
.sched-params{margin:10px 0;padding:12px;background:#f7f9fc;border-radius:8px}
.sched-params label{font-size:14px}
.dias{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:10px}
.dia{display:inline-flex;align-items:center;gap:4px;font-size:13px}
.sched-preview{margin:10px 0;font-size:13px;color:var(--muted);min-height:18px}
.sched-preview code{margin:0 2px}
/* O erro da expressão sai DENTRO da prévia, no lugar das próximas execuções: é a resposta à
   pergunta "quando isto vai rodar?", e a resposta é "não vai, e eis o porquê". */
.sched-preview .erro{color:var(--erro)}
/* Monoespaçada porque os cinco campos se leem por POSIÇÃO — em fonte proporcional,
   `0 5,7,9 * * *` e `0 5,7,9 * *` são difíceis de distinguir, e a diferença é o bot não rodar. */
.cron-input{width:100%;max-width:420px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:14px;padding:8px 10px;border:1px solid var(--borda,#d6dce6);border-radius:6px}

/* ---- Unidades ---- */
.uni-f{width:100%;min-width:80px}
.uni-f[type=checkbox]{width:auto;min-width:0}
.base-f{width:100%;min-width:72px}
.uplift-tbl th.ctr,.uplift-tbl td.ctr{text-align:center}
.uplift-f{width:52px;text-align:right}
.uplift-tbl .pct{color:var(--muted);font-size:11px;margin-left:2px}
.farol-tbl th.ctr,.farol-tbl td.ctr{text-align:center}
.farol-tbl{margin-bottom:10px}
.farol-p50-f{width:52px;text-align:right}
.farol-p50-f::placeholder{color:var(--muted);opacity:.65}
td.ctr{text-align:center}
.save-dot{display:inline-block;width:10px;height:10px;border-radius:50%;background:transparent}
.save-dot.ok{background:var(--ok)}
.save-dot.erro{background:var(--erro)}
.add-row{margin-top:16px;padding-top:14px;border-top:1px dashed var(--line)}
.add-fields{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.add-fields input{flex:0 1 auto}
.inline{display:inline-flex;align-items:center;gap:6px;font-size:13px}

/* ---- Placeholder ---- */
.placeholder{text-align:center;padding:48px 20px}
.ph-emoji{font-size:40px;margin-bottom:8px}

/* ---- Aprovações ---- */
.ap-nota{margin:0 0 14px;padding:9px 12px;background:#eef3fb;border-radius:8px}
.ap-vazio{text-align:center;padding:34px 20px}
.ap-vazio p{margin:4px 0}
.ap-lote{border:1px solid var(--line);border-radius:var(--radius);margin-bottom:16px;overflow:hidden}
.ap-lote-head{display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:10px 14px;background:#f7f9fc;border-bottom:1px solid var(--line);flex-wrap:wrap}
.ap-lote-foot{display:flex;align-items:center;gap:10px;padding:10px 14px;border-top:1px solid var(--line);flex-wrap:wrap}
.ap-msg{color:var(--muted)}
/* Lote sem nada decidível: os três motivos de recusa são permanentes, então ele não volta.
   Fica visível (é registro), mas sem convidar ao clique. */
.ap-lote-morto{opacity:.72;background:#fbfcfd}
.ap-tbl th.num,.ap-tbl td.num{text-align:right}
.ap-tbl td.ap-acoes{text-align:right;white-space:nowrap}
.ap-tbl .ap-acoes .btn{padding:3px 9px}
tr.ap-bloqueada td{opacity:.55}
/* Cor de DIREÇÃO do preço — laranja desce, azul sobe (pedido do Fernando em 31/07, invertido em
   relação ao original). É a mesma linguagem da aba Pricing; não misturar com o farol. */
.ap-desce{color:#c2611b;font-weight:600}
.ap-sobe{color:var(--brand-ink);font-weight:600}
.ap-dec{display:inline-block;padding:2px 9px;border-radius:20px;font-size:12px;font-weight:600}
.ap-dec-aprovada{background:#e2f5ea;color:var(--ok)}
.ap-dec-rejeitada{background:#eef1f6;color:#42506a}
.ap-dec-expirada{background:#fcefdc;color:var(--aviso)}
/* "escrita" NÃO é uma decisão — é o desfecho no Stays. Cor própria (a da marca) para não se
   confundir com aprovada/rejeitada: aprovar é intenção, escrever é fato. */
.ap-dec-escrita{background:#e6eefb;color:var(--brand-ink)}
/* POR QUE a noite foi proposta — mesmo texto do Telegram, em COLUNA própria.
   Duas tentativas anteriores, ambas medidas e descartadas: (1) emendado no cabeçalho da data,
   alongava demais; (2) dentro da célula da acomodação, o texto longo alargava a coluna 1 do grid de
   celular e, como o "De" é alinhado à DIREITA nessa mesma coluna, os preços saíam recuados de forma
   irregular — e antes disso o `nowrap` herdado ainda empurrava o botão 27 px para fora da tela. */
.ap-motivo{color:var(--muted);font-size:12px;white-space:normal}
.ap-lote-hist{margin-bottom:14px}
.ap-lote-hist .ap-lote-head{flex-wrap:wrap;gap:6px}
.ap-lote-hist .ap-lote-head>div{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
/* COLAPSÁVEL com <details>/<summary> NATIVO — sem JS, com teclado, e o Ctrl+F do navegador abre o
   bloco fechado ao achar o texto dentro. Um acordeão em JS perderia as três coisas. */
.ap-lote-hist>summary{cursor:pointer;list-style:none;user-select:none}
.ap-lote-hist>summary::-webkit-details-marker{display:none}   /* o marcador padrão do Safari */
.ap-lote-hist>summary:hover{background:#f2f5fa}
.ap-lote-hist>summary:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}
/* Seta própria: o triângulo nativo não se posiciona bem num cabeçalho em flex. */
.ap-sum{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.ap-sum::before{content:"▸";color:var(--muted);font-size:13px;display:inline-block;transition:transform .15s}
.ap-lote-hist[open]>summary .ap-sum::before{transform:rotate(90deg)}
.ap-farol{font-size:13px;line-height:1}
.ap-sum-quando{white-space:nowrap}
/* A proposta ANTES do teto/piso, ao lado do valor escrito. Só aparece quando os dois divergem —
   é a marca visível do guardrail agindo, e sem ela o número proposto desaparece da auditoria. */
.ap-clamp{display:block;color:var(--muted);font-size:11px;font-weight:400}
.ap-corte{margin-bottom:14px}
/* Três famílias visuais para o estado da ESCRITA (a verdade é status + executed_at, nunca a
   decisão do sidecar): cheia = escreveu · contorno = em curso · esmaecida = encerrou sem escrever. */
.ap-escrita{font-size:13px}
.ap-cheia{color:var(--ink);font-weight:600}
.ap-contorno{color:var(--brand-ink)}
.ap-esmaecida{color:var(--muted)}
/* CELULAR — a linha vira DUAS LINHAS, não uma tabela espremida.
   ┌───────────────────────────────────────────────┐
   │ A31    SEG 10/AGO                   [✅] [❌] │
   │ R$ 154 → R$ 166   +7,8%                       │
   └───────────────────────────────────────────────┘
   A 1ª tentativa mantinha a tabela e escondia colunas para o botão caber. Coube, mas ficou
   ilegível — o Fernando fotografou em 03/08: "R$ 166" quebrando em duas linhas, "ACOMODAÇÃO NOITE"
   colados no cabeçalho, tudo encavalado. Cinco colunas num aparelho de 390 px é briga com a
   física; empilhar é a saída, e ela DEVOLVE o "De", que só tinha sido cortado por falta de espaço.
   Grid de 2 linhas × 4 colunas: as ações ocupam a coluna da direita nas DUAS linhas, então o botão
   fica sempre visível e com alvo de toque grande. */
@media(max-width:760px){
  .ap-tbl .ap-predio{display:none}          /* o nome interno basta; o prédio quebrava em 3 linhas */
  .ap-tbl .ap-col-de{display:block}         /* volta: a 2ª linha tem espaço de sobra */
  .ap-tbl thead{display:none}               /* cabeçalho de tabela não descreve layout empilhado */
  .ap-tbl,.ap-tbl tbody{display:block}
  /* ⚠️ O grid é POSICIONAL (`nth-child`), então vale só para a tabela da FILA. O histórico tem as
     mesmas 6 colunas em ordem DIFERENTE (a 1ª lá é a decisão, não a unidade) e ganha o seu próprio
     grid mais abaixo. Sem essa separação ele herdava este mapa e saía torto — visto em produção
     em 03/08: a seta "→" entre a data e o 1º preço, e o estado da escrita espremido na coluna do
     botão. */
  .ap-tbl:not(.ap-tbl-hist) tbody tr{
    display:grid;
    grid-template-columns:auto auto 1fr auto;
    grid-template-areas:
      "unid  noite noite acoes"
      "mot   mot   mot   acoes"
      "de    para  var   acoes";
    align-items:center;
    gap:2px 8px;
    padding:10px 2px;
    border-bottom:1px solid var(--line);
  }
  .ap-tbl tbody tr:last-child{border-bottom:none}
  /* `nowrap`: sem isto o "R$ 165" quebra em duas linhas a 360 px e a célula vira um bloco de dois
     andares — o defeito exato que o empilhamento veio corrigir. A coluna da variação é `1fr` e
     absorve a folga. */
  .ap-tbl tbody td{display:block;border:none;padding:0;text-align:left;white-space:nowrap}
  .ap-tbl:not(.ap-tbl-hist) tbody td:nth-child(1){grid-area:unid}
  .ap-tbl:not(.ap-tbl-hist) tbody td:nth-child(2){grid-area:noite;color:var(--muted)}
  .ap-tbl:not(.ap-tbl-hist) tbody td:nth-child(3){grid-area:mot}
  .ap-tbl:not(.ap-tbl-hist) tbody td:nth-child(4){grid-area:de;color:var(--muted)}
  .ap-tbl:not(.ap-tbl-hist) tbody td:nth-child(5){grid-area:para}
  .ap-tbl:not(.ap-tbl-hist) tbody td:nth-child(6){grid-area:var}
  /* Sem motivo a célula fica vazia — a linha do grid colapsa sozinha (altura 0), então não sobra
     espaço em branco nas propostas antigas, anteriores à telemetria. */
  .ap-tbl:not(.ap-tbl-hist) tbody td.ap-motivo:empty{display:none}
  .ap-tbl tbody td.ap-acoes{grid-area:acoes;display:flex;gap:6px;justify-content:flex-end}
  /* A seta faz a 2ª linha se ler como "de → para" sem precisar de cabeçalho. */
  .ap-tbl:not(.ap-tbl-hist) tbody td:nth-child(5)::before{content:"→ ";color:var(--muted);font-weight:400}
  .ap-tbl .ap-acoes .btn{padding:6px 10px}  /* alvo de toque, não de mouse */
  .ap-lote-head,.ap-lote-foot{padding:10px}
  .ap-lote-foot .btn{flex:1 1 auto}

  /* HISTÓRICO — mesmas 6 colunas, ORDEM DIFERENTE (decisão · acomodação · noite · de · para · preço).
     ┌───────────────────────────────────────────────┐
     │ [aprovada]  A31   SEG 10/AGO                  │
     │ R$ 154 → R$ 166              preço escrito    │
     └───────────────────────────────────────────────┘
     O estado da escrita fica ao lado dos preços que ele descreve, e não espremido numa coluna
     estreita — que foi como saiu enquanto herdava o grid da fila. */
  .ap-tbl-hist tbody tr{
    display:grid;
    grid-template-columns:auto auto 1fr;
    grid-template-areas:
      "dec  unid  noite"
      "de   para  var"
      "escr escr  escr";
    align-items:center;
    gap:2px 8px;
    padding:10px 2px;
    border-bottom:1px solid var(--line);
  }
  .ap-tbl-hist tbody tr:last-child{border-bottom:none}
  .ap-tbl-hist tbody td:nth-child(1){grid-area:dec}
  .ap-tbl-hist tbody td:nth-child(2){grid-area:unid}
  .ap-tbl-hist tbody td:nth-child(3){grid-area:noite;color:var(--muted)}
  /* ⚠️ `text-align:left` EXPLÍCITO nas duas: a classe `.num` da tabela (`.ap-tbl td.num`, (0,2,1))
     vence o `text-align:left` genérico do empilhamento ((0,1,2)) e joga o preço para a borda. Na
     FILA isso não aparece porque as colunas são estreitas; aqui a linha é larga e o "→ R$ 165"
     ficava a meia tela de distância do "R$ 153" — visto no print de verificação. */
  .ap-tbl-hist tbody td:nth-child(4){grid-area:de;color:var(--muted);text-align:left}
  .ap-tbl-hist tbody td:nth-child(5){grid-area:para;text-align:left}
  .ap-tbl-hist tbody td:nth-child(5)::before{content:"→ ";color:var(--muted);font-weight:400}
  /* ⚠️ LINHA PRÓPRIA, largura inteira. A 1ª tentativa pôs este texto ao lado dos preços com `1fr`:
     a 390 px sobram ~180 px numa linha que já carrega dois valores, e "aprovada, aguardando
     escrita" precisa de ~200 px — quebrava em dois andares e colava na borda. O Fernando
     fotografou de novo ("ainda meio espremido") e estava certo: tirar da coluna do botão não
     bastou. Aqui é REGISTRO, não controle — a 3ª linha não custa nada e nunca aperta, seja qual
     for o texto ("preço escrito", "escrito e desfeito", "não vale mais — nada foi escrito"). */
  .ap-tbl-hist tbody td:nth-child(6){grid-area:var;text-align:right}
  .ap-tbl-hist tbody td:nth-child(7){
    grid-area:escr;text-align:left;white-space:normal;line-height:1.3;font-size:12.5px;margin-top:2px}
}

/* ---- Login ---- */
/* Porta de entrada do painel — desde 2026-08 num subdomínio próprio, em produção. Fundo escuro da
   marca (o MESMO #0f1b2d da sidebar, para a tela já parecer o produto) com brilho da cor-marca. */
.login-body{
  display:flex;align-items:center;justify-content:center;min-height:100vh;padding:24px;
  background:#0f1b2d;
  background-image:
    radial-gradient(900px 520px at 50% -8%,rgba(47,111,237,.34),transparent 62%),
    radial-gradient(700px 460px at 92% 108%,rgba(47,111,237,.15),transparent 60%);
}
.login-wrap{width:100%;max-width:396px;display:flex;flex-direction:column;gap:16px}
.login-card{background:var(--panel);border:1px solid var(--line);border-radius:16px;
  box-shadow:0 18px 48px rgba(6,14,28,.42),0 2px 6px rgba(6,14,28,.24);
  padding:32px 30px;display:flex;flex-direction:column;gap:16px}
.login-head{text-align:center}
/* `.brand` global é branco (feito para a sidebar escura) — aqui o card é claro, então a cor
   precisa ser explícita, senão o "Bot" some e o logo lê só "City". */
.login-card .brand{padding:0;color:var(--ink);font-size:27px;letter-spacing:-.03em}
.login-sub{margin:6px 0 0;color:var(--muted);font-size:13.5px}
.login-card label{display:flex;flex-direction:column;gap:6px;font-size:13px;font-weight:600}
/* O seletor global de input casa por `[type=…]`, e o login usa um campo SEM type (usuário) e um
   `type=password` — nenhum dos dois casava, e os dois caíam no visual padrão do navegador. */
.login-card input:not([type=hidden]){
  width:100%;font:16px/1.2 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  padding:12px 13px;border:1px solid #d6dde8;border-radius:10px;background:#fff;color:var(--ink);
  -webkit-appearance:none;appearance:none;transition:border-color .15s,box-shadow .15s}
.login-card input:not([type=hidden]):focus{
  outline:none;border-color:var(--brand);box-shadow:0 0 0 3px rgba(47,111,237,.18)}
/* O autofill do Chrome pinta o campo de azul-claro e atropela o fundo do card. */
.login-card input:-webkit-autofill{-webkit-box-shadow:0 0 0 100px #fff inset;-webkit-text-fill-color:var(--ink);caret-color:var(--ink)}
.pw-wrap{position:relative;display:flex}
/* Precisa do `.login-card` na frente para EMPATAR em especificidade com a regra de campo acima
   (que tem `:not([type=hidden])`); vencendo por ordem, dispensa `!important`. Sem isso o
   padding-right não aplica e a senha longa corre por baixo do olho. */
.login-card .pw-wrap input{padding-right:44px}
.pw-eye{position:absolute;right:6px;top:50%;transform:translateY(-50%);background:none;border:0;
  color:var(--muted);cursor:pointer;padding:6px;border-radius:8px;display:inline-flex;
  align-items:center;justify-content:center;line-height:0}
.pw-eye:hover{color:var(--ink);background:#eef1f6}
.pw-eye:focus-visible{outline:2px solid var(--brand);outline-offset:1px}
.login-card .btn{width:100%;padding:12px 16px;font-size:15px;border-radius:10px;margin-top:2px}
.login-foot{margin:0;text-align:center;color:#8aa0bd;font-size:12px;display:flex;
  align-items:center;justify-content:center;gap:8px;flex-wrap:wrap}
.login-foot .host{font-family:ui-monospace,"SFMono-Regular",Menlo,monospace}
/* Selo do ambiente: só aparece FORA de produção — é o que evita digitar a senha no vhost errado
   (dev1, dev2 e staging têm telas idênticas). */
.login-env{background:rgba(255,255,255,.11);color:#cfe0ff;border-radius:20px;padding:2px 9px;
  font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em}
.msg-erro{background:#fde8e7;color:#9b2820;padding:9px 12px;border-radius:8px;font-size:13px}
.msg-aviso{background:#fcefdc;color:#8a5216;padding:9px 12px;border-radius:8px;font-size:13px}
@media(max-width:420px){.login-card{padding:26px 20px}}

/* ---- Toast ---- */
#toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%) translateY(20px);background:#0f1b2d;color:#fff;padding:10px 18px;border-radius:8px;font-size:14px;opacity:0;pointer-events:none;transition:.2s;z-index:60}
#toast.show{opacity:1;transform:translateX(-50%) translateY(0)}

/* ---- Responsivo ---- */
@media(max-width:820px){
  .sidebar{transform:translateX(-100%);transition:transform .2s;box-shadow:2px 0 12px rgba(0,0,0,.2)}
  body.menu-open .sidebar{transform:translateX(0)}
  .content{margin-left:0}
  .menu-toggle{display:inline-flex;align-items:center;justify-content:center;position:fixed;top:12px;left:12px;z-index:50;width:40px;height:40px;border:none;border-radius:8px;background:#0f1b2d;color:#fff;font-size:20px;cursor:pointer}
  .topbar{padding-left:64px}
  body.menu-open .scrim{display:block;position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:30}
  .grid2{grid-template-columns:1fr}
  .page{padding:16px}
}

/* ---- Observabilidade (Entrega 4) ---- */
.filtros{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end;margin-bottom:14px}
.filtros label{display:flex;flex-direction:column;gap:3px;font-size:12px;color:var(--muted)}
.filtros .auto{flex-direction:row;align-items:center;gap:5px}
.paginacao{display:flex;align-items:center;gap:12px;margin-top:12px}
.dias-log{display:flex;flex-wrap:wrap;gap:8px}
tr.clickable{cursor:pointer}
tr.clickable:hover{background:#f5f8ff}
.niv-debug{color:#8792a2}.niv-info{color:var(--rodando)}.niv-aviso{color:var(--aviso)}
.niv-erro{color:var(--erro);font-weight:600}
.banner-aviso{background:#fcefdc;border:1px solid #f0d5a8;color:#8a5216;padding:12px 16px;border-radius:var(--radius)}
.run-meta{display:flex;flex-direction:column;gap:4px;margin-bottom:12px}
.txt-ok{color:var(--ok)}.txt-erro{color:var(--erro)}
.timeline{list-style:none;margin:0;padding:0;font-size:13.5px}
.timeline li{display:flex;gap:10px;padding:6px 0;border-bottom:1px solid var(--line);align-items:baseline}
.timeline li:last-child{border-bottom:none}
.tl-hora{color:var(--muted);font-family:ui-monospace,monospace;font-size:12px;white-space:nowrap}
.tl-nivel{min-width:44px;text-transform:uppercase;font-size:11px;font-weight:600}
.tl-msg{flex:1}
.tl-ctx{display:block;margin-top:3px;color:#5a6b8c;word-break:break-all;font-size:12px}
.retencao td:first-child{color:var(--muted)}
.card-n.small{font-size:20px}

/* ---- Modal (rebase estratégia) ---- */
.modal-overlay{position:fixed;inset:0;background:rgba(15,27,45,.45);display:flex;align-items:flex-start;justify-content:center;padding:48px 16px;z-index:80;overflow:auto}
.modal-overlay[hidden]{display:none}
.modal{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);width:100%;max-width:620px;box-shadow:0 12px 40px rgba(15,27,45,.3)}
.modal-head{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;border-bottom:1px solid var(--line)}
.modal-head h3{margin:0}
.modal-x{background:none;border:none;font-size:24px;line-height:1;cursor:pointer;color:var(--muted);padding:0 4px}
.modal-x:hover{color:var(--ink)}
.modal-body{padding:16px 18px}
.modal-body .add-fields{margin:10px 0}
.modal-foot{display:flex;gap:10px;justify-content:flex-end;align-items:center;margin-top:16px;padding-top:14px;border-top:1px solid var(--line)}
.modal-foot .save-msg{margin-right:auto}
.rb-resumo{margin:12px 0;border:1px solid var(--line);border-radius:8px;padding:8px 12px;background:#f7f9fc}
.rb-resumo .tbl{font-size:13px}
.rb-confirm{display:flex;margin:12px 0 0;font-size:13px;line-height:1.4}
.rb-confirm[hidden]{display:none}
/* Mesma armadilha da `.rb-confirm` e dos `.res-presets`: `display:inline-flex` do autor vence o
   `[hidden]{display:none}` do navegador com a especificidade empatada, então o atributo sozinho
   não esconde nada. Sem esta regra, os campos "Meses" e "Fim" apareceriam os DOIS ao mesmo tempo
   no modal do rebase, e o operador escolheria o horizonte por um campo que o servidor ignora. */
.inline[hidden]{display:none}
.rb-dows{gap:10px;padding:8px 0;border-top:1px solid var(--line);border-bottom:1px solid var(--line)}

/* ---- Resultados ---------------------------------------------------------- */
/* As abas aqui são LINKS (a aba vai na query string, para o acesso ser observável
   server-side), então precisam desfazer o sublinhado que .tab não previa. */
.tabs a.tab{text-decoration:none;display:inline-block}
/* overflow-y:hidden é obrigatório: com só overflow-x:auto, o CSS computa overflow-y como `auto`, e o
   1px do sublinhado da aba ativa (border-bottom:2px + margin-bottom:-1px) vira barra de scroll VERTICAL
   (as ▲▼ no Windows). Travar o eixo Y mantém só o scroll horizontal das abas no mobile. */
#resTabs{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}

.res-barra{display:flex;align-items:flex-end;gap:14px;flex-wrap:wrap;margin-bottom:16px}
.res-escopo{display:flex;flex-direction:column;gap:2px}
.res-escopo select{padding:6px 8px;border:1px solid var(--line);border-radius:8px;background:#fff;font:inherit;max-width:100%}
.res-presets{display:flex;gap:6px;flex-wrap:wrap}
/* `display:flex` (autor) vence o `[hidden]{display:none}` do navegador quando a especificidade
   empata, então o atributo hidden sozinho não escondia os presets — eles apareciam no popover
   do "?", que não os usa. Esta regra devolve o efeito do atributo. */
.res-presets[hidden]{display:none}
.res-presets .pill{cursor:pointer;text-decoration:none;border:1px solid var(--line);background:#fff;
  color:var(--muted);font:600 12px system-ui;padding:5px 10px}
.res-presets .pill.is-active{background:var(--brand);border-color:var(--brand);color:#fff}
.res-frescor{margin-left:auto;white-space:nowrap}

/* Controles do gráfico Ritmo do mês: navegação de mês à esquerda, seletor de referência à direita. */
.res-ritmo-barra{display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;margin-bottom:12px}
/* Setas de navegação por mês — as MESMAS regras servem o Ritmo do mês e a Gestão diária (052). */
.res-ritmo-nav{display:flex;align-items:center;gap:8px}
.res-ritmo-nav .pill,.gd-nav .pill{cursor:pointer;border:1px solid var(--line);background:#fff;color:var(--muted);
  font:600 14px system-ui;padding:2px 10px;line-height:1.2}
.res-ritmo-nav .pill:disabled,.gd-nav .pill:disabled{opacity:.4;cursor:default}
.gd-nav{display:inline-flex;align-items:center;gap:6px;vertical-align:middle}
.res-ritmo-mes{min-width:84px;text-align:center;font-weight:700;font-size:13px}

/* Altura fixa: com maintainAspectRatio:false o Chart.js precisa de um contêiner com
   altura própria, senão o canvas cresce sem parar a cada resize. */
.res-chart-wrap{position:relative;height:340px}

/* A grade .cards existente usa auto-fit minmax(180px,1fr) e, com ~343px úteis no
   celular, COLAPSA para 1 coluna. Esta variante crava as 2 colunas pedidas. */
.cards-2{grid-template-columns:repeat(2,minmax(0,1fr))}
/* Variante de 3 colunas: só os 6 cards de destaque da Visão geral (2 linhas de 3). A .cards-2 fica
   intacta para a aba Indicadores. No celular volta a 2 colunas (3 valores de R$ não cabem em ~343px). */
.cards-3{grid-template-columns:repeat(3,minmax(0,1fr))}

.card-kpi{cursor:pointer;display:flex;flex-direction:column;gap:4px}
.card-kpi:hover{border-color:var(--brand)}
.card-kpi:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.card-topo{display:flex;align-items:flex-start;justify-content:space-between;gap:6px}
/* Selo da janela do número (MTD, próx. 30d): torna explícito, na cara do card, que "R$ 135 mil"
   é o acumulado do mês até hoje e não o mês fechado. O "?" detalha em texto. */
.janela{display:inline-block;font:600 10px system-ui;letter-spacing:.02em;text-transform:uppercase;
  color:var(--muted);background:var(--bg);border:1px solid var(--line);border-radius:6px;
  padding:1px 5px;vertical-align:middle;white-space:nowrap}
.card-ajuda{flex:0 0 auto;position:relative;width:20px;height:20px;border-radius:50%;
  border:1px solid var(--line);background:#fff;color:var(--muted);font:700 12px system-ui;
  line-height:1;cursor:pointer;padding:0}
/* O alvo VISÍVEL tem 20px, mas o alvo TOCÁVEL tem 44px (a recomendação para dedo). Sem isto,
   errar o "?" por 3 mm abre o gráfico do cartão em vez da definição do indicador. */
.card-ajuda::before{content:"";position:absolute;top:50%;left:50%;width:44px;height:44px;
  transform:translate(-50%,-50%)}
.card-ajuda:hover{border-color:var(--brand);color:var(--brand-ink)}
.card-ajuda:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.card-kpi .card-n{font-size:22px;font-weight:700}
.card-vars{display:flex;gap:12px;flex-wrap:wrap;margin-top:2px}
.var-pos{color:var(--ok);font-weight:600}
.var-neg{color:var(--erro);font-weight:600}

.spark{width:100%;height:26px;display:block;margin:2px 0}
.spark polyline{fill:none;stroke:var(--brand);stroke-width:1.5;vector-effect:non-scaling-stroke}

/* A lista dos marcos vive FORA do canvas: 11 marcos caem em 4 datas, e rotular as próprias
   linhas imprimia até 4 textos no mesmo pixel. Aqui é legível e o número casa com o selo. */
.marcos-lista{list-style:none;margin:8px 0 0;padding:0;font-size:12px;line-height:1.5}
.marcos-lista li{display:flex;align-items:flex-start;gap:6px;margin-bottom:8px}
.marco-n{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:16px;height:16px;border-radius:50%;background:var(--erro);color:#fff;font:600 9px system-ui;margin-top:1px}
/* min-width:0 deixa a lista de bots quebrar dentro do flex em vez de estourar a largura. */
.marco-corpo{min-width:0}
.marco-dia{display:block;font-weight:700}
/* Cada tipo de marco (rótulo) na própria linha, sob o dia; o rótulo é a CATEGORIA dos bots. */
.marco-linha{display:block}
.marco-rotulo::after{content:"·";margin:0 6px}

/* .modal não tem padding (os outros usos envolvem o conteúdo em .modal-head/.modal-body);
   aqui o conteúdo é direto, então o padding vem na .res-modal — senão título, pills e texto
   ficam colados na borda arredondada. */
.res-modal{max-height:88vh;overflow-y:auto;padding:16px 18px}
.res-modal .res-chart-wrap{height:300px;margin-top:10px}
/* Decomposição do ticket médio por duração (só nesse card). Barras mais baixas que a série. */
#resModalBreak h4{margin:16px 0 2px}
.res-modal .res-chart-break{height:220px;margin-top:6px}
.res-modal .doc-body{margin-top:6px}
.res-modal .res-presets{margin-top:10px}

@media(max-width:820px){
  .res-frescor{margin-left:0;flex-basis:100%}
  .res-chart-wrap{height:260px}
  /* 2 colunas continuam valendo no celular — é o pedido —, mas o número encolhe
     para caber sem quebrar o rótulo. Vale para TODO cartão da grade de 2 colunas,
     não só os de indicador: "R$ 122.003" quebrava em duas linhas nos cartões de
     destaque da Visão geral, que usam .card-n de 30px. */
  .cards-3{grid-template-columns:repeat(2,minmax(0,1fr))}
  .cards-2, .cards-3{gap:10px}
  .cards-2 .card, .cards-3 .card{padding:12px}
  .cards-2 .card-n, .cards-3 .card-n{font-size:18px;white-space:nowrap}
  .card-vars{gap:8px;font-size:11px}
}

/* ── Aba 3: Demanda futura (mapa de calor prédio × mês) ───────────────────────────────────── */
.num{text-align:right;font-variant-numeric:tabular-nums}
.hm-wrap{overflow-x:auto;margin-top:4px}
.hm{border-collapse:separate;border-spacing:3px;font-size:13px}
.hm th{color:var(--muted);font-weight:600;font-size:12px;padding:4px 6px}
.hm thead th{text-align:center;text-transform:uppercase;letter-spacing:.03em}
.hm .hm-predio{text-align:right;white-space:nowrap;font-weight:600;color:var(--ink);position:sticky;left:0;background:var(--panel)}
.hm .hm-canto{position:sticky;left:0;background:var(--panel)}
.hm-cel{position:relative;width:62px;height:46px;border-radius:8px;text-align:center;vertical-align:middle;cursor:default;line-height:1.15}
.hm-vagos{display:block;font-weight:700;font-size:16px;font-variant-numeric:tabular-nums}
.hm-cob{display:block;font-size:11px;opacity:.8}
.hm-vazio{width:62px;height:46px}
/* Escala sequencial num tom só (azul da marca do painel). Cor = quanto vago há para capturar. */
.hm-b1{background:#e4eefc;color:#1c2430}
.hm-b2{background:#bcd6f8;color:#1c2430}
.hm-b3{background:#8ab6f2;color:#10233f}
.hm-b4{background:#4f8cea;color:#fff}
.hm-b5{background:#2f6fed;color:#fff}
/* Quente porém esgotado: cinza neutro, distinto de "sem sinal" (célula em branco). */
.hm-esgotado{background:#eceff3;color:var(--muted)}
.hm-evento{position:absolute;top:3px;right:5px;font-size:9px;color:var(--aviso);line-height:1}
.hm-legenda{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:10px}
.hm-escala{display:inline-flex;gap:2px}
.hm-escala i{width:20px;height:12px;border-radius:3px;display:inline-block}
.hm-amostra{display:inline-flex;align-items:center;justify-content:center;width:20px;height:14px;border-radius:3px;font-style:normal;font-size:10px}
.hm-sep{opacity:.5}
.hm-relogios{margin-top:16px;padding-top:12px;border-top:1px dashed var(--line)}

/* Seção 'À frente' na aba Indicadores (PR-B on-the-books) */
.res-secao{margin:24px 0 10px;font-size:15px;font-weight:700;color:var(--ink)}

/* Aba 4 (Antecedência) e aba 5 (Canais) — PR-C */
.ant-barra{display:inline-flex;width:180px;height:14px;border-radius:3px;overflow:hidden;vertical-align:middle;background:#eef1f6}
.ant-seg{height:100%}
.ant-tbl td{vertical-align:middle}
.can-barra{display:flex;width:100%;height:28px;border-radius:6px;overflow:hidden;margin:4px 0 12px;background:#eef1f6}
.can-seg{height:100%}
.can-dot{display:inline-block;width:9px;height:9px;border-radius:2px;vertical-align:baseline;margin-right:2px}
.can-serie{display:flex;align-items:flex-end;gap:8px;height:150px;margin:8px 0;padding-top:16px}
.can-col{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;height:100%;min-width:34px}
.can-val{font-size:11px;font-variant-numeric:tabular-nums;color:var(--muted);margin-bottom:2px}
.can-bar{width:60%;max-width:34px;background:var(--brand);border-radius:4px 4px 0 0;min-height:2px}
.can-bar.is-parcial{background:#a9c6f6}
.can-mes{font-size:11px;color:var(--muted);margin-top:4px;white-space:nowrap}

/* Waterfall da decomposição "vale a pena?" da coorte de cupom (aba Canais) */
.wf{display:flex;flex-direction:column;gap:3px;margin:6px 0 14px}
.wf-row{display:grid;grid-template-columns:minmax(150px,1.5fr) minmax(70px,1.6fr) auto minmax(96px,auto);
  align-items:center;gap:12px;padding:9px 11px;border-radius:8px}
.wf-lbl{font-size:13px;font-weight:600;color:var(--ink);line-height:1.25}
.wf-lbl em{display:block;font-weight:400;font-style:normal;font-size:11px;color:var(--muted);margin-top:1px}
.wf-track{height:14px;background:var(--bg);border-radius:7px;overflow:hidden}
.wf-bar{display:block;height:100%;border-radius:7px;min-width:2px}
.wf-bar.wf-cost{background:var(--erro)}
.wf-bar.wf-gain{background:var(--ok)}
.wf-val{font-variant-numeric:tabular-nums;font-weight:700;font-size:14px;text-align:right;white-space:nowrap}
.wf-val.pos,.wf-row.pos .wf-val{color:var(--ok)}
.wf-val.neg,.wf-row.neg .wf-val{color:var(--erro)}
.wf-pct{font-size:11px;color:var(--muted);text-align:right;white-space:nowrap;line-height:1.2}
.wf-sub{background:var(--bg);border-top:1px dashed var(--line)}
.wf-sub .wf-lbl,.wf-total .wf-lbl{font-weight:700}
.wf-sub .wf-track,.wf-total .wf-track{background:transparent} /* sem cápsula cinza sobre o fundo tingido */
.wf-total{background:#eef7f1;border:1px solid #cfe8d8}
.wf-total.neg{background:#fdf1f0;border-color:#f3c0bd}
.wf-total .wf-val{font-size:16px}
@media(max-width:640px){
  .wf-row{grid-template-columns:1fr auto;row-gap:1px}
  .wf-track{display:none}
  .wf-pct{grid-column:2;text-align:right}
}

/* ── Bloco "Gestão diária" (aba Visão geral) — mobile-first ───────────────── */
.gd-head{display:flex;align-items:center;gap:10px}
/* Mês NAVEGADO do bloco, ao lado do título — pílula com o azul da marca (o PO aprovou a pílula em
   25/07; por isso ela diverge do rótulo plano do ritmo). `min-width` evita o pulo de layout entre
   "Maio de 2026" e "Setembro de 2026" ao clicar as setas. */
.gd-mes{font-size:13px;font-weight:700;color:var(--brand-ink);background:var(--bg);border:1px solid var(--line);padding:2px 10px;border-radius:999px;vertical-align:middle;min-width:150px;display:inline-block;text-align:center}
/* Faixa "vs ano passado" (migration 20260731-1352): os quatro números lado a lado, porque a variação sozinha
   é o defeito. Empilha em 2 colunas no tablet e 1 no celular — o bloco é mobile-first. */
.gd-yoy{display:grid;grid-template-columns:repeat(4,1fr);gap:9px;margin:2px 0 6px;padding:10px 12px;
  border:1px solid var(--line);border-radius:10px}
.gd-yoy-c{display:flex;flex-direction:column;gap:2px}
.gd-yoy-k{font-size:11px;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);font-weight:600}
.gd-yoy-n{font-size:18px;font-weight:600}
.gd-yoy-nota{grid-column:1/-1;font-size:11px;color:var(--muted);margin-top:4px}
@media(max-width:820px){.gd-yoy{grid-template-columns:repeat(2,1fr)}}
@media(max-width:430px){.gd-yoy{grid-template-columns:1fr}}
/* O olho ofusca também os números da faixa (são R$/noites da operação). */
.gd-oculto .gd-yoy-n{filter:blur(8px);user-select:none}

/* Delta suprimido (comparação desonesta): fica visível mas apagado, com o motivo no title. */
.gd-delta-inc{opacity:.55;font-style:italic}
/* Aviso de mês sem calendário do Stays carregado. */
.gd-aviso{margin:8px 0 0;color:var(--erro)}
.gd-live{margin-left:auto;display:inline-flex;align-items:center;gap:6px;font-size:11px;color:var(--muted);
  background:var(--bg);border:1px solid var(--line);padding:3px 9px;border-radius:999px;white-space:nowrap}
.gd-live i{width:6px;height:6px;border-radius:50%;background:var(--ok);display:inline-block}
/* Cabeçalho à direita: botão-olho + selo "recalculado". O olho ofusca só os VALORES (.card-n); o delta fica. */
.gd-head-right{margin-left:auto;display:inline-flex;align-items:center;gap:10px}
.gd-head-right .gd-live{margin-left:0}
.gd-olho{background:none;border:1px solid var(--line);color:var(--muted);cursor:pointer;padding:5px;border-radius:8px;display:inline-flex;align-items:center;justify-content:center;line-height:0}
.gd-olho:hover{color:var(--ink);border-color:var(--muted)}
.gd-olho[aria-pressed="true"]{color:var(--brand-ink);border-color:var(--brand)}
.gd-oculto .card-n{filter:blur(8px);user-select:none;transition:filter .12s ease}
.gd-cards{margin:12px 0 6px;display:flex;flex-direction:column;gap:9px}
.gd-row{display:grid;gap:9px;grid-template-columns:repeat(4,1fr)}
.gd-span2{grid-column:span 2}
@media(max-width:820px){.gd-row{grid-template-columns:repeat(2,1fr)}}
@media(max-width:430px){.gd-row{grid-template-columns:1fr}.gd-span2{grid-column:auto}}
.gd-card{position:relative;overflow:hidden;padding-left:14px}
.gd-card::before{content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:var(--brand)}
.gd-card.gd-bom::before{background:var(--ok)}
.gd-card.gd-mau::before{background:var(--erro)}
.gd-k{font-size:11px;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);font-weight:600;margin-bottom:5px;display:flex;align-items:center;justify-content:space-between;gap:6px}
.gd-info{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:50%;border:1px solid var(--line);color:var(--muted);font-size:10px;font-weight:700;cursor:pointer;flex:0 0 auto;line-height:1;letter-spacing:0;text-transform:none;user-select:none}
/* O `?` também vive num <h2> desde que a receita acumulada virou seção própria (04/08/2026). */
.gd-h .gd-info,.panel-head h2 .gd-info{margin-left:8px;vertical-align:middle}
.gd-card .card-n{font-size:22px}
.gd-bom .gd-delta{color:var(--ok);font-weight:600}
.gd-mau .gd-delta{color:var(--erro);font-weight:600}
.gd-h{font-size:14px;margin:18px 0 4px;font-weight:700}
.gd-h .small{font-weight:400}
.gd-legend{display:flex;flex-wrap:wrap;gap:12px;margin:0 0 8px}
.gd-lg{display:inline-flex;align-items:center;gap:6px;font-size:11px;color:var(--muted)}
.gd-lg::before{content:"";width:11px;height:11px;border-radius:3px;background:var(--muted);display:inline-block}
.gd-c0::before{background:var(--erro)}
.gd-c1::before{background:var(--amarelo)}
.gd-c2::before{background:var(--ok)}
.gd-c3::before{background:var(--brand)}
.gd-occ::before{background:var(--brand)}
.gd-adr::before{background:var(--aviso);height:3px;border-radius:2px}
.gd-otb::before{background:repeating-linear-gradient(90deg,var(--muted) 0 4px,transparent 4px 7px);height:2px;border-radius:0}
/* Legenda do farol na Gestão diária. ⚠️ São as MESMAS quatro cores do farol no resto da casa
   (verde/amarelo/vermelho/cinza = risco × ação). A outra linguagem de cor do painel
   (laranja = desconto, azul = acréscimo) NUNCA se mistura com esta. */
.gd-f-verde::before{background:var(--ok)}
.gd-f-amarelo::before{background:var(--amarelo)}
.gd-f-vermelho::before{background:var(--erro)}
.gd-f-cinza::before{background:var(--muted);opacity:.55}
/* ── A carteira nos próximos 7 dias (05/08/2026): prédio × dia, cor do farol ──────────────────
   Tabela de verdade (não grid de divs) porque É uma tabela: prédio no cabeçalho de linha, dia no
   de coluna. Isso dá leitura por teclado e por leitor de tela de graça — `scope=row`/`scope=col`.
   ⚠️ `overflow-x` no contêiner: com 7 colunas + nome do prédio, o celular estoura a largura, e a
   regra da casa é que o conteúdo largo role DENTRO do bloco, não que a página role. */
.gd-grade{overflow-x:auto;margin:6px 0 4px}
.gd-grade-tab{border-collapse:separate;border-spacing:4px;font-size:11px}
.gd-grade-tab th{font-weight:600;color:var(--muted);text-align:center;padding:0}
.gd-gr-nome{text-align:left!important;color:var(--ink)!important;font-weight:500;white-space:nowrap;padding-right:8px!important;max-width:210px;overflow:hidden;text-overflow:ellipsis}
.gd-gr-dow{display:block;font-size:11px;line-height:1.1}
.gd-gr-dia{display:block;font-size:10px;opacity:.65;line-height:1.1}
.gd-gr-c{padding:0}
/* O quadradinho é o `span`: a `td` fica com o alvo do title/hover e o span com a cor, senão o
   border-spacing entra no retângulo pintado e as colunas parecem coladas. */
/* O quadradinho passou a carregar TEXTO (o % proposto, 05/08/2026): cresceu de 26 para 42px e
   centraliza. ⚠️ O branco em cima do AMARELO (#e3b100) não passa em contraste — por isso a cor do
   texto é decidida por cor de fundo, logo abaixo, e não fixada aqui. */
.gd-gr-c>span{display:flex;align-items:center;justify-content:center;width:42px;height:22px;border-radius:4px;background:var(--muted);
  font-size:10px;font-weight:700;letter-spacing:-.02em;color:#fff;font-variant-numeric:tabular-nums}
.gd-grade-tab .gd-f-verde>span{background:var(--ok)}
.gd-grade-tab .gd-f-amarelo>span{background:var(--amarelo)}
.gd-grade-tab .gd-f-vermelho>span{background:var(--erro)}
.gd-grade-tab .gd-f-cinza>span{background:var(--muted);opacity:.55}
/* ⚠️ CONTRASTE DO NÚMERO. Sobre o amarelo (#e3b100) o branco tem razão ~1,9:1 — ilegível. Texto
   escuro ali resolve; nos outros três o branco é o que contrasta. É a única regra de cor de TEXTO
   da grade, e existe porque a célula deixou de ser só um quadrado colorido. */
.gd-grade-tab .gd-f-amarelo>span{color:#3a2e00}
/* AUSÊNCIA de leitura ≠ cinza (que é um estado do farol). Hachura, para não parecer sinal. */
.gd-gr-vazio>span{background:repeating-linear-gradient(45deg,var(--line),var(--line) 3px,transparent 3px,transparent 6px);border:1px solid var(--line)}
/* ⚠️ Os `::before` da legenda NÃO valem aqui: `.gd-f-*` é compartilhada com `.gd-lg`, que desenha
   o swatch num pseudo-elemento. Dentro da tabela a cor vai no `span`, e o pseudo tem de sumir. */
.gd-grade-tab .gd-f-verde::before,.gd-grade-tab .gd-f-amarelo::before,
.gd-grade-tab .gd-f-vermelho::before,.gd-grade-tab .gd-f-cinza::before{content:none}

/* A linha do Net ADR (05/08/2026) mora na MESMA legenda, mas o swatch é um TRAÇO, não um quadrado:
   ela é uma série contínua, não uma categoria de cor. E é AZUL de propósito — âmbar (a cor do ADR
   no gráfico irmão) seria idêntica às barras "no plano" logo ao lado. */
.gd-lg-linha::before{height:3px;border-radius:2px;width:14px}
/* A faixa-meta (05/08/2026): o swatch imita a BANDA do gráfico — preenchimento fraco + borda
   tracejada —, não um quadrado sólido. É o que a distingue, no olho, das quatro cores do farol:
   verde sólido = "vendendo à frente", verde translúcido com borda = "a meta". */
.gd-lg-faixa::before{height:10px;width:16px;border-radius:2px;border:1px dashed rgba(31,157,85,.55)}
.gd-f-meta::before{background:rgba(31,157,85,.14)}
.gd-f-netadr::before{background:var(--brand);opacity:1}
/* Rodapé do gráfico do farol: carrega os RELÓGIOS (de quando é cada metade) e, quando existe, o
   motivo de não haver cor. O app.js o esconde quando não há o que dizer. */
.gd-farol-nota{margin:6px 0 0}
.res-chart-sm{height:210px}
/* Aviso de "semana em curso" sob os gráficos semanais: some quando a última semana já fechou. */
.res-nota-parcial{margin:6px 0 0;color:var(--aviso)}
.res-nota-parcial::before{content:"⚠️ ";}
/* Reservas D-7 + Reservas por canal lado a lado no desktop; empilham no mobile. */
.gd-duo{display:grid;grid-template-columns:1fr 1fr;gap:20px}
@media(max-width:820px){.gd-duo{grid-template-columns:1fr;gap:0}}
/* Os TRÊS gráficos da janela de 8 dias na mesma linha no desktop. Abaixo de 1180px a terceira coluna
   ficaria estreita demais para os rótulos, então cai para 2 antes de empilhar de vez no mobile. */
.gd-trio{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
@media(max-width:1180px){.gd-trio{grid-template-columns:1fr 1fr}}
@media(max-width:820px){.gd-trio{grid-template-columns:1fr;gap:0}}
.gd-clg{display:inline-flex;align-items:center;gap:5px;font-size:11px;color:var(--muted);margin-right:2px}
.gd-clg i{width:11px;height:11px;border-radius:3px;display:inline-block;flex:0 0 auto}
/* Popover "?" — como o cálculo é feito. */
.gd-pop{position:fixed;z-index:60;max-width:260px;background:var(--panel);color:var(--ink);border:1px solid var(--line);
  border-radius:10px;box-shadow:0 6px 24px rgba(20,30,50,.16);padding:10px 12px;font-size:12px;line-height:1.45}
.gd-pop b{font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted)}
.gd-pop-f{display:inline-block;margin-top:4px;color:var(--muted)}
.gd-pop-etl{display:inline-block;margin-top:6px;color:var(--aviso)}
